aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/accel/kxtf9.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/accel/kxtf9.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu/accel/kxtf9.c')
-rw-r--r--drivers/misc/inv_mpu/accel/kxtf9.c841
1 files changed, 841 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/accel/kxtf9.c b/drivers/misc/inv_mpu/accel/kxtf9.c
new file mode 100644
index 00000000000..80776f249c6
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/kxtf9.c
@@ -0,0 +1,841 @@
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 ACCELDL
22 * @brief Accelerometer setup and handling methods for Kionix KXTF9.
23 *
24 * @{
25 * @file kxtf9.c
26 * @brief Accelerometer setup and handling methods for Kionix KXTF9.
27*/
28
29/* -------------------------------------------------------------------------- */
30
31#undef MPL_LOG_NDEBUG
32#define MPL_LOG_NDEBUG 1
33
34#include <linux/i2c.h>
35#include <linux/module.h>
36#include <linux/moduleparam.h>
37#include <linux/kernel.h>
38#include <linux/errno.h>
39#include <linux/slab.h>
40#include <linux/delay.h>
41#include "mpu-dev.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-acc"
49
50#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
51#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
52#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
53#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
54#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
55#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
56#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
57#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
58#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
59#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
60#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
61#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
62#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
63#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
64#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
65#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
66#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
67#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
68#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
69#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
70#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
71#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
72#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
73#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
74#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
75#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
76#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
77#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
78#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
79#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
80#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
81#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
82#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
83#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
84#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
85#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
86#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
87#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
88#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
89
90#define KXTF9_MAX_DUR (0xFF)
91#define KXTF9_MAX_THS (0xFF)
92#define KXTF9_THS_COUNTS_P_G (32)
93
94/* -------------------------------------------------------------------------- */
95
96struct kxtf9_config {
97 unsigned long odr; /* Output data rate mHz */
98 unsigned int fsr; /* full scale range mg */
99 unsigned int ths; /* Motion no-motion thseshold mg */
100 unsigned int dur; /* Motion no-motion duration ms */
101 unsigned int irq_type;
102 unsigned char reg_ths;
103 unsigned char reg_dur;
104 unsigned char reg_odr;
105 unsigned char reg_int_cfg1;
106 unsigned char reg_int_cfg2;
107 unsigned char ctrl_reg1;
108};
109
110struct kxtf9_private_data {
111 struct kxtf9_config suspend;
112 struct kxtf9_config resume;
113};
114
115static int kxtf9_set_ths(void *mlsl_handle,
116 struct ext_slave_platform_data *pdata,
117 struct kxtf9_config *config, int apply, long ths)
118{
119 int result = INV_SUCCESS;
120 if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS)
121 ths = (long)(KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G;
122
123 if (ths < 0)
124 ths = 0;
125
126 config->ths = ths;
127 config->reg_ths = (unsigned char)
128 ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000);
129 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
130 if (apply)
131 result = inv_serial_single_write(mlsl_handle, pdata->address,
132 KXTF9_WUF_THRESH,
133 config->reg_ths);
134 return result;
135}
136
137static int kxtf9_set_dur(void *mlsl_handle,
138 struct ext_slave_platform_data *pdata,
139 struct kxtf9_config *config, int apply, long dur)
140{
141 int result = INV_SUCCESS;
142 long reg_dur = (dur * config->odr) / 1000000L;
143 config->dur = dur;
144
145 if (reg_dur > KXTF9_MAX_DUR)
146 reg_dur = KXTF9_MAX_DUR;
147
148 config->reg_dur = (unsigned char)reg_dur;
149 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
150 if (apply)
151 result = inv_serial_single_write(mlsl_handle, pdata->address,
152 KXTF9_WUF_TIMER,
153 (unsigned char)reg_dur);
154 return result;
155}
156
157/**
158 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
159 * duration will not be used uless the type is MOT or NMOT.
160 *
161 * @param config configuration to apply to, suspend or resume
162 * @param irq_type The type of IRQ. Valid values are
163 * - MPU_SLAVE_IRQ_TYPE_NONE
164 * - MPU_SLAVE_IRQ_TYPE_MOTION
165 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
166 */
167static int kxtf9_set_irq(void *mlsl_handle,
168 struct ext_slave_platform_data *pdata,
169 struct kxtf9_config *config, int apply, long irq_type)
170{
171 int result = INV_SUCCESS;
172 struct kxtf9_private_data *private_data = pdata->private_data;
173
174 config->irq_type = (unsigned char)irq_type;
175 config->ctrl_reg1 &= ~0x22;
176 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
177 config->ctrl_reg1 |= 0x20;
178 config->reg_int_cfg1 = 0x38;
179 config->reg_int_cfg2 = 0x00;
180 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
181 config->ctrl_reg1 |= 0x02;
182 if ((unsigned long)config ==
183 (unsigned long)&private_data->suspend)
184 config->reg_int_cfg1 = 0x34;
185 else
186 config->reg_int_cfg1 = 0x24;
187 config->reg_int_cfg2 = 0xE0;
188 } else {
189 config->reg_int_cfg1 = 0x00;
190 config->reg_int_cfg2 = 0x00;
191 }
192
193 if (apply) {
194 /* Must clear bit 7 before writing new configuration */
195 result = inv_serial_single_write(mlsl_handle, pdata->address,
196 KXTF9_CTRL_REG1, 0x40);
197 result = inv_serial_single_write(mlsl_handle, pdata->address,
198 KXTF9_INT_CTRL_REG1,
199 config->reg_int_cfg1);
200 result = inv_serial_single_write(mlsl_handle, pdata->address,
201 KXTF9_INT_CTRL_REG2,
202 config->reg_int_cfg2);
203 result = inv_serial_single_write(mlsl_handle, pdata->address,
204 KXTF9_CTRL_REG1,
205 config->ctrl_reg1);
206 }
207 MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n",
208 (unsigned long)config->ctrl_reg1,
209 (unsigned long)config->reg_int_cfg1,
210 (unsigned long)config->reg_int_cfg2);
211
212 return result;
213}
214
215/**
216 * Set the Output data rate for the particular configuration
217 *
218 * @param config Config to modify with new ODR
219 * @param odr Output data rate in units of 1/1000Hz
220 */
221static int kxtf9_set_odr(void *mlsl_handle,
222 struct ext_slave_platform_data *pdata,
223 struct kxtf9_config *config, int apply, long odr)
224{
225 unsigned char bits;
226 int result = INV_SUCCESS;
227
228 /* Data sheet says there is 12.5 hz, but that seems to produce a single
229 * correct data value, thus we remove it from the table */
230 if (odr > 400000L) {
231 config->odr = 800000L;
232 bits = 0x06;
233 } else if (odr > 200000L) {
234 config->odr = 400000L;
235 bits = 0x05;
236 } else if (odr > 100000L) {
237 config->odr = 200000L;
238 bits = 0x04;
239 } else if (odr > 50000) {
240 config->odr = 100000L;
241 bits = 0x03;
242 } else if (odr > 25000) {
243 config->odr = 50000;
244 bits = 0x02;
245 } else if (odr != 0) {
246 config->odr = 25000;
247 bits = 0x01;
248 } else {
249 config->odr = 0;
250 bits = 0;
251 }
252
253 if (odr != 0)
254 config->ctrl_reg1 |= 0x80;
255 else
256 config->ctrl_reg1 &= ~0x80;
257
258 config->reg_odr = bits;
259 kxtf9_set_dur(mlsl_handle, pdata, config, apply, config->dur);
260 MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
261 if (apply) {
262 result = inv_serial_single_write(mlsl_handle, pdata->address,
263 KXTF9_DATA_CTRL_REG,
264 config->reg_odr);
265 result = inv_serial_single_write(mlsl_handle, pdata->address,
266 KXTF9_CTRL_REG1, 0x40);
267 result = inv_serial_single_write(mlsl_handle, pdata->address,
268 KXTF9_CTRL_REG1,
269 config->ctrl_reg1);
270 }
271 return result;
272}
273
274/**
275 * Set the full scale range of the accels
276 *
277 * @param config pointer to configuration
278 * @param fsr requested full scale range
279 */
280static int kxtf9_set_fsr(void *mlsl_handle,
281 struct ext_slave_platform_data *pdata,
282 struct kxtf9_config *config, int apply, long fsr)
283{
284 int result = INV_SUCCESS;
285
286 config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7);
287 if (fsr <= 2000) {
288 config->fsr = 2000;
289 config->ctrl_reg1 |= 0x00;
290 } else if (fsr <= 4000) {
291 config->fsr = 4000;
292 config->ctrl_reg1 |= 0x08;
293 } else {
294 config->fsr = 8000;
295 config->ctrl_reg1 |= 0x10;
296 }
297
298 MPL_LOGV("FSR: %d\n", config->fsr);
299 if (apply) {
300 /* Must clear bit 7 before writing new configuration */
301 result = inv_serial_single_write(mlsl_handle, pdata->address,
302 KXTF9_CTRL_REG1, 0x40);
303 result = inv_serial_single_write(mlsl_handle, pdata->address,
304 KXTF9_CTRL_REG1,
305 config->ctrl_reg1);
306 }
307 return result;
308}
309
310static int kxtf9_suspend(void *mlsl_handle,
311 struct ext_slave_descr *slave,
312 struct ext_slave_platform_data *pdata)
313{
314 int result;
315 unsigned char data;
316 struct kxtf9_private_data *private_data = pdata->private_data;
317
318 /* Wake up */
319 result = inv_serial_single_write(mlsl_handle, pdata->address,
320 KXTF9_CTRL_REG1, 0x40);
321 if (result) {
322 LOG_RESULT_LOCATION(result);
323 return result;
324 }
325 /* INT_CTRL_REG1: */
326 result = inv_serial_single_write(mlsl_handle, pdata->address,
327 KXTF9_INT_CTRL_REG1,
328 private_data->suspend.reg_int_cfg1);
329 if (result) {
330 LOG_RESULT_LOCATION(result);
331 return result;
332 }
333 /* WUF_THRESH: */
334 result = inv_serial_single_write(mlsl_handle, pdata->address,
335 KXTF9_WUF_THRESH,
336 private_data->suspend.reg_ths);
337 if (result) {
338 LOG_RESULT_LOCATION(result);
339 return result;
340 }
341 /* DATA_CTRL_REG */
342 result = inv_serial_single_write(mlsl_handle, pdata->address,
343 KXTF9_DATA_CTRL_REG,
344 private_data->suspend.reg_odr);
345 if (result) {
346 LOG_RESULT_LOCATION(result);
347 return result;
348 }
349 /* WUF_TIMER */
350 result = inv_serial_single_write(mlsl_handle, pdata->address,
351 KXTF9_WUF_TIMER,
352 private_data->suspend.reg_dur);
353 if (result) {
354 LOG_RESULT_LOCATION(result);
355 return result;
356 }
357
358 /* Normal operation */
359 result = inv_serial_single_write(mlsl_handle, pdata->address,
360 KXTF9_CTRL_REG1,
361 private_data->suspend.ctrl_reg1);
362 if (result) {
363 LOG_RESULT_LOCATION(result);
364 return result;
365 }
366 result = inv_serial_read(mlsl_handle, pdata->address,
367 KXTF9_INT_REL, 1, &data);
368 if (result) {
369 LOG_RESULT_LOCATION(result);
370 return result;
371 }
372
373 return result;
374}
375
376/* full scale setting - register and mask */
377#define ACCEL_KIONIX_CTRL_REG (0x1b)
378#define ACCEL_KIONIX_CTRL_MASK (0x18)
379
380static int kxtf9_resume(void *mlsl_handle,
381 struct ext_slave_descr *slave,
382 struct ext_slave_platform_data *pdata)
383{
384 int result = INV_SUCCESS;
385 unsigned char data;
386 struct kxtf9_private_data *private_data = pdata->private_data;
387
388 /* Wake up */
389 result = inv_serial_single_write(mlsl_handle, pdata->address,
390 KXTF9_CTRL_REG1, 0x40);
391 if (result) {
392 LOG_RESULT_LOCATION(result);
393 return result;
394 }
395 /* INT_CTRL_REG1: */
396 result = inv_serial_single_write(mlsl_handle, pdata->address,
397 KXTF9_INT_CTRL_REG1,
398 private_data->resume.reg_int_cfg1);
399 if (result) {
400 LOG_RESULT_LOCATION(result);
401 return result;
402 }
403 /* WUF_THRESH: */
404 result = inv_serial_single_write(mlsl_handle, pdata->address,
405 KXTF9_WUF_THRESH,
406 private_data->resume.reg_ths);
407 if (result) {
408 LOG_RESULT_LOCATION(result);
409 return result;
410 }
411 /* DATA_CTRL_REG */
412 result = inv_serial_single_write(mlsl_handle, pdata->address,
413 KXTF9_DATA_CTRL_REG,
414 private_data->resume.reg_odr);
415 if (result) {
416 LOG_RESULT_LOCATION(result);
417 return result;
418 }
419 /* WUF_TIMER */
420 result = inv_serial_single_write(mlsl_handle, pdata->address,
421 KXTF9_WUF_TIMER,
422 private_data->resume.reg_dur);
423 if (result) {
424 LOG_RESULT_LOCATION(result);
425 return result;
426 }
427
428 /* Normal operation */
429 result = inv_serial_single_write(mlsl_handle, pdata->address,
430 KXTF9_CTRL_REG1,
431 private_data->resume.ctrl_reg1);
432 if (result) {
433 LOG_RESULT_LOCATION(result);
434 return result;
435 }
436 result = inv_serial_read(mlsl_handle, pdata->address,
437 KXTF9_INT_REL, 1, &data);
438 if (result) {
439 LOG_RESULT_LOCATION(result);
440 return result;
441 }
442
443 return INV_SUCCESS;
444}
445
446static int kxtf9_init(void *mlsl_handle,
447 struct ext_slave_descr *slave,
448 struct ext_slave_platform_data *pdata)
449{
450
451 struct kxtf9_private_data *private_data;
452 int result = INV_SUCCESS;
453
454 private_data = (struct kxtf9_private_data *)
455 kzalloc(sizeof(struct kxtf9_private_data), GFP_KERNEL);
456
457 if (!private_data)
458 return INV_ERROR_MEMORY_EXAUSTED;
459
460 /* RAM reset */
461 /* Fastest Reset */
462 result = inv_serial_single_write(mlsl_handle, pdata->address,
463 KXTF9_CTRL_REG1, 0x40);
464 if (result) {
465 LOG_RESULT_LOCATION(result);
466 return result;
467 }
468 /* Fastest Reset */
469 result = inv_serial_single_write(mlsl_handle, pdata->address,
470 KXTF9_DATA_CTRL_REG, 0x36);
471 if (result) {
472 LOG_RESULT_LOCATION(result);
473 return result;
474 }
475 /* Reset */
476 result = inv_serial_single_write(mlsl_handle, pdata->address,
477 KXTF9_CTRL_REG3, 0xcd);
478 if (result) {
479 LOG_RESULT_LOCATION(result);
480 return result;
481 }
482 msleep(2);
483
484 pdata->private_data = private_data;
485
486 private_data->resume.ctrl_reg1 = 0xC0;
487 private_data->suspend.ctrl_reg1 = 0x40;
488
489 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend,
490 false, 1000);
491 if (result) {
492 LOG_RESULT_LOCATION(result);
493 return result;
494 }
495 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume,
496 false, 2540);
497 if (result) {
498 LOG_RESULT_LOCATION(result);
499 return result;
500 }
501
502 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend,
503 false, 50000);
504 if (result) {
505 LOG_RESULT_LOCATION(result);
506 return result;
507 }
508 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume,
509 false, 200000L);
510
511 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend,
512 false, 2000);
513 if (result) {
514 LOG_RESULT_LOCATION(result);
515 return result;
516 }
517 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume,
518 false, 2000);
519 if (result) {
520 LOG_RESULT_LOCATION(result);
521 return result;
522 }
523
524 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend,
525 false, 80);
526 if (result) {
527 LOG_RESULT_LOCATION(result);
528 return result;
529 }
530 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume,
531 false, 40);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536
537 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend,
538 false, MPU_SLAVE_IRQ_TYPE_NONE);
539 if (result) {
540 LOG_RESULT_LOCATION(result);
541 return result;
542 }
543 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume,
544 false, MPU_SLAVE_IRQ_TYPE_NONE);
545 if (result) {
546 LOG_RESULT_LOCATION(result);
547 return result;
548 }
549 return result;
550}
551
552static int kxtf9_exit(void *mlsl_handle,
553 struct ext_slave_descr *slave,
554 struct ext_slave_platform_data *pdata)
555{
556 kfree(pdata->private_data);
557 return INV_SUCCESS;
558}
559
560static int kxtf9_config(void *mlsl_handle,
561 struct ext_slave_descr *slave,
562 struct ext_slave_platform_data *pdata,
563 struct ext_slave_config *data)
564{
565 struct kxtf9_private_data *private_data = pdata->private_data;
566 if (!data->data)
567 return INV_ERROR_INVALID_PARAMETER;
568
569 switch (data->key) {
570 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
571 return kxtf9_set_odr(mlsl_handle, pdata,
572 &private_data->suspend,
573 data->apply, *((long *)data->data));
574 case MPU_SLAVE_CONFIG_ODR_RESUME:
575 return kxtf9_set_odr(mlsl_handle, pdata,
576 &private_data->resume,
577 data->apply, *((long *)data->data));
578 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
579 return kxtf9_set_fsr(mlsl_handle, pdata,
580 &private_data->suspend,
581 data->apply, *((long *)data->data));
582 case MPU_SLAVE_CONFIG_FSR_RESUME:
583 return kxtf9_set_fsr(mlsl_handle, pdata,
584 &private_data->resume,
585 data->apply, *((long *)data->data));
586 case MPU_SLAVE_CONFIG_MOT_THS:
587 return kxtf9_set_ths(mlsl_handle, pdata,
588 &private_data->suspend,
589 data->apply, *((long *)data->data));
590 case MPU_SLAVE_CONFIG_NMOT_THS:
591 return kxtf9_set_ths(mlsl_handle, pdata,
592 &private_data->resume,
593 data->apply, *((long *)data->data));
594 case MPU_SLAVE_CONFIG_MOT_DUR:
595 return kxtf9_set_dur(mlsl_handle, pdata,
596 &private_data->suspend,
597 data->apply, *((long *)data->data));
598 case MPU_SLAVE_CONFIG_NMOT_DUR:
599 return kxtf9_set_dur(mlsl_handle, pdata,
600 &private_data->resume,
601 data->apply, *((long *)data->data));
602 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
603 return kxtf9_set_irq(mlsl_handle, pdata,
604 &private_data->suspend,
605 data->apply, *((long *)data->data));
606 case MPU_SLAVE_CONFIG_IRQ_RESUME:
607 return kxtf9_set_irq(mlsl_handle, pdata,
608 &private_data->resume,
609 data->apply, *((long *)data->data));
610 default:
611 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
612 };
613
614 return INV_SUCCESS;
615}
616
617static int kxtf9_get_config(void *mlsl_handle,
618 struct ext_slave_descr *slave,
619 struct ext_slave_platform_data *pdata,
620 struct ext_slave_config *data)
621{
622 struct kxtf9_private_data *private_data = pdata->private_data;
623 if (!data->data)
624 return INV_ERROR_INVALID_PARAMETER;
625
626 switch (data->key) {
627 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
628 (*(unsigned long *)data->data) =
629 (unsigned long)private_data->suspend.odr;
630 break;
631 case MPU_SLAVE_CONFIG_ODR_RESUME:
632 (*(unsigned long *)data->data) =
633 (unsigned long)private_data->resume.odr;
634 break;
635 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
636 (*(unsigned long *)data->data) =
637 (unsigned long)private_data->suspend.fsr;
638 break;
639 case MPU_SLAVE_CONFIG_FSR_RESUME:
640 (*(unsigned long *)data->data) =
641 (unsigned long)private_data->resume.fsr;
642 break;
643 case MPU_SLAVE_CONFIG_MOT_THS:
644 (*(unsigned long *)data->data) =
645 (unsigned long)private_data->suspend.ths;
646 break;
647 case MPU_SLAVE_CONFIG_NMOT_THS:
648 (*(unsigned long *)data->data) =
649 (unsigned long)private_data->resume.ths;
650 break;
651 case MPU_SLAVE_CONFIG_MOT_DUR:
652 (*(unsigned long *)data->data) =
653 (unsigned long)private_data->suspend.dur;
654 break;
655 case MPU_SLAVE_CONFIG_NMOT_DUR:
656 (*(unsigned long *)data->data) =
657 (unsigned long)private_data->resume.dur;
658 break;
659 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
660 (*(unsigned long *)data->data) =
661 (unsigned long)private_data->suspend.irq_type;
662 break;
663 case MPU_SLAVE_CONFIG_IRQ_RESUME:
664 (*(unsigned long *)data->data) =
665 (unsigned long)private_data->resume.irq_type;
666 break;
667 default:
668 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
669 };
670
671 return INV_SUCCESS;
672}
673
674static int kxtf9_read(void *mlsl_handle,
675 struct ext_slave_descr *slave,
676 struct ext_slave_platform_data *pdata,
677 unsigned char *data)
678{
679 int result;
680 unsigned char reg;
681 result = inv_serial_read(mlsl_handle, pdata->address,
682 KXTF9_INT_SRC_REG2, 1, &reg);
683 if (result) {
684 LOG_RESULT_LOCATION(result);
685 return result;
686 }
687
688 if (!(reg & 0x10))
689 return INV_ERROR_ACCEL_DATA_NOT_READY;
690
691 result = inv_serial_read(mlsl_handle, pdata->address,
692 slave->read_reg, slave->read_len, data);
693 if (result) {
694 LOG_RESULT_LOCATION(result);
695 return result;
696 }
697 return result;
698}
699
700static struct ext_slave_descr kxtf9_descr = {
701 .init = kxtf9_init,
702 .exit = kxtf9_exit,
703 .suspend = kxtf9_suspend,
704 .resume = kxtf9_resume,
705 .read = kxtf9_read,
706 .config = kxtf9_config,
707 .get_config = kxtf9_get_config,
708 .name = "kxtf9",
709 .type = EXT_SLAVE_TYPE_ACCEL,
710 .id = ACCEL_ID_KXTF9,
711 .read_reg = 0x06,
712 .read_len = 6,
713 .endian = EXT_SLAVE_LITTLE_ENDIAN,
714 .range = {2, 0},
715 .trigger = NULL,
716};
717
718static
719struct ext_slave_descr *kxtf9_get_slave_descr(void)
720{
721 return &kxtf9_descr;
722}
723
724/* -------------------------------------------------------------------------- */
725struct kxtf9_mod_private_data {
726 struct i2c_client *client;
727 struct ext_slave_platform_data *pdata;
728};
729
730static unsigned short normal_i2c[] = { I2C_CLIENT_END };
731
732static int kxtf9_mod_probe(struct i2c_client *client,
733 const struct i2c_device_id *devid)
734{
735 struct ext_slave_platform_data *pdata;
736 struct kxtf9_mod_private_data *private_data;
737 int result = 0;
738
739 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
740
741 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
742 result = -ENODEV;
743 goto out_no_free;
744 }
745
746 pdata = client->dev.platform_data;
747 if (!pdata) {
748 dev_err(&client->adapter->dev,
749 "Missing platform data for slave %s\n", devid->name);
750 result = -EFAULT;
751 goto out_no_free;
752 }
753
754 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
755 if (!private_data) {
756 result = -ENOMEM;
757 goto out_no_free;
758 }
759
760 i2c_set_clientdata(client, private_data);
761 private_data->client = client;
762 private_data->pdata = pdata;
763
764 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
765 kxtf9_get_slave_descr);
766 if (result) {
767 dev_err(&client->adapter->dev,
768 "Slave registration failed: %s, %d\n",
769 devid->name, result);
770 goto out_free_memory;
771 }
772
773 return result;
774
775out_free_memory:
776 kfree(private_data);
777out_no_free:
778 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
779 return result;
780
781}
782
783static int kxtf9_mod_remove(struct i2c_client *client)
784{
785 struct kxtf9_mod_private_data *private_data =
786 i2c_get_clientdata(client);
787
788 dev_dbg(&client->adapter->dev, "%s\n", __func__);
789
790 inv_mpu_unregister_slave(client, private_data->pdata,
791 kxtf9_get_slave_descr);
792
793 kfree(private_data);
794 return 0;
795}
796
797static const struct i2c_device_id kxtf9_mod_id[] = {
798 { "kxtf9", ACCEL_ID_KXTF9 },
799 {}
800};
801
802MODULE_DEVICE_TABLE(i2c, kxtf9_mod_id);
803
804static struct i2c_driver kxtf9_mod_driver = {
805 .class = I2C_CLASS_HWMON,
806 .probe = kxtf9_mod_probe,
807 .remove = kxtf9_mod_remove,
808 .id_table = kxtf9_mod_id,
809 .driver = {
810 .owner = THIS_MODULE,
811 .name = "kxtf9_mod",
812 },
813 .address_list = normal_i2c,
814};
815
816static int __init kxtf9_mod_init(void)
817{
818 int res = i2c_add_driver(&kxtf9_mod_driver);
819 pr_info("%s: Probe name %s\n", __func__, "kxtf9_mod");
820 if (res)
821 pr_err("%s failed\n", __func__);
822 return res;
823}
824
825static void __exit kxtf9_mod_exit(void)
826{
827 pr_info("%s\n", __func__);
828 i2c_del_driver(&kxtf9_mod_driver);
829}
830
831module_init(kxtf9_mod_init);
832module_exit(kxtf9_mod_exit);
833
834MODULE_AUTHOR("Invensense Corporation");
835MODULE_DESCRIPTION("Driver to integrate KXTF9 sensor with the MPU");
836MODULE_LICENSE("GPL");
837MODULE_ALIAS("kxtf9_mod");
838
839/**
840 * @}
841 */