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