/drivers/misc/inv_mpu/accel/lsm303a.c
C | 878 lines | 613 code | 86 blank | 179 comment | 75 complexity | 5acde1795161aa003b3dc749555ada95 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
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 lsm303a.c
26 * @brief Accelerometer setup and handling methods for ST LSM303.
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 <log.h>
41#include <linux/mpu.h>
42#include "mlsl.h"
43#include "mldl_cfg.h"
44#undef MPL_LOG_TAG
45#define MPL_LOG_TAG "MPL-acc"
46
47/* -------------------------------------------------------------------------- */
48
49/* full scale setting - register & mask */
50#define LIS331_CTRL_REG1 (0x20)
51#define LIS331_CTRL_REG2 (0x21)
52#define LIS331_CTRL_REG3 (0x22)
53#define LIS331_CTRL_REG4 (0x23)
54#define LIS331_CTRL_REG5 (0x24)
55#define LIS331_HP_FILTER_RESET (0x25)
56#define LIS331_REFERENCE (0x26)
57#define LIS331_STATUS_REG (0x27)
58#define LIS331_OUT_X_L (0x28)
59#define LIS331_OUT_X_H (0x29)
60#define LIS331_OUT_Y_L (0x2a)
61#define LIS331_OUT_Y_H (0x2b)
62#define LIS331_OUT_Z_L (0x2b)
63#define LIS331_OUT_Z_H (0x2d)
64
65#define LIS331_INT1_CFG (0x30)
66#define LIS331_INT1_SRC (0x31)
67#define LIS331_INT1_THS (0x32)
68#define LIS331_INT1_DURATION (0x33)
69
70#define LIS331_INT2_CFG (0x34)
71#define LIS331_INT2_SRC (0x35)
72#define LIS331_INT2_THS (0x36)
73#define LIS331_INT2_DURATION (0x37)
74
75#define LIS331_CTRL_MASK (0x30)
76#define LIS331_SLEEP_MASK (0x20)
77
78#define LIS331_MAX_DUR (0x7F)
79
80/* -------------------------------------------------------------------------- */
81
82struct lsm303dlha_config {
83 unsigned int odr;
84 unsigned int fsr; /** < full scale range mg */
85 unsigned int ths; /** < Motion no-motion thseshold mg */
86 unsigned int dur; /** < Motion no-motion duration ms */
87 unsigned char reg_ths;
88 unsigned char reg_dur;
89 unsigned char ctrl_reg1;
90 unsigned char irq_type;
91 unsigned char mot_int1_cfg;
92};
93
94struct lsm303dlha_private_data {
95 struct lsm303dlha_config suspend;
96 struct lsm303dlha_config resume;
97};
98
99/* -------------------------------------------------------------------------- */
100
101static int lsm303dlha_set_ths(void *mlsl_handle,
102 struct ext_slave_platform_data *pdata,
103 struct lsm303dlha_config *config,
104 int apply,
105 long ths)
106{
107 int result = INV_SUCCESS;
108 if ((unsigned int) ths >= config->fsr)
109 ths = (long) config->fsr - 1;
110
111 if (ths < 0)
112 ths = 0;
113
114 config->ths = ths;
115 config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
116 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
117 if (apply)
118 result = inv_serial_single_write(mlsl_handle, pdata->address,
119 LIS331_INT1_THS,
120 config->reg_ths);
121 return result;
122}
123
124static int lsm303dlha_set_dur(void *mlsl_handle,
125 struct ext_slave_platform_data *pdata,
126 struct lsm303dlha_config *config,
127 int apply,
128 long dur)
129{
130 int result = INV_SUCCESS;
131 long reg_dur = (dur * config->odr) / 1000000L;
132 config->dur = dur;
133
134 if (reg_dur > LIS331_MAX_DUR)
135 reg_dur = LIS331_MAX_DUR;
136
137 config->reg_dur = (unsigned char) reg_dur;
138 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
139 if (apply)
140 result = inv_serial_single_write(mlsl_handle, pdata->address,
141 LIS331_INT1_DURATION,
142 (unsigned char)reg_dur);
143 return result;
144}
145
146/**
147 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
148 * duration will not be used uless the type is MOT or NMOT.
149 *
150 * @param config configuration to apply to, suspend or resume
151 * @param irq_type The type of IRQ. Valid values are
152 * - MPU_SLAVE_IRQ_TYPE_NONE
153 * - MPU_SLAVE_IRQ_TYPE_MOTION
154 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
155 */
156static int lsm303dlha_set_irq(void *mlsl_handle,
157 struct ext_slave_platform_data *pdata,
158 struct lsm303dlha_config *config,
159 int apply,
160 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 LIS331_CTRL_REG3, reg1);
181 result = inv_serial_single_write(mlsl_handle, pdata->address,
182 LIS331_INT1_CFG, reg2);
183 }
184
185 return result;
186}
187
188/**
189 * @brief Set the output data rate for the particular configuration.
190 *
191 * @param mlsl_handle
192 * the handle to the serial channel the device is connected to.
193 * @param pdata
194 * a pointer to the slave platform data.
195 * @param config
196 * Config to modify with new ODR.
197 * @param apply
198 * whether to apply immediately or save the settings to be applied
199 * at the next resume.
200 * @param odr
201 * Output data rate in units of 1/1000Hz (mHz).
202 *
203 * @return INV_SUCCESS if successful or a non-zero error code.
204 */
205static int lsm303dlha_set_odr(void *mlsl_handle,
206 struct ext_slave_platform_data *pdata,
207 struct lsm303dlha_config *config,
208 int apply,
209 long odr)
210{
211 unsigned char bits;
212 int result = INV_SUCCESS;
213
214 if (odr > 400000) {
215 config->odr = 1000000;
216 bits = 0x38;
217 } else if (odr > 100000) {
218 config->odr = 400000;
219 bits = 0x30;
220 } else if (odr > 50000) {
221 config->odr = 100000;
222 bits = 0x28;
223 } else if (odr > 10000) {
224 config->odr = 50000;
225 bits = 0x20;
226 } else if (odr > 5000) {
227 config->odr = 10000;
228 bits = 0xC0;
229 } else if (odr > 2000) {
230 config->odr = 5000;
231 bits = 0xB0;
232 } else if (odr > 1000) {
233 config->odr = 2000;
234 bits = 0x80;
235 } else if (odr > 500) {
236 config->odr = 1000;
237 bits = 0x60;
238 } else if (odr > 0) {
239 config->odr = 500;
240 bits = 0x40;
241 } else {
242 config->odr = 0;
243 bits = 0;
244 }
245
246 config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7);
247 lsm303dlha_set_dur(mlsl_handle, pdata,
248 config, apply, config->dur);
249 MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
250 if (apply)
251 result = inv_serial_single_write(mlsl_handle, pdata->address,
252 LIS331_CTRL_REG1,
253 config->ctrl_reg1);
254 return result;
255}
256
257/**
258 * @brief Set the full scale range of the accels
259 *
260 * @param mlsl_handle
261 * the handle to the serial channel the device is connected to.
262 * @param pdata
263 * a pointer to the slave platform data.
264 * @param config
265 * pointer to configuration.
266 * @param apply
267 * whether to apply immediately or save the settings to be applied
268 * at the next resume.
269 * @param fsr
270 * requested full scale range.
271 *
272 * @return INV_SUCCESS if successful or a non-zero error code.
273 */
274static int lsm303dlha_set_fsr(void *mlsl_handle,
275 struct ext_slave_platform_data *pdata,
276 struct lsm303dlha_config *config,
277 int apply,
278 long fsr)
279{
280 unsigned char reg1 = 0x40;
281 int result = INV_SUCCESS;
282
283 if (fsr <= 2048) {
284 config->fsr = 2048;
285 } else if (fsr <= 4096) {
286 reg1 |= 0x30;
287 config->fsr = 4096;
288 } else {
289 reg1 |= 0x10;
290 config->fsr = 8192;
291 }
292
293 lsm303dlha_set_ths(mlsl_handle, pdata,
294 config, apply, config->ths);
295 MPL_LOGV("FSR: %d\n", config->fsr);
296 if (apply)
297 result = inv_serial_single_write(mlsl_handle, pdata->address,
298 LIS331_CTRL_REG4, reg1);
299
300 return result;
301}
302
303/**
304 * @brief suspends the device to put it in its lowest power mode.
305 *
306 * @param mlsl_handle
307 * the handle to the serial channel the device is connected to.
308 * @param slave
309 * a pointer to the slave descriptor data structure.
310 * @param pdata
311 * a pointer to the slave platform data.
312 *
313 * @return INV_SUCCESS if successful or a non-zero error code.
314 */
315static int lsm303dlha_suspend(void *mlsl_handle,
316 struct ext_slave_descr *slave,
317 struct ext_slave_platform_data *pdata)
318{
319 int result = INV_SUCCESS;
320 unsigned char reg1;
321 unsigned char reg2;
322 struct lsm303dlha_private_data *private_data =
323 (struct lsm303dlha_private_data *)(pdata->private_data);
324
325 result = inv_serial_single_write(mlsl_handle, pdata->address,
326 LIS331_CTRL_REG1,
327 private_data->suspend.ctrl_reg1);
328
329 result = inv_serial_single_write(mlsl_handle, pdata->address,
330 LIS331_CTRL_REG2, 0x0f);
331 reg1 = 0x40;
332 if (private_data->suspend.fsr == 8192)
333 reg1 |= 0x30;
334 else if (private_data->suspend.fsr == 4096)
335 reg1 |= 0x10;
336 /* else bits [4..5] are already zero */
337
338 result = inv_serial_single_write(mlsl_handle, pdata->address,
339 LIS331_CTRL_REG4, reg1);
340 result = inv_serial_single_write(mlsl_handle, pdata->address,
341 LIS331_INT1_THS,
342 private_data->suspend.reg_ths);
343 result = inv_serial_single_write(mlsl_handle, pdata->address,
344 LIS331_INT1_DURATION,
345 private_data->suspend.reg_dur);
346
347 if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
348 reg1 = 0x02;
349 reg2 = 0x00;
350 } else if (private_data->suspend.irq_type ==
351 MPU_SLAVE_IRQ_TYPE_MOTION) {
352 reg1 = 0x00;
353 reg2 = private_data->suspend.mot_int1_cfg;
354 } else {
355 reg1 = 0x00;
356 reg2 = 0x00;
357 }
358 result = inv_serial_single_write(mlsl_handle, pdata->address,
359 LIS331_CTRL_REG3, reg1);
360 result = inv_serial_single_write(mlsl_handle, pdata->address,
361 LIS331_INT1_CFG, reg2);
362 result = inv_serial_read(mlsl_handle, pdata->address,
363 LIS331_HP_FILTER_RESET, 1, ®1);
364 return result;
365}
366
367/**
368 * @brief resume the device in the proper power state given the configuration
369 * chosen.
370 *
371 * @param mlsl_handle
372 * the handle to the serial channel the device is connected to.
373 * @param slave
374 * a pointer to the slave descriptor data structure.
375 * @param pdata
376 * a pointer to the slave platform data.
377 *
378 * @return INV_SUCCESS if successful or a non-zero error code.
379 */
380static int lsm303dlha_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 reg1;
386 unsigned char reg2;
387 struct lsm303dlha_private_data *private_data =
388 (struct lsm303dlha_private_data *)(pdata->private_data);
389
390
391 result = inv_serial_single_write(mlsl_handle, pdata->address,
392 LIS331_CTRL_REG1,
393 private_data->resume.ctrl_reg1);
394 if (result) {
395 LOG_RESULT_LOCATION(result);
396 return result;
397 }
398 msleep(6);
399
400 /* Full Scale */
401 reg1 = 0x40;
402 if (private_data->resume.fsr == 8192)
403 reg1 |= 0x30;
404 else if (private_data->resume.fsr == 4096)
405 reg1 |= 0x10;
406
407 result = inv_serial_single_write(mlsl_handle, pdata->address,
408 LIS331_CTRL_REG4, reg1);
409 if (result) {
410 LOG_RESULT_LOCATION(result);
411 return result;
412 }
413
414 /* Configure high pass filter */
415 result = inv_serial_single_write(mlsl_handle, pdata->address,
416 LIS331_CTRL_REG2, 0x0F);
417 if (result) {
418 LOG_RESULT_LOCATION(result);
419 return result;
420 }
421
422 if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
423 reg1 = 0x02;
424 reg2 = 0x00;
425 } else if (private_data->resume.irq_type ==
426 MPU_SLAVE_IRQ_TYPE_MOTION) {
427 reg1 = 0x00;
428 reg2 = private_data->resume.mot_int1_cfg;
429 } else {
430 reg1 = 0x00;
431 reg2 = 0x00;
432 }
433 result = inv_serial_single_write(mlsl_handle, pdata->address,
434 LIS331_CTRL_REG3, reg1);
435 if (result) {
436 LOG_RESULT_LOCATION(result);
437 return result;
438 }
439 result = inv_serial_single_write(mlsl_handle, pdata->address,
440 LIS331_INT1_THS,
441 private_data->resume.reg_ths);
442 if (result) {
443 LOG_RESULT_LOCATION(result);
444 return result;
445 }
446 result = inv_serial_single_write(mlsl_handle, pdata->address,
447 LIS331_INT1_DURATION,
448 private_data->resume.reg_dur);
449 if (result) {
450 LOG_RESULT_LOCATION(result);
451 return result;
452 }
453 result = inv_serial_single_write(mlsl_handle, pdata->address,
454 LIS331_INT1_CFG, reg2);
455 if (result) {
456 LOG_RESULT_LOCATION(result);
457 return result;
458 }
459 result = inv_serial_read(mlsl_handle, pdata->address,
460 LIS331_HP_FILTER_RESET, 1, ®1);
461 if (result) {
462 LOG_RESULT_LOCATION(result);
463 return result;
464 }
465 return result;
466}
467
468/**
469 * @brief read the sensor data from the device.
470 *
471 * @param mlsl_handle
472 * the handle to the serial channel the device is connected to.
473 * @param slave
474 * a pointer to the slave descriptor data structure.
475 * @param pdata
476 * a pointer to the slave platform data.
477 * @param data
478 * a buffer to store the data read.
479 *
480 * @return INV_SUCCESS if successful or a non-zero error code.
481 */
482static int lsm303dlha_read(void *mlsl_handle,
483 struct ext_slave_descr *slave,
484 struct ext_slave_platform_data *pdata,
485 unsigned char *data)
486{
487 int result = INV_SUCCESS;
488 result = inv_serial_read(mlsl_handle, pdata->address,
489 LIS331_STATUS_REG, 1, data);
490 if (data[0] & 0x0F) {
491 result = inv_serial_read(mlsl_handle, pdata->address,
492 slave->read_reg, slave->read_len, data);
493 return result;
494 } else
495 return INV_ERROR_ACCEL_DATA_NOT_READY;
496}
497
498/**
499 * @brief one-time device driver initialization function.
500 * If the driver is built as a kernel module, this function will be
501 * called when the module is loaded in the kernel.
502 * If the driver is built-in in the kernel, this function will be
503 * called at boot time.
504 *
505 * @param mlsl_handle
506 * the handle to the serial channel the device is connected to.
507 * @param slave
508 * a pointer to the slave descriptor data structure.
509 * @param pdata
510 * a pointer to the slave platform data.
511 *
512 * @return INV_SUCCESS if successful or a non-zero error code.
513 */
514static int lsm303dlha_init(void *mlsl_handle,
515 struct ext_slave_descr *slave,
516 struct ext_slave_platform_data *pdata)
517{
518 long range;
519 struct lsm303dlha_private_data *private_data;
520 private_data = (struct lsm303dlha_private_data *)
521 kzalloc(sizeof(struct lsm303dlha_private_data), GFP_KERNEL);
522
523 if (!private_data)
524 return INV_ERROR_MEMORY_EXAUSTED;
525
526 pdata->private_data = private_data;
527
528 private_data->resume.ctrl_reg1 = 0x37;
529 private_data->suspend.ctrl_reg1 = 0x47;
530 private_data->resume.mot_int1_cfg = 0x95;
531 private_data->suspend.mot_int1_cfg = 0x2a;
532
533 lsm303dlha_set_odr(mlsl_handle, pdata, &private_data->suspend,
534 FALSE, 0);
535 lsm303dlha_set_odr(mlsl_handle, pdata, &private_data->resume,
536 FALSE, 200000);
537
538 range = range_fixedpoint_to_long_mg(slave->range);
539 lsm303dlha_set_fsr(mlsl_handle, pdata, &private_data->suspend,
540 FALSE, range);
541 lsm303dlha_set_fsr(mlsl_handle, pdata, &private_data->resume,
542 FALSE, range);
543
544 lsm303dlha_set_ths(mlsl_handle, pdata, &private_data->suspend,
545 FALSE, 80);
546 lsm303dlha_set_ths(mlsl_handle, pdata, &private_data->resume,
547 FALSE, 40);
548
549 lsm303dlha_set_dur(mlsl_handle, pdata, &private_data->suspend,
550 FALSE, 1000);
551 lsm303dlha_set_dur(mlsl_handle, pdata, &private_data->resume,
552 FALSE, 2540);
553
554 lsm303dlha_set_irq(mlsl_handle, pdata, &private_data->suspend,
555 FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
556 lsm303dlha_set_irq(mlsl_handle, pdata, &private_data->resume,
557 FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
558 return INV_SUCCESS;
559}
560
561/**
562 * @brief one-time device driver exit function.
563 * If the driver is built as a kernel module, this function will be
564 * called when the module is removed from the kernel.
565 *
566 * @param mlsl_handle
567 * the handle to the serial channel the device is connected to.
568 * @param slave
569 * a pointer to the slave descriptor data structure.
570 * @param pdata
571 * a pointer to the slave platform data.
572 *
573 * @return INV_SUCCESS if successful or a non-zero error code.
574 */
575static int lsm303dlha_exit(void *mlsl_handle,
576 struct ext_slave_descr *slave,
577 struct ext_slave_platform_data *pdata)
578{
579 kfree(pdata->private_data);
580 return INV_SUCCESS;
581}
582
583/**
584 * @brief device configuration facility.
585 *
586 * @param mlsl_handle
587 * the handle to the serial channel the device is connected to.
588 * @param slave
589 * a pointer to the slave descriptor data structure.
590 * @param pdata
591 * a pointer to the slave platform data.
592 * @param data
593 * a pointer to the configuration data structure.
594 *
595 * @return INV_SUCCESS if successful or a non-zero error code.
596 */
597static int lsm303dlha_config(void *mlsl_handle,
598 struct ext_slave_descr *slave,
599 struct ext_slave_platform_data *pdata,
600 struct ext_slave_config *data)
601{
602 struct lsm303dlha_private_data *private_data = pdata->private_data;
603 if (!data->data)
604 return INV_ERROR_INVALID_PARAMETER;
605
606 switch (data->key) {
607 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
608 return lsm303dlha_set_odr(mlsl_handle, pdata,
609 &private_data->suspend,
610 data->apply,
611 *((long *)data->data));
612 case MPU_SLAVE_CONFIG_ODR_RESUME:
613 return lsm303dlha_set_odr(mlsl_handle, pdata,
614 &private_data->resume,
615 data->apply,
616 *((long *)data->data));
617 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
618 return lsm303dlha_set_fsr(mlsl_handle, pdata,
619 &private_data->suspend,
620 data->apply,
621 *((long *)data->data));
622 case MPU_SLAVE_CONFIG_FSR_RESUME:
623 return lsm303dlha_set_fsr(mlsl_handle, pdata,
624 &private_data->resume,
625 data->apply,
626 *((long *)data->data));
627 case MPU_SLAVE_CONFIG_MOT_THS:
628 return lsm303dlha_set_ths(mlsl_handle, pdata,
629 &private_data->suspend,
630 data->apply,
631 *((long *)data->data));
632 case MPU_SLAVE_CONFIG_NMOT_THS:
633 return lsm303dlha_set_ths(mlsl_handle, pdata,
634 &private_data->resume,
635 data->apply,
636 *((long *)data->data));
637 case MPU_SLAVE_CONFIG_MOT_DUR:
638 return lsm303dlha_set_dur(mlsl_handle, pdata,
639 &private_data->suspend,
640 data->apply,
641 *((long *)data->data));
642 case MPU_SLAVE_CONFIG_NMOT_DUR:
643 return lsm303dlha_set_dur(mlsl_handle, pdata,
644 &private_data->resume,
645 data->apply,
646 *((long *)data->data));
647 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
648 return lsm303dlha_set_irq(mlsl_handle, pdata,
649 &private_data->suspend,
650 data->apply,
651 *((long *)data->data));
652 case MPU_SLAVE_CONFIG_IRQ_RESUME:
653 return lsm303dlha_set_irq(mlsl_handle, pdata,
654 &private_data->resume,
655 data->apply,
656 *((long *)data->data));
657 default:
658 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
659 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
660 };
661
662 return INV_SUCCESS;
663}
664
665/**
666 * @brief facility to retrieve the device configuration.
667 *
668 * @param mlsl_handle
669 * the handle to the serial channel the device is connected to.
670 * @param slave
671 * a pointer to the slave descriptor data structure.
672 * @param pdata
673 * a pointer to the slave platform data.
674 * @param data
675 * a pointer to store the returned configuration data structure.
676 *
677 * @return INV_SUCCESS if successful or a non-zero error code.
678 */
679static int lsm303dlha_get_config(void *mlsl_handle,
680 struct ext_slave_descr *slave,
681 struct ext_slave_platform_data *pdata,
682 struct ext_slave_config *data)
683{
684 struct lsm303dlha_private_data *private_data = pdata->private_data;
685 if (!data->data)
686 return INV_ERROR_INVALID_PARAMETER;
687
688 switch (data->key) {
689 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
690 (*(unsigned long *)data->data) =
691 (unsigned long) private_data->suspend.odr;
692 break;
693 case MPU_SLAVE_CONFIG_ODR_RESUME:
694 (*(unsigned long *)data->data) =
695 (unsigned long) private_data->resume.odr;
696 break;
697 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
698 (*(unsigned long *)data->data) =
699 (unsigned long) private_data->suspend.fsr;
700 break;
701 case MPU_SLAVE_CONFIG_FSR_RESUME:
702 (*(unsigned long *)data->data) =
703 (unsigned long) private_data->resume.fsr;
704 break;
705 case MPU_SLAVE_CONFIG_MOT_THS:
706 (*(unsigned long *)data->data) =
707 (unsigned long) private_data->suspend.ths;
708 break;
709 case MPU_SLAVE_CONFIG_NMOT_THS:
710 (*(unsigned long *)data->data) =
711 (unsigned long) private_data->resume.ths;
712 break;
713 case MPU_SLAVE_CONFIG_MOT_DUR:
714 (*(unsigned long *)data->data) =
715 (unsigned long) private_data->suspend.dur;
716 break;
717 case MPU_SLAVE_CONFIG_NMOT_DUR:
718 (*(unsigned long *)data->data) =
719 (unsigned long) private_data->resume.dur;
720 break;
721 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
722 (*(unsigned long *)data->data) =
723 (unsigned long) private_data->suspend.irq_type;
724 break;
725 case MPU_SLAVE_CONFIG_IRQ_RESUME:
726 (*(unsigned long *)data->data) =
727 (unsigned long) private_data->resume.irq_type;
728 break;
729 default:
730 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
731 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
732 };
733
734 return INV_SUCCESS;
735}
736
737static struct ext_slave_descr lsm303dlha_descr = {
738 .init = lsm303dlha_init,
739 .exit = lsm303dlha_exit,
740 .suspend = lsm303dlha_suspend,
741 .resume = lsm303dlha_resume,
742 .read = lsm303dlha_read,
743 .config = lsm303dlha_config,
744 .get_config = lsm303dlha_get_config,
745 .name = "lsm303dlha",
746 .type = EXT_SLAVE_TYPE_ACCELEROMETER,
747 .id = ACCEL_ID_LSM303A,
748 .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */
749 .read_len = 6,
750 .endian = EXT_SLAVE_BIG_ENDIAN,
751 .range = {2, 480},
752 .trigger = NULL,
753};
754
755static
756struct ext_slave_descr *lsm303a_get_slave_descr(void)
757{
758 return &lsm303dlha_descr;
759}
760
761/* -------------------------------------------------------------------------- */
762struct lsm303a_mod_private_data {
763 struct i2c_client *client;
764 struct ext_slave_platform_data *pdata;
765};
766
767static unsigned short normal_i2c[] = { I2C_CLIENT_END };
768
769static int lsm303a_mod_probe(struct i2c_client *client,
770 const struct i2c_device_id *devid)
771{
772 struct ext_slave_platform_data *pdata;
773 struct lsm303a_mod_private_data *private_data;
774 int result = 0;
775
776 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
777
778 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
779 result = -ENODEV;
780 goto out_no_free;
781 }
782
783 pdata = client->dev.platform_data;
784 if (!pdata) {
785 dev_err(&client->adapter->dev,
786 "Missing platform data for slave %s\n", devid->name);
787 result = -EFAULT;
788 goto out_no_free;
789 }
790
791 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
792 if (!private_data) {
793 result = -ENOMEM;
794 goto out_no_free;
795 }
796
797 i2c_set_clientdata(client, private_data);
798 private_data->client = client;
799 private_data->pdata = pdata;
800
801 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
802 lsm303a_get_slave_descr);
803 if (result) {
804 dev_err(&client->adapter->dev,
805 "Slave registration failed: %s, %d\n",
806 devid->name, result);
807 goto out_free_memory;
808 }
809
810 return result;
811
812out_free_memory:
813 kfree(private_data);
814out_no_free:
815 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
816 return result;
817
818}
819
820static int lsm303a_mod_remove(struct i2c_client *client)
821{
822 struct lsm303a_mod_private_data *private_data =
823 i2c_get_clientdata(client);
824
825 dev_dbg(&client->adapter->dev, "%s\n", __func__);
826
827 inv_mpu_unregister_slave(client, private_data->pdata,
828 lsm303a_get_slave_descr);
829
830 kfree(private_data);
831 return 0;
832}
833
834static const struct i2c_device_id lsm303a_mod_id[] = {
835 { "lsm303a", ACCEL_ID_LSM303A },
836 {}
837};
838
839MODULE_DEVICE_TABLE(i2c, lsm303a_mod_id);
840
841static struct i2c_driver lsm303a_mod_driver = {
842 .class = I2C_CLASS_HWMON,
843 .probe = lsm303a_mod_probe,
844 .remove = lsm303a_mod_remove,
845 .id_table = lsm303a_mod_id,
846 .driver = {
847 .owner = THIS_MODULE,
848 .name = "lsm303a_mod",
849 },
850 .address_list = normal_i2c,
851};
852
853static int __init lsm303a_mod_init(void)
854{
855 int res = i2c_add_driver(&lsm303a_mod_driver);
856 pr_info("%s: Probe name %s\n", __func__, "lsm303a_mod");
857 if (res)
858 pr_err("%s failed\n", __func__);
859 return res;
860}
861
862static void __exit lsm303a_mod_exit(void)
863{
864 pr_info("%s\n", __func__);
865 i2c_del_driver(&lsm303a_mod_driver);
866}
867
868module_init(lsm303a_mod_init);
869module_exit(lsm303a_mod_exit);
870
871MODULE_AUTHOR("Invensense Corporation");
872MODULE_DESCRIPTION("Driver to integrate LSM303A sensor with the MPU");
873MODULE_LICENSE("GPL");
874MODULE_ALIAS("lsm303a_mod");
875
876/**
877 * @}
878 */