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