aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/compass/ami306.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/inv_mpu/compass/ami306.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu/compass/ami306.c')
-rw-r--r--drivers/misc/inv_mpu/compass/ami306.c1020
1 files changed, 1020 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/compass/ami306.c b/drivers/misc/inv_mpu/compass/ami306.c
new file mode 100644
index 00000000000..f645457d161
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami306.c
@@ -0,0 +1,1020 @@
1/*
2 $License:
3 Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 $
18 */
19
20/**
21 * @addtogroup COMPASSDL
22 *
23 * @{
24 * @file ami306.c
25 * @brief Magnetometer setup and handling methods for Aichi AMI306
26 * compass.
27 */
28
29/* -------------------------------------------------------------------------- */
30
31#include <linux/i2c.h>
32#include <linux/module.h>
33#include <linux/moduleparam.h>
34#include <linux/kernel.h>
35#include <linux/errno.h>
36#include <linux/slab.h>
37#include <linux/delay.h>
38#include "mpu-dev.h"
39
40#include "ami_hw.h"
41#include "ami_sensor_def.h"
42
43#include <log.h>
44#include <linux/mpu.h>
45#include "mlsl.h"
46#include "mldl_cfg.h"
47#undef MPL_LOG_TAG
48#define MPL_LOG_TAG "MPL-compass"
49
50/* -------------------------------------------------------------------------- */
51#define AMI306_REG_DATAX (0x10)
52#define AMI306_REG_STAT1 (0x18)
53#define AMI306_REG_CNTL1 (0x1B)
54#define AMI306_REG_CNTL2 (0x1C)
55#define AMI306_REG_CNTL3 (0x1D)
56#define AMI306_REG_CNTL4_1 (0x5C)
57#define AMI306_REG_CNTL4_2 (0x5D)
58
59#define AMI306_BIT_CNTL1_PC1 (0x80)
60#define AMI306_BIT_CNTL1_ODR1 (0x10)
61#define AMI306_BIT_CNTL1_FS1 (0x02)
62
63#define AMI306_BIT_CNTL2_IEN (0x10)
64#define AMI306_BIT_CNTL2_DREN (0x08)
65#define AMI306_BIT_CNTL2_DRP (0x04)
66#define AMI306_BIT_CNTL3_F0RCE (0x40)
67
68#define AMI_FINE_MAX (96)
69#define AMI_STANDARD_OFFSET (0x800)
70#define AMI_GAIN_COR_DEFAULT (1000)
71
72/* -------------------------------------------------------------------------- */
73struct ami306_private_data {
74 int isstandby;
75 unsigned char fine[3];
76 struct ami_sensor_parametor param;
77 struct ami_win_parameter win;
78};
79
80/* -------------------------------------------------------------------------- */
81static inline unsigned short little_u8_to_u16(unsigned char *p_u8)
82{
83 return p_u8[0] | (p_u8[1] << 8);
84}
85
86static int ami306_set_bits8(void *mlsl_handle,
87 struct ext_slave_platform_data *pdata,
88 unsigned char reg, unsigned char bits)
89{
90 int result;
91 unsigned char buf;
92
93 result = inv_serial_read(mlsl_handle, pdata->address, reg, 1, &buf);
94 if (result) {
95 LOG_RESULT_LOCATION(result);
96 return result;
97 }
98
99 buf |= bits;
100 result = inv_serial_single_write(mlsl_handle, pdata->address, reg, buf);
101 if (result) {
102 LOG_RESULT_LOCATION(result);
103 return result;
104 }
105 return result;
106}
107
108static int ami306_wait_data_ready(void *mlsl_handle,
109 struct ext_slave_platform_data *pdata,
110 unsigned long usecs, unsigned long times)
111{
112 int result = 0;
113 unsigned char buf;
114
115 for (; 0 < times; --times) {
116 udelay(usecs);
117 result = inv_serial_read(mlsl_handle, pdata->address,
118 AMI_REG_STA1, 1, &buf);
119 if (buf & AMI_STA1_DRDY_BIT)
120 return 0;
121 else if (buf & AMI_STA1_DOR_BIT)
122 return INV_ERROR_COMPASS_DATA_OVERFLOW;
123 }
124 return INV_ERROR_COMPASS_DATA_NOT_READY;
125}
126
127static int ami306_read_raw_data(void *mlsl_handle,
128 struct ext_slave_platform_data *pdata,
129 short dat[3])
130{
131 int result;
132 unsigned char buf[6];
133 result = inv_serial_read(mlsl_handle, pdata->address,
134 AMI_REG_DATAX, sizeof(buf), buf);
135 if (result) {
136 LOG_RESULT_LOCATION(result);
137 return result;
138 }
139 dat[0] = little_u8_to_u16(&buf[0]);
140 dat[1] = little_u8_to_u16(&buf[2]);
141 dat[2] = little_u8_to_u16(&buf[4]);
142 return result;
143}
144
145#define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */
146#define AMI_DRDYWAIT 800 /* u(micro) sec */
147static int ami306_force_mesurement(void *mlsl_handle,
148 struct ext_slave_platform_data *pdata,
149 short ver[3])
150{
151 int result;
152 int status;
153 result = ami306_set_bits8(mlsl_handle, pdata,
154 AMI_REG_CTRL3, AMI_CTRL3_FORCE_BIT);
155 if (result) {
156 LOG_RESULT_LOCATION(result);
157 return result;
158 }
159
160 result = ami306_wait_data_ready(mlsl_handle, pdata,
161 AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY);
162 if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) {
163 LOG_RESULT_LOCATION(result);
164 return result;
165 }
166 /* READ DATA X,Y,Z */
167 status = ami306_read_raw_data(mlsl_handle, pdata, ver);
168 if (status) {
169 LOG_RESULT_LOCATION(status);
170 return status;
171 }
172
173 return result;
174}
175
176static int ami306_mea(void *mlsl_handle,
177 struct ext_slave_platform_data *pdata, short val[3])
178{
179 int result = ami306_force_mesurement(mlsl_handle, pdata, val);
180 if (result) {
181 LOG_RESULT_LOCATION(result);
182 return result;
183 }
184 val[0] += AMI_STANDARD_OFFSET;
185 val[1] += AMI_STANDARD_OFFSET;
186 val[2] += AMI_STANDARD_OFFSET;
187 return result;
188}
189
190static int ami306_write_offset(void *mlsl_handle,
191 struct ext_slave_platform_data *pdata,
192 unsigned char *fine)
193{
194 int result = 0;
195 unsigned char dat[3];
196 dat[0] = AMI_REG_OFFX;
197 dat[1] = 0x7f & fine[0];
198 dat[2] = 0;
199 result = inv_serial_write(mlsl_handle, pdata->address,
200 sizeof(dat), dat);
201 dat[0] = AMI_REG_OFFY;
202 dat[1] = 0x7f & fine[1];
203 dat[2] = 0;
204 result = inv_serial_write(mlsl_handle, pdata->address,
205 sizeof(dat), dat);
206 dat[0] = AMI_REG_OFFZ;
207 dat[1] = 0x7f & fine[2];
208 dat[2] = 0;
209 result = inv_serial_write(mlsl_handle, pdata->address,
210 sizeof(dat), dat);
211 return result;
212}
213
214static int ami306_start_sensor(void *mlsl_handle,
215 struct ext_slave_platform_data *pdata)
216{
217 int result = 0;
218 unsigned char buf[3];
219 struct ami306_private_data *private_data = pdata->private_data;
220
221 /* Step 1 */
222 result = ami306_set_bits8(mlsl_handle, pdata,
223 AMI_REG_CTRL1,
224 AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
225 if (result) {
226 LOG_RESULT_LOCATION(result);
227 return result;
228 }
229 /* Step 2 */
230 result = ami306_set_bits8(mlsl_handle, pdata,
231 AMI_REG_CTRL2, AMI_CTRL2_DREN);
232 if (result) {
233 LOG_RESULT_LOCATION(result);
234 return result;
235 }
236 /* Step 3 */
237 buf[0] = AMI_REG_CTRL4;
238 buf[1] = AMI_CTRL4_HS & 0xFF;
239 buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
240 result = inv_serial_write(mlsl_handle, pdata->address,
241 sizeof(buf), buf);
242 if (result) {
243 LOG_RESULT_LOCATION(result);
244 return result;
245 }
246 /* Step 4 */
247 result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
248 if (result) {
249 LOG_RESULT_LOCATION(result);
250 return result;
251 }
252 return result;
253}
254
255/**
256 * This function does this.
257 *
258 * @param mlsl_handle this param is this.
259 * @param slave
260 * @param pdata
261 *
262 * @return INV_SUCCESS or non-zero error code.
263 */
264static int ami306_read_param(void *mlsl_handle,
265 struct ext_slave_descr *slave,
266 struct ext_slave_platform_data *pdata)
267{
268 int result = 0;
269 unsigned char regs[12];
270 struct ami306_private_data *private_data = pdata->private_data;
271 struct ami_sensor_parametor *param = &private_data->param;
272
273 result = inv_serial_read(mlsl_handle, pdata->address,
274 AMI_REG_SENX, sizeof(regs), regs);
275 if (result) {
276 LOG_RESULT_LOCATION(result);
277 return result;
278 }
279
280 /* Little endian 16 bit registers */
281 param->m_gain.x = little_u8_to_u16(&regs[0]);
282 param->m_gain.y = little_u8_to_u16(&regs[2]);
283 param->m_gain.z = little_u8_to_u16(&regs[4]);
284
285 param->m_interference.xy = regs[7];
286 param->m_interference.xz = regs[6];
287 param->m_interference.yx = regs[9];
288 param->m_interference.yz = regs[8];
289 param->m_interference.zx = regs[11];
290 param->m_interference.zy = regs[10];
291
292 param->m_offset.x = AMI_STANDARD_OFFSET;
293 param->m_offset.y = AMI_STANDARD_OFFSET;
294 param->m_offset.z = AMI_STANDARD_OFFSET;
295
296 param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT;
297 param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT;
298 param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT;
299
300 return result;
301}
302
303static int ami306_initial_b0_adjust(void *mlsl_handle,
304 struct ext_slave_descr *slave,
305 struct ext_slave_platform_data *pdata)
306{
307 int result;
308 unsigned char fine[3] = { 0 };
309 short data[3];
310 int diff[3] = { 0x7fff, 0x7fff, 0x7fff };
311 int fn = 0;
312 int ax = 0;
313 unsigned char buf[3];
314 struct ami306_private_data *private_data = pdata->private_data;
315
316 result = ami306_set_bits8(mlsl_handle, pdata,
317 AMI_REG_CTRL2, AMI_CTRL2_DREN);
318 if (result) {
319 LOG_RESULT_LOCATION(result);
320 return result;
321 }
322
323 buf[0] = AMI_REG_CTRL4;
324 buf[1] = AMI_CTRL4_HS & 0xFF;
325 buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
326 result = inv_serial_write(mlsl_handle, pdata->address,
327 sizeof(buf), buf);
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332
333 for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */
334 fine[0] = fine[1] = fine[2] = fn;
335 result = ami306_write_offset(mlsl_handle, pdata, fine);
336 if (result) {
337 LOG_RESULT_LOCATION(result);
338 return result;
339 }
340
341 result = ami306_force_mesurement(mlsl_handle, pdata, data);
342 if (result) {
343 LOG_RESULT_LOCATION(result);
344 return result;
345 }
346 MPL_LOGV("[%d] x:%-5d y:%-5d z:%-5d\n",
347 fn, data[0], data[1], data[2]);
348
349 for (ax = 0; ax < 3; ax++) {
350 /* search point most close to zero. */
351 if (diff[ax] > abs(data[ax])) {
352 private_data->fine[ax] = fn;
353 diff[ax] = abs(data[ax]);
354 }
355 }
356 }
357 MPL_LOGV("fine x:%-5d y:%-5d z:%-5d\n",
358 private_data->fine[0], private_data->fine[1],
359 private_data->fine[2]);
360
361 result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
362 if (result) {
363 LOG_RESULT_LOCATION(result);
364 return result;
365 }
366
367 /* Software Reset */
368 result = ami306_set_bits8(mlsl_handle, pdata,
369 AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
370 if (result) {
371 LOG_RESULT_LOCATION(result);
372 return result;
373 }
374 return result;
375}
376
377#define SEH_RANGE_MIN 100
378#define SEH_RANGE_MAX 3950
379static int ami306_search_offset(void *mlsl_handle,
380 struct ext_slave_descr *slave,
381 struct ext_slave_platform_data *pdata)
382{
383 int result;
384 int axis;
385 unsigned char regs[6];
386 unsigned char run_flg[3] = { 1, 1, 1 };
387 unsigned char fine[3];
388 unsigned char win_range_fine[3];
389 unsigned short fine_output[3];
390 short val[3];
391 unsigned short cnt[3] = { 0 };
392 struct ami306_private_data *private_data = pdata->private_data;
393
394 result = inv_serial_read(mlsl_handle, pdata->address,
395 AMI_FINEOUTPUT_X, sizeof(regs), regs);
396 if (result) {
397 LOG_RESULT_LOCATION(result);
398 return result;
399 }
400 fine_output[0] = little_u8_to_u16(&regs[0]);
401 fine_output[1] = little_u8_to_u16(&regs[2]);
402 fine_output[2] = little_u8_to_u16(&regs[4]);
403
404 for (axis = 0; axis < 3; ++axis) {
405 if (fine_output[axis] == 0) {
406 MPL_LOGV("error fine_output %d axis:%d\n",
407 __LINE__, axis);
408 return -1;
409 }
410 /* fines per a window */
411 win_range_fine[axis] = (SEH_RANGE_MAX - SEH_RANGE_MIN)
412 / fine_output[axis];
413 }
414
415 /* get current fine */
416 result = inv_serial_read(mlsl_handle, pdata->address,
417 AMI_REG_OFFX, 2, &regs[0]);
418 if (result) {
419 LOG_RESULT_LOCATION(result);
420 return result;
421 }
422 result = inv_serial_read(mlsl_handle, pdata->address,
423 AMI_REG_OFFY, 2, &regs[2]);
424 if (result) {
425 LOG_RESULT_LOCATION(result);
426 return result;
427 }
428 result = inv_serial_read(mlsl_handle, pdata->address,
429 AMI_REG_OFFZ, 2, &regs[4]);
430 if (result) {
431 LOG_RESULT_LOCATION(result);
432 return result;
433 }
434
435 fine[0] = (unsigned char)(regs[0] & 0x7f);
436 fine[1] = (unsigned char)(regs[2] & 0x7f);
437 fine[2] = (unsigned char)(regs[4] & 0x7f);
438
439 while (run_flg[0] == 1 || run_flg[1] == 1 || run_flg[2] == 1) {
440
441 result = ami306_mea(mlsl_handle, pdata, val);
442 if (result) {
443 LOG_RESULT_LOCATION(result);
444 return result;
445 }
446 MPL_LOGV("val x:%-5d y:%-5d z:%-5d\n", val[0], val[1], val[2]);
447 MPL_LOGV("now fine x:%-5d y:%-5d z:%-5d\n",
448 fine[0], fine[1], fine[2]);
449
450 for (axis = 0; axis < 3; ++axis) {
451 if (axis == 0) { /* X-axis is reversed */
452 val[axis] = 0x0FFF & ~val[axis];
453 }
454 if (val[axis] < SEH_RANGE_MIN) {
455 /* At the case of less low limmit. */
456 fine[axis] -= win_range_fine[axis];
457 MPL_LOGV("min : fine=%d diff=%d\n",
458 fine[axis], win_range_fine[axis]);
459 }
460 if (val[axis] > SEH_RANGE_MAX) {
461 /* At the case of over high limmit. */
462 fine[axis] += win_range_fine[axis];
463 MPL_LOGV("max : fine=%d diff=%d\n",
464 fine[axis], win_range_fine[axis]);
465 }
466 if (SEH_RANGE_MIN <= val[axis] &&
467 val[axis] <= SEH_RANGE_MAX) {
468 /* In the current window. */
469 int diff_fine =
470 (val[axis] - AMI_STANDARD_OFFSET) /
471 fine_output[axis];
472 fine[axis] += diff_fine;
473 run_flg[axis] = 0;
474 MPL_LOGV("mid : fine=%d diff=%d\n",
475 fine[axis], diff_fine);
476 }
477
478 if (!(0 <= fine[axis] && fine[axis] < AMI_FINE_MAX)) {
479 MPL_LOGE("fine err :%d\n", cnt[axis]);
480 goto out;
481 }
482 if (cnt[axis] > 3) {
483 MPL_LOGE("cnt err :%d\n", cnt[axis]);
484 goto out;
485 }
486 cnt[axis]++;
487 }
488 MPL_LOGV("new fine x:%-5d y:%-5d z:%-5d\n",
489 fine[0], fine[1], fine[2]);
490
491 /* set current fine */
492 result = ami306_write_offset(mlsl_handle, pdata, fine);
493 if (result) {
494 LOG_RESULT_LOCATION(result);
495 return result;
496 }
497 }
498 memcpy(private_data->fine, fine, sizeof(fine));
499out:
500 result = ami306_set_bits8(mlsl_handle, pdata,
501 AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
502 if (result) {
503 LOG_RESULT_LOCATION(result);
504 return result;
505 }
506 udelay(250 + 50);
507 return 0;
508}
509
510static int ami306_read_win(void *mlsl_handle,
511 struct ext_slave_descr *slave,
512 struct ext_slave_platform_data *pdata)
513{
514 int result = 0;
515 unsigned char regs[6];
516 struct ami306_private_data *private_data = pdata->private_data;
517 struct ami_win_parameter *win = &private_data->win;
518
519 result = inv_serial_read(mlsl_handle, pdata->address,
520 AMI_REG_OFFOTPX, sizeof(regs), regs);
521 if (result) {
522 LOG_RESULT_LOCATION(result);
523 return result;
524 }
525
526 win->m_0Gauss_fine.x = (unsigned char)(regs[0] & 0x7f);
527 win->m_0Gauss_fine.y = (unsigned char)(regs[2] & 0x7f);
528 win->m_0Gauss_fine.z = (unsigned char)(regs[4] & 0x7f);
529
530 result = inv_serial_read(mlsl_handle, pdata->address,
531 AMI_REG_OFFX, 2, &regs[0]);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536 result = inv_serial_read(mlsl_handle, pdata->address,
537 AMI_REG_OFFY, 2, &regs[2]);
538 if (result) {
539 LOG_RESULT_LOCATION(result);
540 return result;
541 }
542 result = inv_serial_read(mlsl_handle, pdata->address,
543 AMI_REG_OFFZ, 2, &regs[4]);
544 if (result) {
545 LOG_RESULT_LOCATION(result);
546 return result;
547 }
548
549 win->m_fine.x = (unsigned char)(regs[0] & 0x7f);
550 win->m_fine.y = (unsigned char)(regs[2] & 0x7f);
551 win->m_fine.z = (unsigned char)(regs[4] & 0x7f);
552
553 result = inv_serial_read(mlsl_handle, pdata->address,
554 AMI_FINEOUTPUT_X, sizeof(regs), regs);
555 if (result) {
556 LOG_RESULT_LOCATION(result);
557 return result;
558 }
559 win->m_fine_output.x = little_u8_to_u16(&regs[0]);
560 win->m_fine_output.y = little_u8_to_u16(&regs[2]);
561 win->m_fine_output.z = little_u8_to_u16(&regs[4]);
562
563 return result;
564}
565
566static int ami306_suspend(void *mlsl_handle,
567 struct ext_slave_descr *slave,
568 struct ext_slave_platform_data *pdata)
569{
570 int result;
571 unsigned char reg;
572 result = inv_serial_read(mlsl_handle, pdata->address,
573 AMI306_REG_CNTL1, 1, &reg);
574 if (result) {
575 LOG_RESULT_LOCATION(result);
576 return result;
577 }
578
579 reg &= ~(AMI306_BIT_CNTL1_PC1 | AMI306_BIT_CNTL1_FS1);
580 result = inv_serial_single_write(mlsl_handle, pdata->address,
581 AMI306_REG_CNTL1, reg);
582 if (result) {
583 LOG_RESULT_LOCATION(result);
584 return result;
585 }
586
587 return result;
588}
589
590static int ami306_resume(void *mlsl_handle,
591 struct ext_slave_descr *slave,
592 struct ext_slave_platform_data *pdata)
593{
594 int result = INV_SUCCESS;
595 unsigned char regs[] = {
596 AMI306_REG_CNTL4_1,
597 0x7E,
598 0xA0
599 };
600 /* Step1. Set CNTL1 reg to power model active (Write CNTL1:PC1=1) */
601 result = inv_serial_single_write(mlsl_handle, pdata->address,
602 AMI306_REG_CNTL1,
603 AMI306_BIT_CNTL1_PC1 |
604 AMI306_BIT_CNTL1_FS1);
605 if (result) {
606 LOG_RESULT_LOCATION(result);
607 return result;
608 }
609
610 /* Step2. Set CNTL2 reg to DRDY active high and enabled
611 (Write CNTL2:DREN=1) */
612 result = inv_serial_single_write(mlsl_handle, pdata->address,
613 AMI306_REG_CNTL2,
614 AMI306_BIT_CNTL2_DREN |
615 AMI306_BIT_CNTL2_DRP);
616 if (result) {
617 LOG_RESULT_LOCATION(result);
618 return result;
619 }
620
621 /* Step3. Set CNTL4 reg to for measurement speed: Write CNTL4, 0xA07E */
622 result = inv_serial_write(mlsl_handle, pdata->address,
623 ARRAY_SIZE(regs), regs);
624 if (result) {
625 LOG_RESULT_LOCATION(result);
626 return result;
627 }
628
629 /* Step4. skipped */
630
631 /* Step5. Set CNTL3 reg to forced measurement period
632 (Write CNTL3:FORCE=1) */
633 result = inv_serial_single_write(mlsl_handle, pdata->address,
634 AMI306_REG_CNTL3,
635 AMI306_BIT_CNTL3_F0RCE);
636
637 return result;
638}
639
640static int ami306_read(void *mlsl_handle,
641 struct ext_slave_descr *slave,
642 struct ext_slave_platform_data *pdata,
643 unsigned char *data)
644{
645 int result = INV_SUCCESS;
646 int ii;
647 short val[COMPASS_NUM_AXES];
648
649 result = ami306_mea(mlsl_handle, pdata, val);
650 if (result) {
651 LOG_RESULT_LOCATION(result);
652 return result;
653 }
654 for (ii = 0; ii < COMPASS_NUM_AXES; ii++) {
655 val[ii] -= AMI_STANDARD_OFFSET;
656 data[2 * ii] = val[ii] & 0xFF;
657 data[(2 * ii) + 1] = (val[ii] >> 8) & 0xFF;
658 }
659 return result;
660}
661
662static int ami306_init(void *mlsl_handle,
663 struct ext_slave_descr *slave,
664 struct ext_slave_platform_data *pdata)
665{
666 int result;
667 struct ami306_private_data *private_data;
668 private_data = (struct ami306_private_data *)
669 kzalloc(sizeof(struct ami306_private_data), GFP_KERNEL);
670
671 if (!private_data)
672 return INV_ERROR_MEMORY_EXAUSTED;
673
674 pdata->private_data = private_data;
675 result = ami306_set_bits8(mlsl_handle, pdata,
676 AMI_REG_CTRL1,
677 AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
678 if (result) {
679 LOG_RESULT_LOCATION(result);
680 return result;
681 }
682 /* Read Parameters */
683 result = ami306_read_param(mlsl_handle, slave, pdata);
684 if (result) {
685 LOG_RESULT_LOCATION(result);
686 return result;
687 }
688 /* Read Window */
689 result = ami306_initial_b0_adjust(mlsl_handle, slave, pdata);
690 if (result) {
691 LOG_RESULT_LOCATION(result);
692 return result;
693 }
694 result = ami306_start_sensor(mlsl_handle, pdata);
695 if (result) {
696 LOG_RESULT_LOCATION(result);
697 return result;
698 }
699 result = ami306_read_win(mlsl_handle, slave, pdata);
700 if (result) {
701 LOG_RESULT_LOCATION(result);
702 return result;
703 }
704
705 result = inv_serial_single_write(mlsl_handle, pdata->address,
706 AMI306_REG_CNTL1, 0);
707 if (result) {
708 LOG_RESULT_LOCATION(result);
709 return result;
710 }
711
712 return INV_SUCCESS;
713}
714
715static int ami306_exit(void *mlsl_handle,
716 struct ext_slave_descr *slave,
717 struct ext_slave_platform_data *pdata)
718{
719 kfree(pdata->private_data);
720 return INV_SUCCESS;
721}
722
723static int ami306_config(void *mlsl_handle,
724 struct ext_slave_descr *slave,
725 struct ext_slave_platform_data *pdata,
726 struct ext_slave_config *data)
727{
728 if (!data->data) {
729 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
730 return INV_ERROR_INVALID_PARAMETER;
731 }
732
733 switch (data->key) {
734 case MPU_SLAVE_PARAM:
735 case MPU_SLAVE_WINDOW:
736 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
737 case MPU_SLAVE_CONFIG_ODR_RESUME:
738 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
739 case MPU_SLAVE_CONFIG_FSR_RESUME:
740 case MPU_SLAVE_CONFIG_MOT_THS:
741 case MPU_SLAVE_CONFIG_NMOT_THS:
742 case MPU_SLAVE_CONFIG_MOT_DUR:
743 case MPU_SLAVE_CONFIG_NMOT_DUR:
744 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
745 case MPU_SLAVE_CONFIG_IRQ_RESUME:
746 default:
747 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
748 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
749 };
750
751 return INV_SUCCESS;
752}
753
754static int ami306_get_config(void *mlsl_handle,
755 struct ext_slave_descr *slave,
756 struct ext_slave_platform_data *pdata,
757 struct ext_slave_config *data)
758{
759 int result;
760 struct ami306_private_data *private_data = pdata->private_data;
761 if (!data->data) {
762 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
763 return INV_ERROR_INVALID_PARAMETER;
764 }
765
766 switch (data->key) {
767 case MPU_SLAVE_PARAM:
768 if (sizeof(struct ami_sensor_parametor) > data->len) {
769 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
770 return INV_ERROR_INVALID_PARAMETER;
771 }
772 if (data->apply) {
773 result = ami306_read_param(mlsl_handle, slave, pdata);
774 if (result) {
775 LOG_RESULT_LOCATION(result);
776 return result;
777 }
778 }
779 memcpy(data->data, &private_data->param,
780 sizeof(struct ami_sensor_parametor));
781 break;
782 case MPU_SLAVE_WINDOW:
783 if (sizeof(struct ami_win_parameter) > data->len) {
784 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
785 return INV_ERROR_INVALID_PARAMETER;
786 }
787 if (data->apply) {
788 result = ami306_read_win(mlsl_handle, slave, pdata);
789 if (result) {
790 LOG_RESULT_LOCATION(result);
791 return result;
792 }
793 }
794 memcpy(data->data, &private_data->win,
795 sizeof(struct ami_win_parameter));
796 break;
797 case MPU_SLAVE_SEARCHOFFSET:
798 if (sizeof(struct ami_win_parameter) > data->len) {
799 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
800 return INV_ERROR_INVALID_PARAMETER;
801 }
802 if (data->apply) {
803 result = ami306_search_offset(mlsl_handle,
804 slave, pdata);
805 if (result) {
806 LOG_RESULT_LOCATION(result);
807 return result;
808 }
809 /* Start sensor */
810 result = ami306_start_sensor(mlsl_handle, pdata);
811 if (result) {
812 LOG_RESULT_LOCATION(result);
813 return result;
814 }
815 result = ami306_read_win(mlsl_handle, slave, pdata);
816 if (result) {
817 LOG_RESULT_LOCATION(result);
818 return result;
819 }
820 }
821 memcpy(data->data, &private_data->win,
822 sizeof(struct ami_win_parameter));
823 break;
824 case MPU_SLAVE_READWINPARAMS:
825 if (sizeof(struct ami_win_parameter) > data->len) {
826 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
827 return INV_ERROR_INVALID_PARAMETER;
828 }
829 if (data->apply) {
830 result = ami306_initial_b0_adjust(mlsl_handle,
831 slave, pdata);
832 if (result) {
833 LOG_RESULT_LOCATION(result);
834 return result;
835 }
836 /* Start sensor */
837 result = ami306_start_sensor(mlsl_handle, pdata);
838 if (result) {
839 LOG_RESULT_LOCATION(result);
840 return result;
841 }
842 result = ami306_read_win(mlsl_handle, slave, pdata);
843 if (result) {
844 LOG_RESULT_LOCATION(result);
845 return result;
846 }
847 }
848 memcpy(data->data, &private_data->win,
849 sizeof(struct ami_win_parameter));
850 break;
851 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
852 (*(unsigned long *)data->data) = 0;
853 break;
854 case MPU_SLAVE_CONFIG_ODR_RESUME:
855 (*(unsigned long *)data->data) = 50000;
856 break;
857 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
858 case MPU_SLAVE_CONFIG_FSR_RESUME:
859 case MPU_SLAVE_CONFIG_MOT_THS:
860 case MPU_SLAVE_CONFIG_NMOT_THS:
861 case MPU_SLAVE_CONFIG_MOT_DUR:
862 case MPU_SLAVE_CONFIG_NMOT_DUR:
863 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
864 case MPU_SLAVE_CONFIG_IRQ_RESUME:
865 case MPU_SLAVE_READ_SCALE:
866 default:
867 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
868 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
869 };
870
871 return INV_SUCCESS;
872}
873
874static struct ext_slave_read_trigger ami306_read_trigger = {
875 /*.reg = */ AMI_REG_CTRL3,
876 /*.value = */ AMI_CTRL3_FORCE_BIT
877};
878
879static struct ext_slave_descr ami306_descr = {
880 .init = ami306_init,
881 .exit = ami306_exit,
882 .suspend = ami306_suspend,
883 .resume = ami306_resume,
884 .read = ami306_read,
885 .config = ami306_config,
886 .get_config = ami306_get_config,
887 .name = "ami306",
888 .type = EXT_SLAVE_TYPE_COMPASS,
889 .id = COMPASS_ID_AMI306,
890 .read_reg = 0x0E,
891 .read_len = 13,
892 .endian = EXT_SLAVE_LITTLE_ENDIAN,
893 .range = {5461, 3333},
894 .trigger = &ami306_read_trigger,
895};
896
897static
898struct ext_slave_descr *ami306_get_slave_descr(void)
899{
900 return &ami306_descr;
901}
902
903/* -------------------------------------------------------------------------- */
904struct ami306_mod_private_data {
905 struct i2c_client *client;
906 struct ext_slave_platform_data *pdata;
907};
908
909static unsigned short normal_i2c[] = { I2C_CLIENT_END };
910
911static int ami306_mod_probe(struct i2c_client *client,
912 const struct i2c_device_id *devid)
913{
914 struct ext_slave_platform_data *pdata;
915 struct ami306_mod_private_data *private_data;
916 int result = 0;
917
918 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
919
920 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
921 result = -ENODEV;
922 goto out_no_free;
923 }
924
925 pdata = client->dev.platform_data;
926 if (!pdata) {
927 dev_err(&client->adapter->dev,
928 "Missing platform data for slave %s\n", devid->name);
929 result = -EFAULT;
930 goto out_no_free;
931 }
932
933 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
934 if (!private_data) {
935 result = -ENOMEM;
936 goto out_no_free;
937 }
938
939 i2c_set_clientdata(client, private_data);
940 private_data->client = client;
941 private_data->pdata = pdata;
942
943 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
944 ami306_get_slave_descr);
945 if (result) {
946 dev_err(&client->adapter->dev,
947 "Slave registration failed: %s, %d\n",
948 devid->name, result);
949 goto out_free_memory;
950 }
951
952 return result;
953
954out_free_memory:
955 kfree(private_data);
956out_no_free:
957 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
958 return result;
959
960}
961
962static int ami306_mod_remove(struct i2c_client *client)
963{
964 struct ami306_mod_private_data *private_data =
965 i2c_get_clientdata(client);
966
967 dev_dbg(&client->adapter->dev, "%s\n", __func__);
968
969 inv_mpu_unregister_slave(client, private_data->pdata,
970 ami306_get_slave_descr);
971
972 kfree(private_data);
973 return 0;
974}
975
976static const struct i2c_device_id ami306_mod_id[] = {
977 { "ami306", COMPASS_ID_AMI306 },
978 {}
979};
980
981MODULE_DEVICE_TABLE(i2c, ami306_mod_id);
982
983static struct i2c_driver ami306_mod_driver = {
984 .class = I2C_CLASS_HWMON,
985 .probe = ami306_mod_probe,
986 .remove = ami306_mod_remove,
987 .id_table = ami306_mod_id,
988 .driver = {
989 .owner = THIS_MODULE,
990 .name = "ami306_mod",
991 },
992 .address_list = normal_i2c,
993};
994
995static int __init ami306_mod_init(void)
996{
997 int res = i2c_add_driver(&ami306_mod_driver);
998 pr_info("%s: Probe name %s\n", __func__, "ami306_mod");
999 if (res)
1000 pr_err("%s failed\n", __func__);
1001 return res;
1002}
1003
1004static void __exit ami306_mod_exit(void)
1005{
1006 pr_info("%s\n", __func__);
1007 i2c_del_driver(&ami306_mod_driver);
1008}
1009
1010module_init(ami306_mod_init);
1011module_exit(ami306_mod_exit);
1012
1013MODULE_AUTHOR("Invensense Corporation");
1014MODULE_DESCRIPTION("Driver to integrate AMI306 sensor with the MPU");
1015MODULE_LICENSE("GPL");
1016MODULE_ALIAS("ami306_mod");
1017
1018/**
1019 * @}
1020 */