aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/accel/lis3dh.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/lis3dh.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu/accel/lis3dh.c')
-rw-r--r--drivers/misc/inv_mpu/accel/lis3dh.c728
1 files changed, 728 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/accel/lis3dh.c b/drivers/misc/inv_mpu/accel/lis3dh.c
new file mode 100644
index 00000000000..27206e4b847
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/lis3dh.c
@@ -0,0 +1,728 @@
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 Provides the interface to setup and handle an accelerometer.
23 *
24 * @{
25 * @file lis3dh.c
26 * @brief Accelerometer setup and handling methods for ST LIS3DH.
27 */
28
29/* -------------------------------------------------------------------------- */
30
31#undef MPL_LOG_NDEBUG
32#define MPL_LOG_NDEBUG 0
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/* full scale setting - register & mask */
51#define LIS3DH_CTRL_REG1 (0x20)
52#define LIS3DH_CTRL_REG2 (0x21)
53#define LIS3DH_CTRL_REG3 (0x22)
54#define LIS3DH_CTRL_REG4 (0x23)
55#define LIS3DH_CTRL_REG5 (0x24)
56#define LIS3DH_CTRL_REG6 (0x25)
57#define LIS3DH_REFERENCE (0x26)
58#define LIS3DH_STATUS_REG (0x27)
59#define LIS3DH_OUT_X_L (0x28)
60#define LIS3DH_OUT_X_H (0x29)
61#define LIS3DH_OUT_Y_L (0x2a)
62#define LIS3DH_OUT_Y_H (0x2b)
63#define LIS3DH_OUT_Z_L (0x2c)
64#define LIS3DH_OUT_Z_H (0x2d)
65
66#define LIS3DH_INT1_CFG (0x30)
67#define LIS3DH_INT1_SRC (0x31)
68#define LIS3DH_INT1_THS (0x32)
69#define LIS3DH_INT1_DURATION (0x33)
70
71#define LIS3DH_MAX_DUR (0x7F)
72
73/* -------------------------------------------------------------------------- */
74
75struct lis3dh_config {
76 unsigned long odr;
77 unsigned int fsr; /* full scale range mg */
78 unsigned int ths; /* Motion no-motion thseshold mg */
79 unsigned int dur; /* Motion no-motion duration ms */
80 unsigned char reg_ths;
81 unsigned char reg_dur;
82 unsigned char ctrl_reg1;
83 unsigned char irq_type;
84 unsigned char mot_int1_cfg;
85};
86
87struct lis3dh_private_data {
88 struct lis3dh_config suspend;
89 struct lis3dh_config resume;
90};
91
92/* -------------------------------------------------------------------------- */
93
94static int lis3dh_set_ths(void *mlsl_handle,
95 struct ext_slave_platform_data *pdata,
96 struct lis3dh_config *config, int apply, long ths)
97{
98 int result = INV_SUCCESS;
99 if ((unsigned int)ths > 1000 * config->fsr)
100 ths = (long)1000 * config->fsr;
101
102 if (ths < 0)
103 ths = 0;
104
105 config->ths = ths;
106 config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
107 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
108 if (apply)
109 result = inv_serial_single_write(mlsl_handle, pdata->address,
110 LIS3DH_INT1_THS,
111 config->reg_ths);
112 return result;
113}
114
115static int lis3dh_set_dur(void *mlsl_handle,
116 struct ext_slave_platform_data *pdata,
117 struct lis3dh_config *config, int apply, long dur)
118{
119 int result = INV_SUCCESS;
120 long reg_dur = (dur * config->odr) / 1000000L;
121 config->dur = dur;
122
123 if (reg_dur > LIS3DH_MAX_DUR)
124 reg_dur = LIS3DH_MAX_DUR;
125
126 config->reg_dur = (unsigned char)reg_dur;
127 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
128 if (apply)
129 result = inv_serial_single_write(mlsl_handle, pdata->address,
130 LIS3DH_INT1_DURATION,
131 (unsigned char)reg_dur);
132 return result;
133}
134
135/**
136 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
137 * duration will not be used uless the type is MOT or NMOT.
138 *
139 * @param config configuration to apply to, suspend or resume
140 * @param irq_type The type of IRQ. Valid values are
141 * - MPU_SLAVE_IRQ_TYPE_NONE
142 * - MPU_SLAVE_IRQ_TYPE_MOTION
143 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
144 */
145static int lis3dh_set_irq(void *mlsl_handle,
146 struct ext_slave_platform_data *pdata,
147 struct lis3dh_config *config,
148 int apply, long irq_type)
149{
150 int result = INV_SUCCESS;
151 unsigned char reg1;
152 unsigned char reg2;
153
154 config->irq_type = (unsigned char)irq_type;
155 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
156 reg1 = 0x10;
157 reg2 = 0x00;
158 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
159 reg1 = 0x40;
160 reg2 = config->mot_int1_cfg;
161 } else {
162 reg1 = 0x00;
163 reg2 = 0x00;
164 }
165
166 if (apply) {
167 result = inv_serial_single_write(mlsl_handle, pdata->address,
168 LIS3DH_CTRL_REG3, reg1);
169 result = inv_serial_single_write(mlsl_handle, pdata->address,
170 LIS3DH_INT1_CFG, reg2);
171 }
172
173 return result;
174}
175
176/**
177 * Set the Output data rate for the particular configuration
178 *
179 * @param config Config to modify with new ODR
180 * @param odr Output data rate in units of 1/1000Hz
181 */
182static int lis3dh_set_odr(void *mlsl_handle,
183 struct ext_slave_platform_data *pdata,
184 struct lis3dh_config *config, int apply, long odr)
185{
186 unsigned char bits;
187 int result = INV_SUCCESS;
188
189 if (odr > 400000L) {
190 config->odr = 1250000L;
191 bits = 0x90;
192 } else if (odr > 200000L) {
193 config->odr = 400000L;
194 bits = 0x70;
195 } else if (odr > 100000L) {
196 config->odr = 200000L;
197 bits = 0x60;
198 } else if (odr > 50000) {
199 config->odr = 100000L;
200 bits = 0x50;
201 } else if (odr > 25000) {
202 config->odr = 50000;
203 bits = 0x40;
204 } else if (odr > 10000) {
205 config->odr = 25000;
206 bits = 0x30;
207 } else if (odr > 1000) {
208 config->odr = 10000;
209 bits = 0x20;
210 } else if (odr > 500) {
211 config->odr = 1000;
212 bits = 0x10;
213 } else {
214 config->odr = 0;
215 bits = 0;
216 }
217
218 config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xf);
219 lis3dh_set_dur(mlsl_handle, pdata, config, apply, config->dur);
220 MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
221 if (apply)
222 result = inv_serial_single_write(mlsl_handle, pdata->address,
223 LIS3DH_CTRL_REG1,
224 config->ctrl_reg1);
225 return result;
226}
227
228/**
229 * Set the full scale range of the accels
230 *
231 * @param config pointer to configuration
232 * @param fsr requested full scale range
233 */
234static int lis3dh_set_fsr(void *mlsl_handle,
235 struct ext_slave_platform_data *pdata,
236 struct lis3dh_config *config, int apply, long fsr)
237{
238 int result = INV_SUCCESS;
239 unsigned char reg1 = 0x48;
240
241 if (fsr <= 2048) {
242 config->fsr = 2048;
243 } else if (fsr <= 4096) {
244 reg1 |= 0x10;
245 config->fsr = 4096;
246 } else if (fsr <= 8192) {
247 reg1 |= 0x20;
248 config->fsr = 8192;
249 } else {
250 reg1 |= 0x30;
251 config->fsr = 16348;
252 }
253
254 lis3dh_set_ths(mlsl_handle, pdata, config, apply, config->ths);
255 MPL_LOGV("FSR: %d\n", config->fsr);
256 if (apply)
257 result = inv_serial_single_write(mlsl_handle, pdata->address,
258 LIS3DH_CTRL_REG4, reg1);
259
260 return result;
261}
262
263static int lis3dh_suspend(void *mlsl_handle,
264 struct ext_slave_descr *slave,
265 struct ext_slave_platform_data *pdata)
266{
267 int result = INV_SUCCESS;
268 unsigned char reg1;
269 unsigned char reg2;
270 struct lis3dh_private_data *private_data = pdata->private_data;
271
272 result = inv_serial_single_write(mlsl_handle, pdata->address,
273 LIS3DH_CTRL_REG1,
274 private_data->suspend.ctrl_reg1);
275
276 reg1 = 0x48;
277 if (private_data->suspend.fsr == 16384)
278 reg1 |= 0x30;
279 else if (private_data->suspend.fsr == 8192)
280 reg1 |= 0x20;
281 else if (private_data->suspend.fsr == 4096)
282 reg1 |= 0x10;
283 else if (private_data->suspend.fsr == 2048)
284 reg1 |= 0x00;
285
286 result = inv_serial_single_write(mlsl_handle, pdata->address,
287 LIS3DH_CTRL_REG4, reg1);
288 result = inv_serial_single_write(mlsl_handle, pdata->address,
289 LIS3DH_INT1_THS,
290 private_data->suspend.reg_ths);
291 result = inv_serial_single_write(mlsl_handle, pdata->address,
292 LIS3DH_INT1_DURATION,
293 private_data->suspend.reg_dur);
294
295 if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
296 reg1 = 0x10;
297 reg2 = 0x00;
298 } else if (private_data->suspend.irq_type ==
299 MPU_SLAVE_IRQ_TYPE_MOTION) {
300 reg1 = 0x40;
301 reg2 = private_data->suspend.mot_int1_cfg;
302 } else {
303 reg1 = 0x00;
304 reg2 = 0x00;
305 }
306 result = inv_serial_single_write(mlsl_handle, pdata->address,
307 LIS3DH_CTRL_REG3, reg1);
308 result = inv_serial_single_write(mlsl_handle, pdata->address,
309 LIS3DH_INT1_CFG, reg2);
310 result = inv_serial_read(mlsl_handle, pdata->address,
311 LIS3DH_CTRL_REG6, 1, &reg1);
312
313 return result;
314}
315
316static int lis3dh_resume(void *mlsl_handle,
317 struct ext_slave_descr *slave,
318 struct ext_slave_platform_data *pdata)
319{
320 int result;
321 unsigned char reg1;
322 unsigned char reg2;
323 struct lis3dh_private_data *private_data = pdata->private_data;
324
325 result = inv_serial_single_write(mlsl_handle, pdata->address,
326 LIS3DH_CTRL_REG1,
327 private_data->resume.ctrl_reg1);
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332 msleep(6);
333
334 /* Full Scale */
335 reg1 = 0x48;
336 if (private_data->suspend.fsr == 16384)
337 reg1 |= 0x30;
338 else if (private_data->suspend.fsr == 8192)
339 reg1 |= 0x20;
340 else if (private_data->suspend.fsr == 4096)
341 reg1 |= 0x10;
342 else if (private_data->suspend.fsr == 2048)
343 reg1 |= 0x00;
344
345 result = inv_serial_single_write(mlsl_handle, pdata->address,
346 LIS3DH_CTRL_REG4, reg1);
347 if (result) {
348 LOG_RESULT_LOCATION(result);
349 return result;
350 }
351
352 if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
353 reg1 = 0x10;
354 reg2 = 0x00;
355 } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
356 reg1 = 0x40;
357 reg2 = private_data->resume.mot_int1_cfg;
358 } else {
359 reg1 = 0x00;
360 reg2 = 0x00;
361 }
362 result = inv_serial_single_write(mlsl_handle, pdata->address,
363 LIS3DH_CTRL_REG3, reg1);
364 if (result) {
365 LOG_RESULT_LOCATION(result);
366 return result;
367 }
368 result = inv_serial_single_write(mlsl_handle, pdata->address,
369 LIS3DH_INT1_THS,
370 private_data->resume.reg_ths);
371 if (result) {
372 LOG_RESULT_LOCATION(result);
373 return result;
374 }
375 result = inv_serial_single_write(mlsl_handle, pdata->address,
376 LIS3DH_INT1_DURATION,
377 private_data->resume.reg_dur);
378 if (result) {
379 LOG_RESULT_LOCATION(result);
380 return result;
381 }
382 result = inv_serial_single_write(mlsl_handle, pdata->address,
383 LIS3DH_INT1_CFG, reg2);
384 if (result) {
385 LOG_RESULT_LOCATION(result);
386 return result;
387 }
388 result = inv_serial_read(mlsl_handle, pdata->address,
389 LIS3DH_CTRL_REG6, 1, &reg1);
390 if (result) {
391 LOG_RESULT_LOCATION(result);
392 return result;
393 }
394 return result;
395}
396
397static int lis3dh_read(void *mlsl_handle,
398 struct ext_slave_descr *slave,
399 struct ext_slave_platform_data *pdata,
400 unsigned char *data)
401{
402 int result = INV_SUCCESS;
403 result = inv_serial_read(mlsl_handle, pdata->address,
404 LIS3DH_STATUS_REG, 1, data);
405 if (data[0] & 0x0F) {
406 result = inv_serial_read(mlsl_handle, pdata->address,
407 slave->read_reg, slave->read_len,
408 data);
409 return result;
410 } else
411 return INV_ERROR_ACCEL_DATA_NOT_READY;
412}
413
414static int lis3dh_init(void *mlsl_handle,
415 struct ext_slave_descr *slave,
416 struct ext_slave_platform_data *pdata)
417{
418 int result;
419 long range;
420 struct lis3dh_private_data *private_data;
421 private_data = (struct lis3dh_private_data *)
422 kzalloc(sizeof(struct lis3dh_private_data), GFP_KERNEL);
423
424 if (!private_data)
425 return INV_ERROR_MEMORY_EXAUSTED;
426
427 pdata->private_data = private_data;
428
429 private_data->resume.ctrl_reg1 = 0x67;
430 private_data->suspend.ctrl_reg1 = 0x18;
431 private_data->resume.mot_int1_cfg = 0x95;
432 private_data->suspend.mot_int1_cfg = 0x2a;
433
434 lis3dh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0);
435 lis3dh_set_odr(mlsl_handle, pdata, &private_data->resume,
436 false, 200000L);
437
438 range = range_fixedpoint_to_long_mg(slave->range);
439 lis3dh_set_fsr(mlsl_handle, pdata, &private_data->suspend,
440 false, range);
441 lis3dh_set_fsr(mlsl_handle, pdata, &private_data->resume,
442 false, range);
443
444 lis3dh_set_ths(mlsl_handle, pdata, &private_data->suspend,
445 false, 80);
446 lis3dh_set_ths(mlsl_handle, pdata, &private_data->resume,
447 false, 40);
448
449 lis3dh_set_dur(mlsl_handle, pdata, &private_data->suspend,
450 false, 1000);
451 lis3dh_set_dur(mlsl_handle, pdata, &private_data->resume,
452 false, 2540);
453
454 lis3dh_set_irq(mlsl_handle, pdata, &private_data->suspend,
455 false, MPU_SLAVE_IRQ_TYPE_NONE);
456 lis3dh_set_irq(mlsl_handle, pdata, &private_data->resume,
457 false, MPU_SLAVE_IRQ_TYPE_NONE);
458
459 result = inv_serial_single_write(mlsl_handle, pdata->address,
460 LIS3DH_CTRL_REG1, 0x07);
461 msleep(6);
462
463 return INV_SUCCESS;
464}
465
466static int lis3dh_exit(void *mlsl_handle,
467 struct ext_slave_descr *slave,
468 struct ext_slave_platform_data *pdata)
469{
470 kfree(pdata->private_data);
471 return INV_SUCCESS;
472}
473
474static int lis3dh_config(void *mlsl_handle,
475 struct ext_slave_descr *slave,
476 struct ext_slave_platform_data *pdata,
477 struct ext_slave_config *data)
478{
479 struct lis3dh_private_data *private_data = pdata->private_data;
480 if (!data->data)
481 return INV_ERROR_INVALID_PARAMETER;
482
483 switch (data->key) {
484 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
485 return lis3dh_set_odr(mlsl_handle, pdata,
486 &private_data->suspend,
487 data->apply, *((long *)data->data));
488 case MPU_SLAVE_CONFIG_ODR_RESUME:
489 return lis3dh_set_odr(mlsl_handle, pdata,
490 &private_data->resume,
491 data->apply, *((long *)data->data));
492 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
493 return lis3dh_set_fsr(mlsl_handle, pdata,
494 &private_data->suspend,
495 data->apply, *((long *)data->data));
496 case MPU_SLAVE_CONFIG_FSR_RESUME:
497 return lis3dh_set_fsr(mlsl_handle, pdata,
498 &private_data->resume,
499 data->apply, *((long *)data->data));
500 case MPU_SLAVE_CONFIG_MOT_THS:
501 return lis3dh_set_ths(mlsl_handle, pdata,
502 &private_data->suspend,
503 data->apply, *((long *)data->data));
504 case MPU_SLAVE_CONFIG_NMOT_THS:
505 return lis3dh_set_ths(mlsl_handle, pdata,
506 &private_data->resume,
507 data->apply, *((long *)data->data));
508 case MPU_SLAVE_CONFIG_MOT_DUR:
509 return lis3dh_set_dur(mlsl_handle, pdata,
510 &private_data->suspend,
511 data->apply, *((long *)data->data));
512 case MPU_SLAVE_CONFIG_NMOT_DUR:
513 return lis3dh_set_dur(mlsl_handle, pdata,
514 &private_data->resume,
515 data->apply, *((long *)data->data));
516 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
517 return lis3dh_set_irq(mlsl_handle, pdata,
518 &private_data->suspend,
519 data->apply, *((long *)data->data));
520 case MPU_SLAVE_CONFIG_IRQ_RESUME:
521 return lis3dh_set_irq(mlsl_handle, pdata,
522 &private_data->resume,
523 data->apply, *((long *)data->data));
524 default:
525 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
526 };
527 return INV_SUCCESS;
528}
529
530static int lis3dh_get_config(void *mlsl_handle,
531 struct ext_slave_descr *slave,
532 struct ext_slave_platform_data *pdata,
533 struct ext_slave_config *data)
534{
535 struct lis3dh_private_data *private_data = pdata->private_data;
536 if (!data->data)
537 return INV_ERROR_INVALID_PARAMETER;
538
539 switch (data->key) {
540 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
541 (*(unsigned long *)data->data) =
542 (unsigned long)private_data->suspend.odr;
543 break;
544 case MPU_SLAVE_CONFIG_ODR_RESUME:
545 (*(unsigned long *)data->data) =
546 (unsigned long)private_data->resume.odr;
547 break;
548 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
549 (*(unsigned long *)data->data) =
550 (unsigned long)private_data->suspend.fsr;
551 break;
552 case MPU_SLAVE_CONFIG_FSR_RESUME:
553 (*(unsigned long *)data->data) =
554 (unsigned long)private_data->resume.fsr;
555 break;
556 case MPU_SLAVE_CONFIG_MOT_THS:
557 (*(unsigned long *)data->data) =
558 (unsigned long)private_data->suspend.ths;
559 break;
560 case MPU_SLAVE_CONFIG_NMOT_THS:
561 (*(unsigned long *)data->data) =
562 (unsigned long)private_data->resume.ths;
563 break;
564 case MPU_SLAVE_CONFIG_MOT_DUR:
565 (*(unsigned long *)data->data) =
566 (unsigned long)private_data->suspend.dur;
567 break;
568 case MPU_SLAVE_CONFIG_NMOT_DUR:
569 (*(unsigned long *)data->data) =
570 (unsigned long)private_data->resume.dur;
571 break;
572 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
573 (*(unsigned long *)data->data) =
574 (unsigned long)private_data->suspend.irq_type;
575 break;
576 case MPU_SLAVE_CONFIG_IRQ_RESUME:
577 (*(unsigned long *)data->data) =
578 (unsigned long)private_data->resume.irq_type;
579 break;
580 default:
581 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
582 };
583
584 return INV_SUCCESS;
585}
586
587static struct ext_slave_descr lis3dh_descr = {
588 .init = lis3dh_init,
589 .exit = lis3dh_exit,
590 .suspend = lis3dh_suspend,
591 .resume = lis3dh_resume,
592 .read = lis3dh_read,
593 .config = lis3dh_config,
594 .get_config = lis3dh_get_config,
595 .name = "lis3dh",
596 .type = EXT_SLAVE_TYPE_ACCEL,
597 .id = ACCEL_ID_LIS3DH,
598 .read_reg = 0x28 | 0x80, /* 0x80 for burst reads */
599 .read_len = 6,
600 .endian = EXT_SLAVE_BIG_ENDIAN,
601 .range = {2, 480},
602 .trigger = NULL,
603};
604
605static
606struct ext_slave_descr *lis3dh_get_slave_descr(void)
607{
608 return &lis3dh_descr;
609}
610
611/* -------------------------------------------------------------------------- */
612struct lis3dh_mod_private_data {
613 struct i2c_client *client;
614 struct ext_slave_platform_data *pdata;
615};
616
617static unsigned short normal_i2c[] = { I2C_CLIENT_END };
618
619static int lis3dh_mod_probe(struct i2c_client *client,
620 const struct i2c_device_id *devid)
621{
622 struct ext_slave_platform_data *pdata;
623 struct lis3dh_mod_private_data *private_data;
624 int result = 0;
625
626 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
627
628 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
629 result = -ENODEV;
630 goto out_no_free;
631 }
632
633 pdata = client->dev.platform_data;
634 if (!pdata) {
635 dev_err(&client->adapter->dev,
636 "Missing platform data for slave %s\n", devid->name);
637 result = -EFAULT;
638 goto out_no_free;
639 }
640
641 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
642 if (!private_data) {
643 result = -ENOMEM;
644 goto out_no_free;
645 }
646
647 i2c_set_clientdata(client, private_data);
648 private_data->client = client;
649 private_data->pdata = pdata;
650
651 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
652 lis3dh_get_slave_descr);
653 if (result) {
654 dev_err(&client->adapter->dev,
655 "Slave registration failed: %s, %d\n",
656 devid->name, result);
657 goto out_free_memory;
658 }
659
660 return result;
661
662out_free_memory:
663 kfree(private_data);
664out_no_free:
665 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
666 return result;
667
668}
669
670static int lis3dh_mod_remove(struct i2c_client *client)
671{
672 struct lis3dh_mod_private_data *private_data =
673 i2c_get_clientdata(client);
674
675 dev_dbg(&client->adapter->dev, "%s\n", __func__);
676
677 inv_mpu_unregister_slave(client, private_data->pdata,
678 lis3dh_get_slave_descr);
679
680 kfree(private_data);
681 return 0;
682}
683
684static const struct i2c_device_id lis3dh_mod_id[] = {
685 { "lis3dh", ACCEL_ID_LIS3DH },
686 {}
687};
688
689MODULE_DEVICE_TABLE(i2c, lis3dh_mod_id);
690
691static struct i2c_driver lis3dh_mod_driver = {
692 .class = I2C_CLASS_HWMON,
693 .probe = lis3dh_mod_probe,
694 .remove = lis3dh_mod_remove,
695 .id_table = lis3dh_mod_id,
696 .driver = {
697 .owner = THIS_MODULE,
698 .name = "lis3dh_mod",
699 },
700 .address_list = normal_i2c,
701};
702
703static int __init lis3dh_mod_init(void)
704{
705 int res = i2c_add_driver(&lis3dh_mod_driver);
706 pr_info("%s: Probe name %s\n", __func__, "lis3dh_mod");
707 if (res)
708 pr_err("%s failed\n", __func__);
709 return res;
710}
711
712static void __exit lis3dh_mod_exit(void)
713{
714 pr_info("%s\n", __func__);
715 i2c_del_driver(&lis3dh_mod_driver);
716}
717
718module_init(lis3dh_mod_init);
719module_exit(lis3dh_mod_exit);
720
721MODULE_AUTHOR("Invensense Corporation");
722MODULE_DESCRIPTION("Driver to integrate LIS3DH sensor with the MPU");
723MODULE_LICENSE("GPL");
724MODULE_ALIAS("lis3dh_mod");
725
726/*
727 * @}
728 */