PageRenderTime 56ms CodeModel.GetById 5ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/misc/inv_mpu/accel/lsm303a.c

https://bitbucket.org/slukk/jb-tsm-kernel-4.2
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, &reg1);
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, &reg1);
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 */