aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu
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
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu')
-rw-r--r--drivers/misc/inv_mpu/Kconfig70
-rw-r--r--drivers/misc/inv_mpu/Makefile18
-rw-r--r--drivers/misc/inv_mpu/accel/Kconfig133
-rw-r--r--drivers/misc/inv_mpu/accel/Makefile38
-rw-r--r--drivers/misc/inv_mpu/accel/adxl34x.c728
-rw-r--r--drivers/misc/inv_mpu/accel/bma150.c777
-rw-r--r--drivers/misc/inv_mpu/accel/bma222.c654
-rw-r--r--drivers/misc/inv_mpu/accel/bma250.c787
-rw-r--r--drivers/misc/inv_mpu/accel/cma3000.c222
-rw-r--r--drivers/misc/inv_mpu/accel/kxsd9.c264
-rw-r--r--drivers/misc/inv_mpu/accel/kxtf9.c841
-rw-r--r--drivers/misc/inv_mpu/accel/lis331.c745
-rw-r--r--drivers/misc/inv_mpu/accel/lis3dh.c728
-rw-r--r--drivers/misc/inv_mpu/accel/lsm303dlx_a.c881
-rw-r--r--drivers/misc/inv_mpu/accel/mma8450.c804
-rw-r--r--drivers/misc/inv_mpu/accel/mma845x.c713
-rw-r--r--drivers/misc/inv_mpu/accel/mpu6050.c695
-rw-r--r--drivers/misc/inv_mpu/accel/mpu6050.h28
-rw-r--r--drivers/misc/inv_mpu/compass/Kconfig130
-rw-r--r--drivers/misc/inv_mpu/compass/Makefile41
-rw-r--r--drivers/misc/inv_mpu/compass/ak8972.c499
-rw-r--r--drivers/misc/inv_mpu/compass/ak8975.c500
-rw-r--r--drivers/misc/inv_mpu/compass/ak89xx.c522
-rw-r--r--drivers/misc/inv_mpu/compass/ami306.c1020
-rw-r--r--drivers/misc/inv_mpu/compass/ami30x.c308
-rw-r--r--drivers/misc/inv_mpu/compass/ami_hw.h87
-rw-r--r--drivers/misc/inv_mpu/compass/ami_sensor_def.h144
-rw-r--r--drivers/misc/inv_mpu/compass/hmc5883.c391
-rw-r--r--drivers/misc/inv_mpu/compass/hscdtd002b.c294
-rw-r--r--drivers/misc/inv_mpu/compass/hscdtd004a.c318
-rw-r--r--drivers/misc/inv_mpu/compass/lsm303dlx_m.c395
-rw-r--r--drivers/misc/inv_mpu/compass/mmc314x.c313
-rw-r--r--drivers/misc/inv_mpu/compass/yas529-kernel.c611
-rw-r--r--drivers/misc/inv_mpu/compass/yas530.c580
-rw-r--r--drivers/misc/inv_mpu/log.h287
-rw-r--r--drivers/misc/inv_mpu/mldl_cfg.h384
-rw-r--r--drivers/misc/inv_mpu/mldl_print_cfg.h38
-rw-r--r--drivers/misc/inv_mpu/mlsl.h186
-rw-r--r--drivers/misc/inv_mpu/mltypes.h234
-rw-r--r--drivers/misc/inv_mpu/mpu-dev.h36
-rw-r--r--drivers/misc/inv_mpu/mpu3050.h251
-rw-r--r--drivers/misc/inv_mpu/mpu3050/Makefile17
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mldl_cfg.c1765
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c137
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c420
-rw-r--r--drivers/misc/inv_mpu/mpu3050/mpu-dev.c1250
-rw-r--r--drivers/misc/inv_mpu/mpu6050/Makefile18
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mldl_cfg.c1916
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c138
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c420
-rw-r--r--drivers/misc/inv_mpu/mpu6050/mpu-dev.c1309
-rw-r--r--drivers/misc/inv_mpu/mpu6050b1.h435
-rw-r--r--drivers/misc/inv_mpu/mpuirq.c254
-rw-r--r--drivers/misc/inv_mpu/mpuirq.h37
-rw-r--r--drivers/misc/inv_mpu/pressure/Kconfig20
-rw-r--r--drivers/misc/inv_mpu/pressure/Makefile8
-rw-r--r--drivers/misc/inv_mpu/pressure/bma085.c367
-rw-r--r--drivers/misc/inv_mpu/slaveirq.c268
-rw-r--r--drivers/misc/inv_mpu/slaveirq.h36
-rw-r--r--drivers/misc/inv_mpu/timerirq.c296
-rw-r--r--drivers/misc/inv_mpu/timerirq.h30
61 files changed, 25836 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig
new file mode 100644
index 00000000000..4c231b576f6
--- /dev/null
+++ b/drivers/misc/inv_mpu/Kconfig
@@ -0,0 +1,70 @@
1config MPU_SENSORS_TIMERIRQ
2 tristate "MPU Timer IRQ"
3 help
4 If you say yes here you get access to the timerirq device handle which
5 can be used to select on. This can be used instead of IRQ's, sleeping,
6 or timer threads. Reading from this device returns the same type of
7 information as reading from the MPU and slave IRQ's.
8
9menuconfig INV_SENSORS
10 tristate "Motion Processing Unit"
11 depends on I2C
12 default y
13
14if INV_SENSORS
15
16choice
17 tristate "MPU Master"
18 depends on I2C && INV_SENSORS
19 default MPU_SENSORS_MPU3050
20
21config MPU_SENSORS_MPU3050
22 tristate "MPU3050"
23 depends on I2C
24 select MPU_SENSORS_MPU3050_GYRO
25 help
26 If you say yes here you get support for the MPU3050 Gyroscope driver
27 This driver can also be built as a module. If so, the module
28 will be called mpu3050.
29
30config MPU_SENSORS_MPU6050A2
31 tristate "MPU6050A2"
32 depends on I2C
33 select MPU_SENSORS_MPU6050_GYRO
34 help
35 If you say yes here you get support for the MPU6050A2 Gyroscope driver
36 This driver can also be built as a module. If so, the module
37 will be called mpu6050a2.
38
39config MPU_SENSORS_MPU6050B1
40 tristate "MPU6050B1"
41 depends on I2C
42 select MPU_SENSORS_MPU6050_GYRO
43 help
44 If you say yes here you get support for the MPU6050 Gyroscope driver
45 This driver can also be built as a module. If so, the module
46 will be called mpu6050b1.
47
48endchoice
49
50config MPU_SENSORS_MPU3050_GYRO
51 bool "MPU3050 built in gyroscope"
52 depends on MPU_SENSORS_MPU3050
53
54config MPU_SENSORS_MPU6050_GYRO
55 bool "MPU6050 built in gyroscope"
56 depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
57
58source "drivers/misc/inv_mpu/accel/Kconfig"
59source "drivers/misc/inv_mpu/compass/Kconfig"
60source "drivers/misc/inv_mpu/pressure/Kconfig"
61
62config MPU_USERSPACE_DEBUG
63 bool "MPU Userspace debugging ioctls"
64 help
65 Allows the ioctls MPU_SET_MPU_PLATFORM_DATA and
66 MPU_SET_EXT_SLAVE_PLATFORM_DATA, which allows userspace applications
67 to override the slave address and orientation. This is dangerous
68 and could cause the devices not to work.
69
70endif #INV_SENSORS
diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile
new file mode 100644
index 00000000000..f10c6844a08
--- /dev/null
+++ b/drivers/misc/inv_mpu/Makefile
@@ -0,0 +1,18 @@
1
2# Kernel makefile for motions sensors
3#
4#
5
6EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
7EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
8EXTRA_CFLAGS += -DINV_CACHE_DMP=1
9
10obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o
11obj-$(CONFIG_INV_SENSORS) += mpuirq.o slaveirq.o
12
13obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050/
14obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050/
15
16obj-y += accel/
17obj-y += compass/
18obj-y += pressure/
diff --git a/drivers/misc/inv_mpu/accel/Kconfig b/drivers/misc/inv_mpu/accel/Kconfig
new file mode 100644
index 00000000000..4e280bd876b
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/Kconfig
@@ -0,0 +1,133 @@
1menuconfig INV_SENSORS_ACCELEROMETERS
2 bool "Accelerometer Slave Sensors"
3 default y
4 ---help---
5 Say Y here to get to see options for device drivers for various
6 accelerometrs for integration with the MPU3050 or MPU6050 driver.
7 This option alone does not add any kernel code.
8
9 If you say N, all options in this submenu will be skipped and disabled.
10
11if INV_SENSORS_ACCELEROMETERS
12
13config MPU_SENSORS_ADXL34X
14 tristate "ADI adxl34x"
15 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
16 help
17 This enables support for the ADI adxl345 or adxl346 accelerometers.
18 This support is for integration with the MPU3050 gyroscope device
19 driver. Only one accelerometer can be registered at a time.
20 Specifying more that one accelerometer in the board file will result
21 in runtime errors.
22
23config MPU_SENSORS_BMA222
24 tristate "Bosch BMA222"
25 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
26 help
27 This enables support for the Bosch BMA222 accelerometer
28 This support is for integration with the MPU3050 gyroscope device
29 driver. Only one accelerometer can be registered at a time.
30 Specifying more that one accelerometer in the board file will result
31 in runtime errors.
32
33config MPU_SENSORS_BMA150
34 tristate "Bosch BMA150"
35 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
36 help
37 This enables support for the Bosch BMA150 accelerometer
38 This support is for integration with the MPU3050 gyroscope device
39 driver. Only one accelerometer can be registered at a time.
40 Specifying more that one accelerometer in the board file will result
41 in runtime errors.
42
43config MPU_SENSORS_BMA250
44 tristate "Bosch BMA250"
45 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
46 help
47 This enables support for the Bosch BMA250 accelerometer
48 This support is for integration with the MPU3050 gyroscope device
49 driver. Only one accelerometer can be registered at a time.
50 Specifying more that one accelerometer in the board file will result
51 in runtime errors.
52
53config MPU_SENSORS_KXSD9
54 tristate "Kionix KXSD9"
55 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
56 help
57 This enables support for the Kionix KXSD9 accelerometer
58 This support is for integration with the MPU3050 gyroscope device
59 driver. Only one accelerometer can be registered at a time.
60 Specifying more that one accelerometer in the board file will result
61 in runtime errors.
62
63config MPU_SENSORS_KXTF9
64 tristate "Kionix KXTF9"
65 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
66 help
67 This enables support for the Kionix KXFT9 accelerometer
68 This support is for integration with the MPU3050 gyroscope device
69 driver. Only one accelerometer can be registered at a time.
70 Specifying more that one accelerometer in the board file will result
71 in runtime errors.
72
73config MPU_SENSORS_LIS331DLH
74 tristate "ST lis331dlh"
75 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
76 help
77 This enables support for the ST lis331dlh accelerometer
78 This support is for integration with the MPU3050 gyroscope device
79 driver. Only one accelerometer can be registered at a time.
80 Specifying more that one accelerometer in the board file will result
81 in runtime errors.
82
83config MPU_SENSORS_LIS3DH
84 tristate "ST lis3dh"
85 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
86 help
87 This enables support for the ST lis3dh accelerometer
88 This support is for integration with the MPU3050 gyroscope device
89 driver. Only one accelerometer can be registered at a time.
90 Specifying more that one accelerometer in the board file will result
91 in runtime errors.
92
93config MPU_SENSORS_LSM303DLX_A
94 tristate "ST lsm303dlx"
95 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
96 help
97 This enables support for the ST lsm303dlh and lsm303dlm accelerometers
98 This support is for integration with the MPU3050 gyroscope device
99 driver. Only one accelerometer can be registered at a time.
100 Specifying more that one accelerometer in the board file will result
101 in runtime errors.
102
103config MPU_SENSORS_MMA8450
104 tristate "Freescale mma8450"
105 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
106 help
107 This enables support for the Freescale mma8450 accelerometer
108 This support is for integration with the MPU3050 gyroscope device
109 driver. Only one accelerometer can be registered at a time.
110 Specifying more that one accelerometer in the board file will result
111 in runtime errors.
112
113config MPU_SENSORS_MMA845X
114 tristate "Freescale mma8451/8452/8453"
115 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
116 help
117 This enables support for the Freescale mma8451/8452/8453 accelerometer
118 This support is for integration with the MPU3050 gyroscope device
119 driver. Only one accelerometer can be registered at a time.
120 Specifying more that one accelerometer in the board file will result
121 in runtime errors.
122
123config MPU_SENSORS_MPU6050_ACCEL
124 tristate "MPU6050 built in accelerometer"
125 depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
126 help
127 This enables support for the MPU6050 built in accelerometer.
128 This the built in support for integration with the MPU6050 gyroscope
129 device driver. This is the only accelerometer supported with the
130 MPU6050. Specifying another accelerometer in the board file will
131 result in runtime errors.
132
133endif
diff --git a/drivers/misc/inv_mpu/accel/Makefile b/drivers/misc/inv_mpu/accel/Makefile
new file mode 100644
index 00000000000..1f0f5bec677
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/Makefile
@@ -0,0 +1,38 @@
1#
2# Accel Slaves to MPUxxxx
3#
4obj-$(CONFIG_MPU_SENSORS_ADXL34X) += inv_mpu_adxl34x.o
5inv_mpu_adxl34x-objs += adxl34x.o
6
7obj-$(CONFIG_MPU_SENSORS_BMA150) += inv_mpu_bma150.o
8inv_mpu_bma150-objs += bma150.o
9
10obj-$(CONFIG_MPU_SENSORS_KXTF9) += inv_mpu_kxtf9.o
11inv_mpu_kxtf9-objs += kxtf9.o
12
13obj-$(CONFIG_MPU_SENSORS_BMA222) += inv_mpu_bma222.o
14inv_mpu_bma222-objs += bma222.o
15
16obj-$(CONFIG_MPU_SENSORS_BMA250) += inv_mpu_bma250.o
17inv_mpu_bma250-objs += bma250.o
18
19obj-$(CONFIG_MPU_SENSORS_KXSD9) += inv_mpu_kxsd9.o
20inv_mpu_kxsd9-objs += kxsd9.o
21
22obj-$(CONFIG_MPU_SENSORS_LIS331DLH) += inv_mpu_lis331.o
23inv_mpu_lis331-objs += lis331.o
24
25obj-$(CONFIG_MPU_SENSORS_LIS3DH) += inv_mpu_lis3dh.o
26inv_mpu_lis3dh-objs += lis3dh.o
27
28obj-$(CONFIG_MPU_SENSORS_LSM303DLX_A) += inv_mpu_lsm303dlx_a.o
29inv_mpu_lsm303dlx_a-objs += lsm303dlx_a.o
30
31obj-$(CONFIG_MPU_SENSORS_MMA8450) += inv_mpu_mma8450.o
32inv_mpu_mma8450-objs += mma8450.o
33
34obj-$(CONFIG_MPU_SENSORS_MMA845X) += inv_mpu_mma845x.o
35inv_mpu_mma845x-objs += mma845x.o
36
37EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
38EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
diff --git a/drivers/misc/inv_mpu/accel/adxl34x.c b/drivers/misc/inv_mpu/accel/adxl34x.c
new file mode 100644
index 00000000000..f2bff8a7592
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/adxl34x.c
@@ -0,0 +1,728 @@
1/*
2 $License:
3 Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 $
18 */
19
20/**
21 * @addtogroup ACCELDL
22 * @brief Provides the interface to setup and handle an accelerometer.
23 *
24 * @{
25 * @file adxl34x.c
26 * @brief Accelerometer setup and handling methods for AD adxl345 and
27 * adxl346.
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
43#include <linux/mpu.h>
44#include "mlsl.h"
45#include "mldl_cfg.h"
46
47#undef MPL_LOG_TAG
48#define MPL_LOG_TAG "MPL-acc"
49
50/* -------------------------------------------------------------------------- */
51
52/* registers */
53#define ADXL34X_ODR_REG (0x2C)
54#define ADXL34X_PWR_REG (0x2D)
55#define ADXL34X_DATAFORMAT_REG (0x31)
56
57/* masks */
58#define ADXL34X_ODR_MASK (0x0F)
59#define ADXL34X_PWR_SLEEP_MASK (0x04)
60#define ADXL34X_PWR_MEAS_MASK (0x08)
61#define ADXL34X_DATAFORMAT_JUSTIFY_MASK (0x04)
62#define ADXL34X_DATAFORMAT_FSR_MASK (0x03)
63
64/* -------------------------------------------------------------------------- */
65
66struct adxl34x_config {
67 unsigned int odr; /** < output data rate in mHz */
68 unsigned int fsr; /** < full scale range mg */
69 unsigned int fsr_reg_mask; /** < register setting for fsr */
70};
71
72struct adxl34x_private_data {
73 struct adxl34x_config suspend; /** < suspend configuration */
74 struct adxl34x_config resume; /** < resume configuration */
75};
76
77/**
78 * @brief Set the output data rate for the particular configuration.
79 *
80 * @param mlsl_handle
81 * the handle to the serial channel the device is connected to.
82 * @param pdata
83 * a pointer to the slave platform data.
84 * @param config
85 * Config to modify with new ODR.
86 * @param apply
87 * whether to apply immediately or save the settings to be applied
88 * at the next resume.
89 * @param odr
90 * Output data rate in units of 1/1000Hz (mHz).
91 *
92 * @return INV_SUCCESS if successful or a non-zero error code.
93 */
94static int adxl34x_set_odr(void *mlsl_handle,
95 struct ext_slave_platform_data *pdata,
96 struct adxl34x_config *config,
97 int apply,
98 long odr)
99{
100 int result = INV_SUCCESS;
101 unsigned char new_odr_mask;
102
103 /* ADXL346 (Rev. A) pages 13, 24 */
104 if (odr >= 3200000) {
105 new_odr_mask = 0x0F;
106 config->odr = 3200000;
107 } else if (odr >= 1600000) {
108 new_odr_mask = 0x0E;
109 config->odr = 1600000;
110 } else if (odr >= 800000) {
111 new_odr_mask = 0x0D;
112 config->odr = 800000;
113 } else if (odr >= 400000) {
114 new_odr_mask = 0x0C;
115 config->odr = 400000;
116 } else if (odr >= 200000) {
117 new_odr_mask = 0x0B;
118 config->odr = 200000;
119 } else if (odr >= 100000) {
120 new_odr_mask = 0x0A;
121 config->odr = 100000;
122 } else if (odr >= 50000) {
123 new_odr_mask = 0x09;
124 config->odr = 50000;
125 } else if (odr >= 25000) {
126 new_odr_mask = 0x08;
127 config->odr = 25000;
128 } else if (odr >= 12500) {
129 new_odr_mask = 0x07;
130 config->odr = 12500;
131 } else if (odr >= 6250) {
132 new_odr_mask = 0x06;
133 config->odr = 6250;
134 } else if (odr >= 3130) {
135 new_odr_mask = 0x05;
136 config->odr = 3130;
137 } else if (odr >= 1560) {
138 new_odr_mask = 0x04;
139 config->odr = 1560;
140 } else if (odr >= 780) {
141 new_odr_mask = 0x03;
142 config->odr = 780;
143 } else if (odr >= 390) {
144 new_odr_mask = 0x02;
145 config->odr = 390;
146 } else if (odr >= 200) {
147 new_odr_mask = 0x01;
148 config->odr = 200;
149 } else {
150 new_odr_mask = 0x00;
151 config->odr = 100;
152 }
153
154 if (apply) {
155 unsigned char reg_odr;
156 result = inv_serial_read(mlsl_handle, pdata->address,
157 ADXL34X_ODR_REG, 1, &reg_odr);
158 if (result) {
159 LOG_RESULT_LOCATION(result);
160 return result;
161 }
162 reg_odr &= ~ADXL34X_ODR_MASK;
163 reg_odr |= new_odr_mask;
164 result = inv_serial_single_write(mlsl_handle, pdata->address,
165 ADXL34X_ODR_REG, reg_odr);
166 if (result) {
167 LOG_RESULT_LOCATION(result);
168 return result;
169 }
170 MPL_LOGV("ODR: %d mHz\n", config->odr);
171 }
172 return result;
173}
174
175/**
176 * @brief Set the full scale range of the accels
177 *
178 * @param mlsl_handle
179 * the handle to the serial channel the device is connected to.
180 * @param pdata
181 * a pointer to the slave platform data.
182 * @param config
183 * pointer to configuration.
184 * @param apply
185 * whether to apply immediately or save the settings to be applied
186 * at the next resume.
187 * @param fsr
188 * requested full scale range in milli gees (mg).
189 *
190 * @return INV_SUCCESS if successful or a non-zero error code.
191 */
192static int adxl34x_set_fsr(void *mlsl_handle,
193 struct ext_slave_platform_data *pdata,
194 struct adxl34x_config *config,
195 int apply,
196 long fsr)
197{
198 int result = INV_SUCCESS;
199
200 if (fsr <= 2000) {
201 config->fsr_reg_mask = 0x00;
202 config->fsr = 2000;
203 } else if (fsr <= 4000) {
204 config->fsr_reg_mask = 0x01;
205 config->fsr = 4000;
206 } else if (fsr <= 8000) {
207 config->fsr_reg_mask = 0x02;
208 config->fsr = 8000;
209 } else { /* 8001 -> oo */
210 config->fsr_reg_mask = 0x03;
211 config->fsr = 16000;
212 }
213
214 if (apply) {
215 unsigned char reg_df;
216 result = inv_serial_read(mlsl_handle, pdata->address,
217 ADXL34X_DATAFORMAT_REG, 1, &reg_df);
218 reg_df &= ~ADXL34X_DATAFORMAT_FSR_MASK;
219 result = inv_serial_single_write(mlsl_handle, pdata->address,
220 ADXL34X_DATAFORMAT_REG,
221 reg_df | config->fsr_reg_mask);
222 if (result) {
223 LOG_RESULT_LOCATION(result);
224 return result;
225 }
226 MPL_LOGV("FSR: %d mg\n", config->fsr);
227 }
228 return result;
229}
230
231/**
232 * @brief facility to retrieve the device configuration.
233 *
234 * @param mlsl_handle
235 * the handle to the serial channel the device is connected to.
236 * @param slave
237 * a pointer to the slave descriptor data structure.
238 * @param pdata
239 * a pointer to the slave platform data.
240 * @param data
241 * a pointer to store the returned configuration data structure.
242 *
243 * @return INV_SUCCESS if successful or a non-zero error code.
244 */
245static int adxl34x_get_config(void *mlsl_handle,
246 struct ext_slave_descr *slave,
247 struct ext_slave_platform_data *pdata,
248 struct ext_slave_config *data)
249{
250 struct adxl34x_private_data *private_data =
251 (struct adxl34x_private_data *)(pdata->private_data);
252
253 if (!data->data)
254 return INV_ERROR_INVALID_PARAMETER;
255
256 switch (data->key) {
257 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
258 (*(unsigned long *)data->data) =
259 (unsigned long) private_data->suspend.odr;
260 break;
261 case MPU_SLAVE_CONFIG_ODR_RESUME:
262 (*(unsigned long *)data->data) =
263 (unsigned long) private_data->resume.odr;
264 break;
265 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
266 (*(unsigned long *)data->data) =
267 (unsigned long) private_data->suspend.fsr;
268 break;
269 case MPU_SLAVE_CONFIG_FSR_RESUME:
270 (*(unsigned long *)data->data) =
271 (unsigned long) private_data->resume.fsr;
272 break;
273 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
274 case MPU_SLAVE_CONFIG_IRQ_RESUME:
275 default:
276 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
277 };
278
279 return INV_SUCCESS;
280}
281
282/**
283 * @brief device configuration facility.
284 *
285 * @param mlsl_handle
286 * the handle to the serial channel the device is connected to.
287 * @param slave
288 * a pointer to the slave descriptor data structure.
289 * @param pdata
290 * a pointer to the slave platform data.
291 * @param data
292 * a pointer to the configuration data structure.
293 *
294 * @return INV_SUCCESS if successful or a non-zero error code.
295 */
296static int adxl34x_config(void *mlsl_handle,
297 struct ext_slave_descr *slave,
298 struct ext_slave_platform_data *pdata,
299 struct ext_slave_config *data)
300{
301 struct adxl34x_private_data *private_data =
302 (struct adxl34x_private_data *)(pdata->private_data);
303
304 if (!data->data)
305 return INV_ERROR_INVALID_PARAMETER;
306
307 switch (data->key) {
308 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
309 return adxl34x_set_odr(mlsl_handle, pdata,
310 &private_data->suspend,
311 data->apply,
312 *((long *)data->data));
313 case MPU_SLAVE_CONFIG_ODR_RESUME:
314 return adxl34x_set_odr(mlsl_handle, pdata,
315 &private_data->resume,
316 data->apply,
317 *((long *)data->data));
318 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
319 return adxl34x_set_fsr(mlsl_handle, pdata,
320 &private_data->suspend,
321 data->apply,
322 *((long *)data->data));
323 case MPU_SLAVE_CONFIG_FSR_RESUME:
324 return adxl34x_set_fsr(mlsl_handle, pdata,
325 &private_data->resume,
326 data->apply,
327 *((long *)data->data));
328 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
329 case MPU_SLAVE_CONFIG_IRQ_RESUME:
330 default:
331 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
332 };
333 return INV_SUCCESS;
334}
335
336/**
337 * @brief suspends the device to put it in its lowest power mode.
338 *
339 * @param mlsl_handle
340 * the handle to the serial channel the device is connected to.
341 * @param slave
342 * a pointer to the slave descriptor data structure.
343 * @param pdata
344 * a pointer to the slave platform data.
345 *
346 * @return INV_SUCCESS if successful or a non-zero error code.
347 */
348static int adxl34x_suspend(void *mlsl_handle,
349 struct ext_slave_descr *slave,
350 struct ext_slave_platform_data *pdata)
351{
352 int result;
353
354 /*
355 struct adxl34x_config *suspend_config =
356 &((struct adxl34x_private_data *)pdata->private_data)->suspend;
357
358 result = adxl34x_set_odr(mlsl_handle, pdata, suspend_config,
359 true, suspend_config->odr);
360 if (result) {
361 LOG_RESULT_LOCATION(result);
362 return result;
363}
364 result = adxl34x_set_fsr(mlsl_handle, pdata, suspend_config,
365 true, suspend_config->fsr);
366 if (result) {
367 LOG_RESULT_LOCATION(result);
368 return result;
369}
370 */
371
372 /*
373 Page 25
374 When clearing the sleep bit, it is recommended that the part
375 be placed into standby mode and then set back to measurement mode
376 with a subsequent write.
377 This is done to ensure that the device is properly biased if sleep
378 mode is manually disabled; otherwise, the first few samples of data
379 after the sleep bit is cleared may have additional noise,
380 especially if the device was asleep when the bit was cleared. */
381
382 /* go in standy-by mode (suspends measurements) */
383 result = inv_serial_single_write(mlsl_handle, pdata->address,
384 ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK);
385 if (result) {
386 LOG_RESULT_LOCATION(result);
387 return result;
388 }
389 /* and then in sleep */
390 result = inv_serial_single_write(mlsl_handle, pdata->address,
391 ADXL34X_PWR_REG,
392 ADXL34X_PWR_MEAS_MASK | ADXL34X_PWR_SLEEP_MASK);
393 if (result) {
394 LOG_RESULT_LOCATION(result);
395 return result;
396 }
397 return result;
398}
399
400/**
401 * @brief resume the device in the proper power state given the configuration
402 * chosen.
403 *
404 * @param mlsl_handle
405 * the handle to the serial channel the device is connected to.
406 * @param slave
407 * a pointer to the slave descriptor data structure.
408 * @param pdata
409 * a pointer to the slave platform data.
410 *
411 * @return INV_SUCCESS if successful or a non-zero error code.
412 */
413static int adxl34x_resume(void *mlsl_handle,
414 struct ext_slave_descr *slave,
415 struct ext_slave_platform_data *pdata)
416{
417 int result = INV_SUCCESS;
418 struct adxl34x_config *resume_config =
419 &((struct adxl34x_private_data *)pdata->private_data)->resume;
420 unsigned char reg;
421
422 /*
423 Page 25
424 When clearing the sleep bit, it is recommended that the part
425 be placed into standby mode and then set back to measurement mode
426 with a subsequent write.
427 This is done to ensure that the device is properly biased if sleep
428 mode is manually disabled; otherwise, the first few samples of data
429 after the sleep bit is cleared may have additional noise,
430 especially if the device was asleep when the bit was cleared. */
431
432 /* remove sleep, but leave in stand-by */
433 result = inv_serial_single_write(mlsl_handle, pdata->address,
434 ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK);
435 if (result) {
436 LOG_RESULT_LOCATION(result);
437 return result;
438 }
439
440 result = adxl34x_set_odr(mlsl_handle, pdata, resume_config,
441 true, resume_config->odr);
442 if (result) {
443 LOG_RESULT_LOCATION(result);
444 return result;
445 }
446
447 /*
448 -> FSR
449 -> Justiy bit for Big endianess
450 -> resulution to 10 bits
451 */
452 reg = ADXL34X_DATAFORMAT_JUSTIFY_MASK;
453 reg |= resume_config->fsr_reg_mask;
454 result = inv_serial_single_write(mlsl_handle, pdata->address,
455 ADXL34X_DATAFORMAT_REG, reg);
456 if (result) {
457 LOG_RESULT_LOCATION(result);
458 return result;
459 }
460
461 /* go in measurement mode */
462 result = inv_serial_single_write(mlsl_handle, pdata->address,
463 ADXL34X_PWR_REG, 0x00);
464 if (result) {
465 LOG_RESULT_LOCATION(result);
466 return result;
467 }
468
469 /* DATA_FORMAT: full resolution of +/-2g; data is left justified */
470 result = inv_serial_single_write(mlsl_handle, pdata->address,
471 0x31, reg);
472
473 return result;
474}
475
476/**
477 * @brief one-time device driver initialization function.
478 * If the driver is built as a kernel module, this function will be
479 * called when the module is loaded in the kernel.
480 * If the driver is built-in in the kernel, this function will be
481 * called at boot time.
482 *
483 * @param mlsl_handle
484 * the handle to the serial channel the device is connected to.
485 * @param slave
486 * a pointer to the slave descriptor data structure.
487 * @param pdata
488 * a pointer to the slave platform data.
489 *
490 * @return INV_SUCCESS if successful or a non-zero error code.
491 */
492static int adxl34x_init(void *mlsl_handle,
493 struct ext_slave_descr *slave,
494 struct ext_slave_platform_data *pdata)
495{
496 int result;
497 long range;
498
499 struct adxl34x_private_data *private_data;
500 private_data = (struct adxl34x_private_data *)
501 kzalloc(sizeof(struct adxl34x_private_data), GFP_KERNEL);
502
503 if (!private_data)
504 return INV_ERROR_MEMORY_EXAUSTED;
505
506 pdata->private_data = private_data;
507
508 result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->suspend,
509 false, 0);
510 if (result) {
511 LOG_RESULT_LOCATION(result);
512 return result;
513 }
514 result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->resume,
515 false, 200000);
516 if (result) {
517 LOG_RESULT_LOCATION(result);
518 return result;
519 }
520
521 range = range_fixedpoint_to_long_mg(slave->range);
522 result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->suspend,
523 false, range);
524 result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->resume,
525 false, range);
526 if (result) {
527 LOG_RESULT_LOCATION(result);
528 return result;
529 }
530
531 result = adxl34x_suspend(mlsl_handle, slave, pdata);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536
537 return result;
538}
539
540/**
541 * @brief one-time device driver exit function.
542 * If the driver is built as a kernel module, this function will be
543 * called when the module is removed from the kernel.
544 *
545 * @param mlsl_handle
546 * the handle to the serial channel the device is connected to.
547 * @param slave
548 * a pointer to the slave descriptor data structure.
549 * @param pdata
550 * a pointer to the slave platform data.
551 *
552 * @return INV_SUCCESS if successful or a non-zero error code.
553 */
554static int adxl34x_exit(void *mlsl_handle,
555 struct ext_slave_descr *slave,
556 struct ext_slave_platform_data *pdata)
557{
558 kfree(pdata->private_data);
559 return INV_SUCCESS;
560}
561
562/**
563 * @brief read the sensor data from the device.
564 *
565 * @param mlsl_handle
566 * the handle to the serial channel the device is connected to.
567 * @param slave
568 * a pointer to the slave descriptor data structure.
569 * @param pdata
570 * a pointer to the slave platform data.
571 * @param data
572 * a buffer to store the data read.
573 *
574 * @return INV_SUCCESS if successful or a non-zero error code.
575 */
576static int adxl34x_read(void *mlsl_handle,
577 struct ext_slave_descr *slave,
578 struct ext_slave_platform_data *pdata,
579 unsigned char *data)
580{
581 int result;
582 result = inv_serial_read(mlsl_handle, pdata->address,
583 slave->read_reg, slave->read_len, data);
584 return result;
585}
586
587static struct ext_slave_descr adxl34x_descr = {
588 .init = adxl34x_init,
589 .exit = adxl34x_exit,
590 .suspend = adxl34x_suspend,
591 .resume = adxl34x_resume,
592 .read = adxl34x_read,
593 .config = adxl34x_config,
594 .get_config = adxl34x_get_config,
595 .name = "adxl34x", /* 5 or 6 */
596 .type = EXT_SLAVE_TYPE_ACCEL,
597 .id = ACCEL_ID_ADXL34X,
598 .read_reg = 0x32,
599 .read_len = 6,
600 .endian = EXT_SLAVE_LITTLE_ENDIAN,
601 .range = {2, 0},
602 .trigger = NULL,
603};
604
605static
606struct ext_slave_descr *adxl34x_get_slave_descr(void)
607{
608 return &adxl34x_descr;
609}
610
611/* -------------------------------------------------------------------------- */
612struct adxl34x_mod_private_data {
613 struct i2c_client *client;
614 struct ext_slave_platform_data *pdata;
615};
616
617static unsigned short normal_i2c[] = { I2C_CLIENT_END };
618
619static int adxl34x_mod_probe(struct i2c_client *client,
620 const struct i2c_device_id *devid)
621{
622 struct ext_slave_platform_data *pdata;
623 struct adxl34x_mod_private_data *private_data;
624 int result = 0;
625
626 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
627
628 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
629 result = -ENODEV;
630 goto out_no_free;
631 }
632
633 pdata = client->dev.platform_data;
634 if (!pdata) {
635 dev_err(&client->adapter->dev,
636 "Missing platform data for slave %s\n", devid->name);
637 result = -EFAULT;
638 goto out_no_free;
639 }
640
641 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
642 if (!private_data) {
643 result = -ENOMEM;
644 goto out_no_free;
645 }
646
647 i2c_set_clientdata(client, private_data);
648 private_data->client = client;
649 private_data->pdata = pdata;
650
651 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
652 adxl34x_get_slave_descr);
653 if (result) {
654 dev_err(&client->adapter->dev,
655 "Slave registration failed: %s, %d\n",
656 devid->name, result);
657 goto out_free_memory;
658 }
659
660 return result;
661
662out_free_memory:
663 kfree(private_data);
664out_no_free:
665 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
666 return result;
667
668}
669
670static int adxl34x_mod_remove(struct i2c_client *client)
671{
672 struct adxl34x_mod_private_data *private_data =
673 i2c_get_clientdata(client);
674
675 dev_dbg(&client->adapter->dev, "%s\n", __func__);
676
677 inv_mpu_unregister_slave(client, private_data->pdata,
678 adxl34x_get_slave_descr);
679
680 kfree(private_data);
681 return 0;
682}
683
684static const struct i2c_device_id adxl34x_mod_id[] = {
685 { "adxl34x", ACCEL_ID_ADXL34X },
686 {}
687};
688
689MODULE_DEVICE_TABLE(i2c, adxl34x_mod_id);
690
691static struct i2c_driver adxl34x_mod_driver = {
692 .class = I2C_CLASS_HWMON,
693 .probe = adxl34x_mod_probe,
694 .remove = adxl34x_mod_remove,
695 .id_table = adxl34x_mod_id,
696 .driver = {
697 .owner = THIS_MODULE,
698 .name = "adxl34x_mod",
699 },
700 .address_list = normal_i2c,
701};
702
703static int __init adxl34x_mod_init(void)
704{
705 int res = i2c_add_driver(&adxl34x_mod_driver);
706 pr_info("%s: Probe name %s\n", __func__, "adxl34x_mod");
707 if (res)
708 pr_err("%s failed\n", __func__);
709 return res;
710}
711
712static void __exit adxl34x_mod_exit(void)
713{
714 pr_info("%s\n", __func__);
715 i2c_del_driver(&adxl34x_mod_driver);
716}
717
718module_init(adxl34x_mod_init);
719module_exit(adxl34x_mod_exit);
720
721MODULE_AUTHOR("Invensense Corporation");
722MODULE_DESCRIPTION("Driver to integrate ADXL34X sensor with the MPU");
723MODULE_LICENSE("GPL");
724MODULE_ALIAS("adxl34x_mod");
725
726/**
727 * @}
728 */
diff --git a/drivers/misc/inv_mpu/accel/bma150.c b/drivers/misc/inv_mpu/accel/bma150.c
new file mode 100644
index 00000000000..745d90a6744
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma150.c
@@ -0,0 +1,777 @@
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 bma150.c
26 * @brief Accelerometer setup and handling methods for Bosch BMA150.
27 */
28
29/* -------------------------------------------------------------------------- */
30#include <linux/i2c.h>
31#include <linux/module.h>
32#include <linux/moduleparam.h>
33#include <linux/kernel.h>
34#include <linux/errno.h>
35#include <linux/slab.h>
36#include <linux/delay.h>
37#include "mpu-dev.h"
38
39#include <linux/mpu.h>
40#include "mlsl.h"
41#include "mldl_cfg.h"
42
43/* -------------------------------------------------------------------------- */
44/* registers */
45#define BMA150_CTRL_REG (0x14)
46#define BMA150_INT_REG (0x15)
47#define BMA150_PWR_REG (0x0A)
48
49/* masks */
50#define BMA150_CTRL_MASK (0x18)
51#define BMA150_CTRL_MASK_ODR (0xF8)
52#define BMA150_CTRL_MASK_FSR (0xE7)
53#define BMA150_INT_MASK_WUP (0xF8)
54#define BMA150_INT_MASK_IRQ (0xDF)
55#define BMA150_PWR_MASK_SLEEP (0x01)
56#define BMA150_PWR_MASK_SOFT_RESET (0x02)
57
58/* -------------------------------------------------------------------------- */
59struct bma150_config {
60 unsigned int odr; /** < output data rate mHz */
61 unsigned int fsr; /** < full scale range mgees */
62 unsigned int irq_type; /** < type of IRQ, see bma150_set_irq */
63 unsigned char ctrl_reg; /** < control register value */
64 unsigned char int_reg; /** < interrupt control register value */
65};
66
67struct bma150_private_data {
68 struct bma150_config suspend; /** < suspend configuration */
69 struct bma150_config resume; /** < resume configuration */
70};
71
72/**
73 * @brief Simply disables the IRQ since it is not usable on BMA150 devices.
74 *
75 * @param mlsl_handle
76 * the handle to the serial channel the device is connected to.
77 * @param pdata
78 * a pointer to the slave platform data.
79 * @param config
80 * configuration to apply to, suspend or resume
81 * @param apply
82 * whether to apply immediately or save the settings to be applied
83 * at the next resume.
84 * @param irq_type
85 * the type of IRQ. Valid values are
86 * - MPU_SLAVE_IRQ_TYPE_NONE
87 * - MPU_SLAVE_IRQ_TYPE_MOTION
88 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
89 * The only supported IRQ type is MPU_SLAVE_IRQ_TYPE_NONE which
90 * corresponds to disabling the IRQ completely.
91 *
92 * @return INV_SUCCESS if successful or a non-zero error code.
93 */
94static int bma150_set_irq(void *mlsl_handle,
95 struct ext_slave_platform_data *pdata,
96 struct bma150_config *config,
97 int apply,
98 long irq_type)
99{
100 int result = INV_SUCCESS;
101
102 if (irq_type != MPU_SLAVE_IRQ_TYPE_NONE)
103 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
104
105 config->irq_type = MPU_SLAVE_IRQ_TYPE_NONE;
106 config->int_reg = 0x00;
107
108 if (apply) {
109 result = inv_serial_single_write(mlsl_handle, pdata->address,
110 BMA150_CTRL_REG, config->ctrl_reg);
111 if (result) {
112 LOG_RESULT_LOCATION(result);
113 return result;
114 }
115 result = inv_serial_single_write(mlsl_handle, pdata->address,
116 BMA150_INT_REG, config->int_reg);
117 if (result) {
118 LOG_RESULT_LOCATION(result);
119 return result;
120 }
121 }
122 return result;
123}
124
125/**
126 * @brief Set the output data rate for the particular configuration.
127 *
128 * @param mlsl_handle
129 * the handle to the serial channel the device is connected to.
130 * @param pdata
131 * a pointer to the slave platform data.
132 * @param config
133 * Config to modify with new ODR.
134 * @param apply
135 * whether to apply immediately or save the settings to be applied
136 * at the next resume.
137 * @param odr
138 * Output data rate in units of 1/1000Hz (mHz).
139 *
140 * @return INV_SUCCESS if successful or a non-zero error code.
141 */
142static int bma150_set_odr(void *mlsl_handle,
143 struct ext_slave_platform_data *pdata,
144 struct bma150_config *config,
145 int apply,
146 long odr)
147{
148 unsigned char odr_bits = 0;
149 unsigned char wup_bits = 0;
150 int result = INV_SUCCESS;
151
152 if (odr > 100000) {
153 config->odr = 190000;
154 odr_bits = 0x03;
155 } else if (odr > 50000) {
156 config->odr = 100000;
157 odr_bits = 0x02;
158 } else if (odr > 25000) {
159 config->odr = 50000;
160 odr_bits = 0x01;
161 } else if (odr > 0) {
162 config->odr = 25000;
163 odr_bits = 0x00;
164 } else {
165 config->odr = 0;
166 wup_bits = 0x00;
167 }
168
169 config->int_reg &= BMA150_INT_MASK_WUP;
170 config->ctrl_reg &= BMA150_CTRL_MASK_ODR;
171 config->ctrl_reg |= odr_bits;
172
173 MPL_LOGV("ODR: %d\n", config->odr);
174 if (apply) {
175 result = inv_serial_single_write(mlsl_handle, pdata->address,
176 BMA150_CTRL_REG, config->ctrl_reg);
177 if (result) {
178 LOG_RESULT_LOCATION(result);
179 return result;
180 }
181 result = inv_serial_single_write(mlsl_handle, pdata->address,
182 BMA150_INT_REG, config->int_reg);
183 if (result) {
184 LOG_RESULT_LOCATION(result);
185 return result;
186 }
187 }
188
189 return result;
190}
191
192/**
193 * @brief Set the full scale range of the accels
194 *
195 * @param mlsl_handle
196 * the handle to the serial channel the device is connected to.
197 * @param pdata
198 * a pointer to the slave platform data.
199 * @param config
200 * pointer to configuration.
201 * @param apply
202 * whether to apply immediately or save the settings to be applied
203 * at the next resume.
204 * @param fsr
205 * requested full scale range.
206 *
207 * @return INV_SUCCESS if successful or a non-zero error code.
208 */
209static int bma150_set_fsr(void *mlsl_handle,
210 struct ext_slave_platform_data *pdata,
211 struct bma150_config *config,
212 int apply,
213 long fsr)
214{
215 unsigned char fsr_bits;
216 int result = INV_SUCCESS;
217
218 if (fsr <= 2048) {
219 fsr_bits = 0x00;
220 config->fsr = 2048;
221 } else if (fsr <= 4096) {
222 fsr_bits = 0x08;
223 config->fsr = 4096;
224 } else {
225 fsr_bits = 0x10;
226 config->fsr = 8192;
227 }
228
229 config->ctrl_reg &= BMA150_CTRL_MASK_FSR;
230 config->ctrl_reg |= fsr_bits;
231
232 MPL_LOGV("FSR: %d\n", config->fsr);
233 if (apply) {
234 result = inv_serial_single_write(mlsl_handle, pdata->address,
235 BMA150_CTRL_REG, config->ctrl_reg);
236 if (result) {
237 LOG_RESULT_LOCATION(result);
238 return result;
239 }
240 result = inv_serial_single_write(mlsl_handle, pdata->address,
241 BMA150_CTRL_REG, config->ctrl_reg);
242 if (result) {
243 LOG_RESULT_LOCATION(result);
244 return result;
245 }
246 }
247 return result;
248}
249
250/**
251 * @brief one-time device driver initialization function.
252 * If the driver is built as a kernel module, this function will be
253 * called when the module is loaded in the kernel.
254 * If the driver is built-in in the kernel, this function will be
255 * called at boot time.
256 *
257 * @param mlsl_handle
258 * the handle to the serial channel the device is connected to.
259 * @param slave
260 * a pointer to the slave descriptor data structure.
261 * @param pdata
262 * a pointer to the slave platform data.
263 *
264 * @return INV_SUCCESS if successful or a non-zero error code.
265 */
266static int bma150_init(void *mlsl_handle,
267 struct ext_slave_descr *slave,
268 struct ext_slave_platform_data *pdata)
269{
270 int result;
271 unsigned char reg;
272 long range;
273
274 struct bma150_private_data *private_data;
275 private_data = (struct bma150_private_data *)
276 kzalloc(sizeof(struct bma150_private_data), GFP_KERNEL);
277
278 if (!private_data)
279 return INV_ERROR_MEMORY_EXAUSTED;
280
281 pdata->private_data = private_data;
282
283 result = inv_serial_single_write(mlsl_handle, pdata->address,
284 BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
285 if (result) {
286 LOG_RESULT_LOCATION(result);
287 return result;
288 }
289 msleep(1);
290
291 result = inv_serial_read(mlsl_handle, pdata->address,
292 BMA150_CTRL_REG, 1, &reg);
293 if (result) {
294 LOG_RESULT_LOCATION(result);
295 return result;
296 }
297 private_data->resume.ctrl_reg = reg;
298 private_data->suspend.ctrl_reg = reg;
299
300 result = inv_serial_read(mlsl_handle, pdata->address,
301 BMA150_INT_REG, 1, &reg);
302 if (result) {
303 LOG_RESULT_LOCATION(result);
304 return result;
305 }
306 private_data->resume.int_reg = reg;
307 private_data->suspend.int_reg = reg;
308
309 result = bma150_set_odr(mlsl_handle, pdata, &private_data->suspend,
310 false, 0);
311 if (result) {
312 LOG_RESULT_LOCATION(result);
313 return result;
314 }
315 result = bma150_set_odr(mlsl_handle, pdata, &private_data->resume,
316 false, 200000);
317 if (result) {
318 LOG_RESULT_LOCATION(result);
319 return result;
320 }
321
322 range = range_fixedpoint_to_long_mg(slave->range);
323 result = bma150_set_fsr(mlsl_handle, pdata, &private_data->suspend,
324 false, range);
325 result = bma150_set_fsr(mlsl_handle, pdata, &private_data->resume,
326 false, range);
327 if (result) {
328 LOG_RESULT_LOCATION(result);
329 return result;
330 }
331
332 result = bma150_set_irq(mlsl_handle, pdata, &private_data->suspend,
333 false, MPU_SLAVE_IRQ_TYPE_NONE);
334 if (result) {
335 LOG_RESULT_LOCATION(result);
336 return result;
337 }
338 result = bma150_set_irq(mlsl_handle, pdata, &private_data->resume,
339 false, MPU_SLAVE_IRQ_TYPE_NONE);
340 if (result) {
341 LOG_RESULT_LOCATION(result);
342 return result;
343 }
344
345 result = inv_serial_single_write(mlsl_handle, pdata->address,
346 BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP);
347 if (result) {
348 LOG_RESULT_LOCATION(result);
349 return result;
350 }
351
352 return result;
353}
354
355/**
356 * @brief one-time device driver exit function.
357 * If the driver is built as a kernel module, this function will be
358 * called when the module is removed from the kernel.
359 *
360 * @param mlsl_handle
361 * the handle to the serial channel the device is connected to.
362 * @param slave
363 * a pointer to the slave descriptor data structure.
364 * @param pdata
365 * a pointer to the slave platform data.
366 *
367 * @return INV_SUCCESS if successful or a non-zero error code.
368 */
369static int bma150_exit(void *mlsl_handle,
370 struct ext_slave_descr *slave,
371 struct ext_slave_platform_data *pdata)
372{
373 kfree(pdata->private_data);
374 return INV_SUCCESS;
375}
376
377/**
378 * @brief device configuration facility.
379 *
380 * @param mlsl_handle
381 * the handle to the serial channel the device is connected to.
382 * @param slave
383 * a pointer to the slave descriptor data structure.
384 * @param pdata
385 * a pointer to the slave platform data.
386 * @param data
387 * a pointer to the configuration data structure.
388 *
389 * @return INV_SUCCESS if successful or a non-zero error code.
390 */
391static int bma150_config(void *mlsl_handle,
392 struct ext_slave_descr *slave,
393 struct ext_slave_platform_data *pdata,
394 struct ext_slave_config *data)
395{
396 struct bma150_private_data *private_data =
397 (struct bma150_private_data *)(pdata->private_data);
398
399 if (!data->data)
400 return INV_ERROR_INVALID_PARAMETER;
401
402 switch (data->key) {
403 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
404 return bma150_set_odr(mlsl_handle, pdata,
405 &private_data->suspend,
406 data->apply,
407 *((long *)data->data));
408 case MPU_SLAVE_CONFIG_ODR_RESUME:
409 return bma150_set_odr(mlsl_handle, pdata,
410 &private_data->resume,
411 data->apply,
412 *((long *)data->data));
413 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
414 return bma150_set_fsr(mlsl_handle, pdata,
415 &private_data->suspend,
416 data->apply,
417 *((long *)data->data));
418 case MPU_SLAVE_CONFIG_FSR_RESUME:
419 return bma150_set_fsr(mlsl_handle, pdata,
420 &private_data->resume,
421 data->apply,
422 *((long *)data->data));
423 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
424 return bma150_set_irq(mlsl_handle, pdata,
425 &private_data->suspend,
426 data->apply,
427 *((long *)data->data));
428 case MPU_SLAVE_CONFIG_IRQ_RESUME:
429 return bma150_set_irq(mlsl_handle, pdata,
430 &private_data->resume,
431 data->apply,
432 *((long *)data->data));
433 default:
434 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
435 };
436 return INV_SUCCESS;
437}
438
439/**
440 * @brief facility to retrieve the device configuration.
441 *
442 * @param mlsl_handle
443 * the handle to the serial channel the device is connected to.
444 * @param slave
445 * a pointer to the slave descriptor data structure.
446 * @param pdata
447 * a pointer to the slave platform data.
448 * @param data
449 * a pointer to store the returned configuration data structure.
450 *
451 * @return INV_SUCCESS if successful or a non-zero error code.
452 */
453static int bma150_get_config(void *mlsl_handle,
454 struct ext_slave_descr *slave,
455 struct ext_slave_platform_data *pdata,
456 struct ext_slave_config *data)
457{
458 struct bma150_private_data *private_data =
459 (struct bma150_private_data *)(pdata->private_data);
460
461 if (!data->data)
462 return INV_ERROR_INVALID_PARAMETER;
463
464 switch (data->key) {
465 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
466 (*(unsigned long *)data->data) =
467 (unsigned long) private_data->suspend.odr;
468 break;
469 case MPU_SLAVE_CONFIG_ODR_RESUME:
470 (*(unsigned long *)data->data) =
471 (unsigned long) private_data->resume.odr;
472 break;
473 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
474 (*(unsigned long *)data->data) =
475 (unsigned long) private_data->suspend.fsr;
476 break;
477 case MPU_SLAVE_CONFIG_FSR_RESUME:
478 (*(unsigned long *)data->data) =
479 (unsigned long) private_data->resume.fsr;
480 break;
481 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
482 (*(unsigned long *)data->data) =
483 (unsigned long) private_data->suspend.irq_type;
484 break;
485 case MPU_SLAVE_CONFIG_IRQ_RESUME:
486 (*(unsigned long *)data->data) =
487 (unsigned long) private_data->resume.irq_type;
488 break;
489 default:
490 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
491 };
492
493 return INV_SUCCESS;
494}
495
496/**
497 * @brief suspends the device to put it in its lowest power mode.
498 *
499 * @param mlsl_handle
500 * the handle to the serial channel the device is connected to.
501 * @param slave
502 * a pointer to the slave descriptor data structure.
503 * @param pdata
504 * a pointer to the slave platform data.
505 *
506 * @return INV_SUCCESS if successful or a non-zero error code.
507 */
508static int bma150_suspend(void *mlsl_handle,
509 struct ext_slave_descr *slave,
510 struct ext_slave_platform_data *pdata)
511{
512 int result;
513 unsigned char ctrl_reg;
514 unsigned char int_reg;
515
516 struct bma150_private_data *private_data =
517 (struct bma150_private_data *)(pdata->private_data);
518
519 ctrl_reg = private_data->suspend.ctrl_reg;
520 int_reg = private_data->suspend.int_reg;
521
522 result = inv_serial_single_write(mlsl_handle, pdata->address,
523 BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
524 if (result) {
525 LOG_RESULT_LOCATION(result);
526 return result;
527 }
528 msleep(1);
529
530 result = inv_serial_single_write(mlsl_handle, pdata->address,
531 BMA150_CTRL_REG, ctrl_reg);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536 result = inv_serial_single_write(mlsl_handle, pdata->address,
537 BMA150_INT_REG, int_reg);
538 if (result) {
539 LOG_RESULT_LOCATION(result);
540 return result;
541 }
542 result = inv_serial_single_write(mlsl_handle, pdata->address,
543 BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP);
544 if (result) {
545 LOG_RESULT_LOCATION(result);
546 return result;
547 }
548
549 return result;
550}
551
552/**
553 * @brief resume the device in the proper power state given the configuration
554 * chosen.
555 *
556 * @param mlsl_handle
557 * the handle to the serial channel the device is connected to.
558 * @param slave
559 * a pointer to the slave descriptor data structure.
560 * @param pdata
561 * a pointer to the slave platform data.
562 *
563 * @return INV_SUCCESS if successful or a non-zero error code.
564 */
565static int bma150_resume(void *mlsl_handle,
566 struct ext_slave_descr *slave,
567 struct ext_slave_platform_data *pdata)
568{
569 int result;
570 unsigned char ctrl_reg;
571 unsigned char int_reg;
572
573 struct bma150_private_data *private_data =
574 (struct bma150_private_data *)(pdata->private_data);
575
576 ctrl_reg = private_data->resume.ctrl_reg;
577 int_reg = private_data->resume.int_reg;
578
579 result = inv_serial_single_write(mlsl_handle, pdata->address,
580 BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
581 if (result) {
582 LOG_RESULT_LOCATION(result);
583 return result;
584 }
585 msleep(1);
586
587 result = inv_serial_single_write(mlsl_handle, pdata->address,
588 BMA150_CTRL_REG, ctrl_reg);
589 if (result) {
590 LOG_RESULT_LOCATION(result);
591 return result;
592 }
593 result = inv_serial_single_write(mlsl_handle, pdata->address,
594 BMA150_INT_REG, int_reg);
595 if (result) {
596 LOG_RESULT_LOCATION(result);
597 return result;
598 }
599
600 result = inv_serial_single_write(mlsl_handle, pdata->address,
601 BMA150_PWR_REG, 0x00);
602 if (result) {
603 LOG_RESULT_LOCATION(result);
604 return result;
605 }
606
607 return result;
608}
609
610/**
611 * @brief read the sensor data from the device.
612 *
613 * @param mlsl_handle
614 * the handle to the serial channel the device is connected to.
615 * @param slave
616 * a pointer to the slave descriptor data structure.
617 * @param pdata
618 * a pointer to the slave platform data.
619 * @param data
620 * a buffer to store the data read.
621 *
622 * @return INV_SUCCESS if successful or a non-zero error code.
623 */
624static int bma150_read(void *mlsl_handle,
625 struct ext_slave_descr *slave,
626 struct ext_slave_platform_data *pdata,
627 unsigned char *data)
628{
629 return inv_serial_read(mlsl_handle, pdata->address,
630 slave->read_reg, slave->read_len, data);
631}
632
633static struct ext_slave_descr bma150_descr = {
634 .init = bma150_init,
635 .exit = bma150_exit,
636 .suspend = bma150_suspend,
637 .resume = bma150_resume,
638 .read = bma150_read,
639 .config = bma150_config,
640 .get_config = bma150_get_config,
641 .name = "bma150",
642 .type = EXT_SLAVE_TYPE_ACCEL,
643 .id = ACCEL_ID_BMA150,
644 .read_reg = 0x02,
645 .read_len = 6,
646 .endian = EXT_SLAVE_LITTLE_ENDIAN,
647 .range = {2, 0},
648 .trigger = NULL,
649};
650
651static
652struct ext_slave_descr *bma150_get_slave_descr(void)
653{
654 return &bma150_descr;
655}
656
657/* -------------------------------------------------------------------------- */
658
659/* Platform data for the MPU */
660struct bma150_mod_private_data {
661 struct i2c_client *client;
662 struct ext_slave_platform_data *pdata;
663};
664
665static unsigned short normal_i2c[] = { I2C_CLIENT_END };
666
667static int bma150_mod_probe(struct i2c_client *client,
668 const struct i2c_device_id *devid)
669{
670 struct ext_slave_platform_data *pdata;
671 struct bma150_mod_private_data *private_data;
672 int result = 0;
673
674 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
675
676 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
677 result = -ENODEV;
678 goto out_no_free;
679 }
680
681 pdata = client->dev.platform_data;
682 if (!pdata) {
683 dev_err(&client->adapter->dev,
684 "Missing platform data for slave %s\n", devid->name);
685 result = -EFAULT;
686 goto out_no_free;
687 }
688
689 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
690 if (!private_data) {
691 result = -ENOMEM;
692 goto out_no_free;
693 }
694
695 i2c_set_clientdata(client, private_data);
696 private_data->client = client;
697 private_data->pdata = pdata;
698
699 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
700 bma150_get_slave_descr);
701 if (result) {
702 dev_err(&client->adapter->dev,
703 "Slave registration failed: %s, %d\n",
704 devid->name, result);
705 goto out_free_memory;
706 }
707
708 return result;
709
710out_free_memory:
711 kfree(private_data);
712out_no_free:
713 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
714 return result;
715
716}
717
718static int bma150_mod_remove(struct i2c_client *client)
719{
720 struct bma150_mod_private_data *private_data =
721 i2c_get_clientdata(client);
722
723 dev_dbg(&client->adapter->dev, "%s\n", __func__);
724
725 inv_mpu_unregister_slave(client, private_data->pdata,
726 bma150_get_slave_descr);
727
728 kfree(private_data);
729 return 0;
730}
731
732static const struct i2c_device_id bma150_mod_id[] = {
733 { "bma150", ACCEL_ID_BMA150 },
734 {}
735};
736
737MODULE_DEVICE_TABLE(i2c, bma150_mod_id);
738
739static struct i2c_driver bma150_mod_driver = {
740 .class = I2C_CLASS_HWMON,
741 .probe = bma150_mod_probe,
742 .remove = bma150_mod_remove,
743 .id_table = bma150_mod_id,
744 .driver = {
745 .owner = THIS_MODULE,
746 .name = "bma150_mod",
747 },
748 .address_list = normal_i2c,
749};
750
751static int __init bma150_mod_init(void)
752{
753 int res = i2c_add_driver(&bma150_mod_driver);
754 pr_info("%s: Probe name %s\n", __func__, "bma150_mod");
755 if (res)
756 pr_err("%s failed\n", __func__);
757 return res;
758}
759
760static void __exit bma150_mod_exit(void)
761{
762 pr_info("%s\n", __func__);
763 i2c_del_driver(&bma150_mod_driver);
764}
765
766module_init(bma150_mod_init);
767module_exit(bma150_mod_exit);
768
769MODULE_AUTHOR("Invensense Corporation");
770MODULE_DESCRIPTION("Driver to integrate BMA150 sensor with the MPU");
771MODULE_LICENSE("GPL");
772MODULE_ALIAS("bma150_mod");
773
774/**
775 * @}
776 */
777
diff --git a/drivers/misc/inv_mpu/accel/bma222.c b/drivers/misc/inv_mpu/accel/bma222.c
new file mode 100644
index 00000000000..e9fc99b1a62
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma222.c
@@ -0,0 +1,654 @@
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 bma222.c
26 * @brief Accelerometer setup and handling methods for Bosch BMA222.
27 */
28
29/* ------------------ */
30/* - Include Files. - */
31/* ------------------ */
32
33#include <linux/i2c.h>
34#include <linux/module.h>
35#include <linux/moduleparam.h>
36#include <linux/kernel.h>
37#include <linux/errno.h>
38#include <linux/slab.h>
39#include <linux/delay.h>
40#include "mpu-dev.h"
41
42#include <linux/mpu.h>
43#include "mlsl.h"
44#include "mldl_cfg.h"
45
46/* -------------------------------------------------------------------------- */
47
48#define BMA222_STATUS_REG (0x0A)
49#define BMA222_FSR_REG (0x0F)
50#define ADXL34X_ODR_REG (0x10)
51#define BMA222_PWR_REG (0x11)
52#define BMA222_SOFTRESET_REG (0x14)
53
54#define BMA222_STATUS_RDY_MASK (0x80)
55#define BMA222_FSR_MASK (0x0F)
56#define BMA222_ODR_MASK (0x1F)
57#define BMA222_PWR_SLEEP_MASK (0x80)
58#define BMA222_PWR_AWAKE_MASK (0x00)
59#define BMA222_SOFTRESET_MASK (0xB6)
60#define BMA222_SOFTRESET_MASK (0xB6)
61
62/* -------------------------------------------------------------------------- */
63
64struct bma222_config {
65 unsigned int odr; /** < output data rate in mHz */
66 unsigned int fsr; /** < full scale range mg */
67};
68
69struct bma222_private_data {
70 struct bma222_config suspend; /** < suspend configuration */
71 struct bma222_config resume; /** < resume configuration */
72};
73
74
75/* -------------------------------------------------------------------------- */
76
77/**
78 * @brief Set the output data rate for the particular configuration.
79 *
80 * @param mlsl_handle
81 * the handle to the serial channel the device is connected to.
82 * @param pdata
83 * a pointer to the slave platform data.
84 * @param config
85 * Config to modify with new ODR.
86 * @param apply
87 * whether to apply immediately or save the settings to be applied
88 * at the next resume.
89 * @param odr
90 * Output data rate in units of 1/1000Hz (mHz).
91 *
92 * @return INV_SUCCESS if successful or a non-zero error code.
93 */
94static int bma222_set_odr(void *mlsl_handle,
95 struct ext_slave_platform_data *pdata,
96 struct bma222_config *config,
97 int apply,
98 long odr)
99{
100 int result = INV_SUCCESS;
101 unsigned char reg_odr;
102
103 if (odr >= 1000000) {
104 reg_odr = 0x0F;
105 config->odr = 1000000;
106 } else if (odr >= 500000) {
107 reg_odr = 0x0E;
108 config->odr = 500000;
109 } else if (odr >= 250000) {
110 reg_odr = 0x0D;
111 config->odr = 250000;
112 } else if (odr >= 125000) {
113 reg_odr = 0x0C;
114 config->odr = 125000;
115 } else if (odr >= 62500) {
116 reg_odr = 0x0B;
117 config->odr = 62500;
118 } else if (odr >= 32000) {
119 reg_odr = 0x0A;
120 config->odr = 32000;
121 } else if (odr >= 16000) {
122 reg_odr = 0x09;
123 config->odr = 16000;
124 } else {
125 reg_odr = 0x08;
126 config->odr = 8000;
127 }
128
129 if (apply) {
130 MPL_LOGV("ODR: %d\n", config->odr);
131 result = inv_serial_single_write(mlsl_handle, pdata->address,
132 ADXL34X_ODR_REG, reg_odr);
133 if (result) {
134 LOG_RESULT_LOCATION(result);
135 return result;
136 }
137 }
138 return result;
139}
140
141/**
142 * @brief Set the full scale range of the accels
143 *
144 * @param mlsl_handle
145 * the handle to the serial channel the device is connected to.
146 * @param pdata
147 * a pointer to the slave platform data.
148 * @param config
149 * pointer to configuration.
150 * @param apply
151 * whether to apply immediately or save the settings to be applied
152 * at the next resume.
153 * @param fsr
154 * requested full scale range.
155 *
156 * @return INV_SUCCESS if successful or a non-zero error code.
157 */
158static int bma222_set_fsr(void *mlsl_handle,
159 struct ext_slave_platform_data *pdata,
160 struct bma222_config *config,
161 int apply,
162 long fsr)
163{
164 int result = INV_SUCCESS;
165 unsigned char reg_fsr_mask;
166
167 if (fsr <= 2000) {
168 reg_fsr_mask = 0x03;
169 config->fsr = 2000;
170 } else if (fsr <= 4000) {
171 reg_fsr_mask = 0x05;
172 config->fsr = 4000;
173 } else if (fsr <= 8000) {
174 reg_fsr_mask = 0x08;
175 config->fsr = 8000;
176 } else { /* 8001 -> oo */
177 reg_fsr_mask = 0x0C;
178 config->fsr = 16000;
179 }
180
181 if (apply) {
182 MPL_LOGV("FSR: %d\n", config->fsr);
183 result = inv_serial_single_write(mlsl_handle, pdata->address,
184 BMA222_FSR_REG, reg_fsr_mask);
185 if (result) {
186 LOG_RESULT_LOCATION(result);
187 return result;
188 }
189 }
190 return result;
191}
192
193/**
194 * @brief one-time device driver initialization function.
195 * If the driver is built as a kernel module, this function will be
196 * called when the module is loaded in the kernel.
197 * If the driver is built-in in the kernel, this function will be
198 * called at boot time.
199 *
200 * @param mlsl_handle
201 * the handle to the serial channel the device is connected to.
202 * @param slave
203 * a pointer to the slave descriptor data structure.
204 * @param pdata
205 * a pointer to the slave platform data.
206 *
207 * @return INV_SUCCESS if successful or a non-zero error code.
208 */
209static int bma222_init(void *mlsl_handle,
210 struct ext_slave_descr *slave,
211 struct ext_slave_platform_data *pdata)
212{
213 int result;
214
215 struct bma222_private_data *private_data;
216 private_data = (struct bma222_private_data *)
217 kzalloc(sizeof(struct bma222_private_data), GFP_KERNEL);
218
219 if (!private_data)
220 return INV_ERROR_MEMORY_EXAUSTED;
221
222 pdata->private_data = private_data;
223
224 result = inv_serial_single_write(mlsl_handle, pdata->address,
225 BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK);
226 if (result) {
227 LOG_RESULT_LOCATION(result);
228 return result;
229 }
230 msleep(1);
231
232 result = bma222_set_odr(mlsl_handle, pdata, &private_data->suspend,
233 false, 0);
234 if (result) {
235 LOG_RESULT_LOCATION(result);
236 return result;
237 }
238 result = bma222_set_odr(mlsl_handle, pdata, &private_data->resume,
239 false, 200000);
240 if (result) {
241 LOG_RESULT_LOCATION(result);
242 return result;
243 }
244
245 result = bma222_set_fsr(mlsl_handle, pdata, &private_data->suspend,
246 false, 2000);
247 result = bma222_set_fsr(mlsl_handle, pdata, &private_data->resume,
248 false, 2000);
249 if (result) {
250 LOG_RESULT_LOCATION(result);
251 return result;
252 }
253
254 result = inv_serial_single_write(mlsl_handle, pdata->address,
255 BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK);
256 if (result) {
257 LOG_RESULT_LOCATION(result);
258 return result;
259 }
260
261 return result;
262}
263
264/**
265 * @brief one-time device driver exit function.
266 * If the driver is built as a kernel module, this function will be
267 * called when the module is removed from the kernel.
268 *
269 * @param mlsl_handle
270 * the handle to the serial channel the device is connected to.
271 * @param slave
272 * a pointer to the slave descriptor data structure.
273 * @param pdata
274 * a pointer to the slave platform data.
275 *
276 * @return INV_SUCCESS if successful or a non-zero error code.
277 */
278static int bma222_exit(void *mlsl_handle,
279 struct ext_slave_descr *slave,
280 struct ext_slave_platform_data *pdata)
281{
282 kfree(pdata->private_data);
283 return INV_SUCCESS;
284}
285
286
287/**
288 * @brief facility to retrieve the device configuration.
289 *
290 * @param mlsl_handle
291 * the handle to the serial channel the device is connected to.
292 * @param slave
293 * a pointer to the slave descriptor data structure.
294 * @param pdata
295 * a pointer to the slave platform data.
296 * @param data
297 * a pointer to store the returned configuration data structure.
298 *
299 * @return INV_SUCCESS if successful or a non-zero error code.
300 */
301static int bma222_get_config(void *mlsl_handle,
302 struct ext_slave_descr *slave,
303 struct ext_slave_platform_data *pdata,
304 struct ext_slave_config *data)
305{
306 struct bma222_private_data *private_data =
307 (struct bma222_private_data *)(pdata->private_data);
308
309 if (!data->data)
310 return INV_ERROR_INVALID_PARAMETER;
311
312 switch (data->key) {
313 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
314 (*(unsigned long *)data->data) =
315 (unsigned long) private_data->suspend.odr;
316 break;
317 case MPU_SLAVE_CONFIG_ODR_RESUME:
318 (*(unsigned long *)data->data) =
319 (unsigned long) private_data->resume.odr;
320 break;
321 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
322 (*(unsigned long *)data->data) =
323 (unsigned long) private_data->suspend.fsr;
324 break;
325 case MPU_SLAVE_CONFIG_FSR_RESUME:
326 (*(unsigned long *)data->data) =
327 (unsigned long) private_data->resume.fsr;
328 break;
329 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
330 case MPU_SLAVE_CONFIG_IRQ_RESUME:
331 default:
332 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
333 };
334
335 return INV_SUCCESS;
336}
337
338/**
339 * @brief device configuration facility.
340 *
341 * @param mlsl_handle
342 * the handle to the serial channel the device is connected to.
343 * @param slave
344 * a pointer to the slave descriptor data structure.
345 * @param pdata
346 * a pointer to the slave platform data.
347 * @param data
348 * a pointer to the configuration data structure.
349 *
350 * @return INV_SUCCESS if successful or a non-zero error code.
351 */
352static int bma222_config(void *mlsl_handle,
353 struct ext_slave_descr *slave,
354 struct ext_slave_platform_data *pdata,
355 struct ext_slave_config *data)
356{
357 struct bma222_private_data *private_data =
358 (struct bma222_private_data *)(pdata->private_data);
359
360 if (!data->data)
361 return INV_ERROR_INVALID_PARAMETER;
362
363 switch (data->key) {
364 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
365 return bma222_set_odr(mlsl_handle, pdata,
366 &private_data->suspend,
367 data->apply,
368 *((long *)data->data));
369 case MPU_SLAVE_CONFIG_ODR_RESUME:
370 return bma222_set_odr(mlsl_handle, pdata,
371 &private_data->resume,
372 data->apply,
373 *((long *)data->data));
374 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
375 return bma222_set_fsr(mlsl_handle, pdata,
376 &private_data->suspend,
377 data->apply,
378 *((long *)data->data));
379 case MPU_SLAVE_CONFIG_FSR_RESUME:
380 return bma222_set_fsr(mlsl_handle, pdata,
381 &private_data->resume,
382 data->apply,
383 *((long *)data->data));
384 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
385 case MPU_SLAVE_CONFIG_IRQ_RESUME:
386 default:
387 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
388 };
389 return INV_SUCCESS;
390}
391
392/**
393 * @brief suspends the device to put it in its lowest power mode.
394 *
395 * @param mlsl_handle
396 * the handle to the serial channel the device is connected to.
397 * @param slave
398 * a pointer to the slave descriptor data structure.
399 * @param pdata
400 * a pointer to the slave platform data.
401 *
402 * @return INV_SUCCESS if successful or a non-zero error code.
403 */
404static int bma222_suspend(void *mlsl_handle,
405 struct ext_slave_descr *slave,
406 struct ext_slave_platform_data *pdata)
407{
408 int result;
409 struct bma222_config *suspend_config =
410 &((struct bma222_private_data *)pdata->private_data)->suspend;
411
412 result = bma222_set_odr(mlsl_handle, pdata, suspend_config,
413 true, suspend_config->odr);
414 if (result) {
415 LOG_RESULT_LOCATION(result);
416 return result;
417 }
418 result = bma222_set_fsr(mlsl_handle, pdata, suspend_config,
419 true, suspend_config->fsr);
420 if (result) {
421 LOG_RESULT_LOCATION(result);
422 return result;
423 }
424
425 result = inv_serial_single_write(mlsl_handle, pdata->address,
426 BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK);
427 if (result) {
428 LOG_RESULT_LOCATION(result);
429 return result;
430 }
431
432 msleep(3); /* 3 ms powerup time maximum */
433 return result;
434}
435
436/**
437 * @brief resume the device in the proper power state given the configuration
438 * chosen.
439 *
440 * @param mlsl_handle
441 * the handle to the serial channel the device is connected to.
442 * @param slave
443 * a pointer to the slave descriptor data structure.
444 * @param pdata
445 * a pointer to the slave platform data.
446 *
447 * @return INV_SUCCESS if successful or a non-zero error code.
448 */
449static int bma222_resume(void *mlsl_handle,
450 struct ext_slave_descr *slave,
451 struct ext_slave_platform_data *pdata)
452{
453 int result;
454 struct bma222_config *resume_config =
455 &((struct bma222_private_data *)pdata->private_data)->resume;
456
457 /* Soft reset */
458 result = inv_serial_single_write(mlsl_handle, pdata->address,
459 BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK);
460 if (result) {
461 LOG_RESULT_LOCATION(result);
462 return result;
463 }
464 msleep(10);
465
466 result = bma222_set_odr(mlsl_handle, pdata, resume_config,
467 true, resume_config->odr);
468 if (result) {
469 LOG_RESULT_LOCATION(result);
470 return result;
471 }
472 result = bma222_set_fsr(mlsl_handle, pdata, resume_config,
473 true, resume_config->fsr);
474 if (result) {
475 LOG_RESULT_LOCATION(result);
476 return result;
477 }
478
479 return result;
480}
481
482/**
483 * @brief read the sensor data from the device.
484 *
485 * @param mlsl_handle
486 * the handle to the serial channel the device is connected to.
487 * @param slave
488 * a pointer to the slave descriptor data structure.
489 * @param pdata
490 * a pointer to the slave platform data.
491 * @param data
492 * a buffer to store the data read.
493 *
494 * @return INV_SUCCESS if successful or a non-zero error code.
495 */
496static int bma222_read(void *mlsl_handle,
497 struct ext_slave_descr *slave,
498 struct ext_slave_platform_data *pdata,
499 unsigned char *data)
500{
501 int result = INV_SUCCESS;
502 result = inv_serial_read(mlsl_handle, pdata->address,
503 BMA222_STATUS_REG, 1, data);
504 if (data[0] & BMA222_STATUS_RDY_MASK) {
505 result = inv_serial_read(mlsl_handle, pdata->address,
506 slave->read_reg, slave->read_len, data);
507 return result;
508 } else
509 return INV_ERROR_ACCEL_DATA_NOT_READY;
510}
511
512static struct ext_slave_descr bma222_descr = {
513 .init = bma222_init,
514 .exit = bma222_exit,
515 .suspend = bma222_suspend,
516 .resume = bma222_resume,
517 .read = bma222_read,
518 .config = bma222_config,
519 .get_config = bma222_get_config,
520 .name = "bma222",
521 .type = EXT_SLAVE_TYPE_ACCEL,
522 .id = ACCEL_ID_BMA222,
523 .read_reg = 0x02,
524 .read_len = 6,
525 .endian = EXT_SLAVE_LITTLE_ENDIAN,
526 .range = {2, 0},
527 .trigger = NULL,
528};
529
530static
531struct ext_slave_descr *bma222_get_slave_descr(void)
532{
533 return &bma222_descr;
534}
535
536/* -------------------------------------------------------------------------- */
537
538struct bma222_mod_private_data {
539 struct i2c_client *client;
540 struct ext_slave_platform_data *pdata;
541};
542
543static unsigned short normal_i2c[] = { I2C_CLIENT_END };
544
545static int bma222_mod_probe(struct i2c_client *client,
546 const struct i2c_device_id *devid)
547{
548 struct ext_slave_platform_data *pdata;
549 struct bma222_mod_private_data *private_data;
550 int result = 0;
551
552 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
553
554 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
555 result = -ENODEV;
556 goto out_no_free;
557 }
558
559 pdata = client->dev.platform_data;
560 if (!pdata) {
561 dev_err(&client->adapter->dev,
562 "Missing platform data for slave %s\n", devid->name);
563 result = -EFAULT;
564 goto out_no_free;
565 }
566
567 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
568 if (!private_data) {
569 result = -ENOMEM;
570 goto out_no_free;
571 }
572
573 i2c_set_clientdata(client, private_data);
574 private_data->client = client;
575 private_data->pdata = pdata;
576
577 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
578 bma222_get_slave_descr);
579 if (result) {
580 dev_err(&client->adapter->dev,
581 "Slave registration failed: %s, %d\n",
582 devid->name, result);
583 goto out_free_memory;
584 }
585
586 return result;
587
588out_free_memory:
589 kfree(private_data);
590out_no_free:
591 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
592 return result;
593
594}
595
596static int bma222_mod_remove(struct i2c_client *client)
597{
598 struct bma222_mod_private_data *private_data =
599 i2c_get_clientdata(client);
600
601 dev_dbg(&client->adapter->dev, "%s\n", __func__);
602
603 inv_mpu_unregister_slave(client, private_data->pdata,
604 bma222_get_slave_descr);
605
606 kfree(private_data);
607 return 0;
608}
609
610static const struct i2c_device_id bma222_mod_id[] = {
611 { "bma222", ACCEL_ID_BMA222 },
612 {}
613};
614
615MODULE_DEVICE_TABLE(i2c, bma222_mod_id);
616
617static struct i2c_driver bma222_mod_driver = {
618 .class = I2C_CLASS_HWMON,
619 .probe = bma222_mod_probe,
620 .remove = bma222_mod_remove,
621 .id_table = bma222_mod_id,
622 .driver = {
623 .owner = THIS_MODULE,
624 .name = "bma222_mod",
625 },
626 .address_list = normal_i2c,
627};
628
629static int __init bma222_mod_init(void)
630{
631 int res = i2c_add_driver(&bma222_mod_driver);
632 pr_info("%s: Probe name %s\n", __func__, "bma222_mod");
633 if (res)
634 pr_err("%s failed\n", __func__);
635 return res;
636}
637
638static void __exit bma222_mod_exit(void)
639{
640 pr_info("%s\n", __func__);
641 i2c_del_driver(&bma222_mod_driver);
642}
643
644module_init(bma222_mod_init);
645module_exit(bma222_mod_exit);
646
647MODULE_AUTHOR("Invensense Corporation");
648MODULE_DESCRIPTION("Driver to integrate BMA222 sensor with the MPU");
649MODULE_LICENSE("GPL");
650MODULE_ALIAS("bma222_mod");
651
652/**
653 * @}
654 */
diff --git a/drivers/misc/inv_mpu/accel/bma250.c b/drivers/misc/inv_mpu/accel/bma250.c
new file mode 100644
index 00000000000..6a245f4566a
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma250.c
@@ -0,0 +1,787 @@
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 bma250.c
26 * @brief Accelerometer setup and handling methods for Bosch BMA250.
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 <linux/mpu.h>
41#include "mlsl.h"
42#include "mldl_cfg.h"
43
44/* -------------------------------------------------------------------------- */
45
46/* registers */
47#define BMA250_STATUS_REG (0x0A)
48#define BMA250_FSR_REG (0x0F)
49#define BMA250_ODR_REG (0x10)
50#define BMA250_PWR_REG (0x11)
51#define BMA250_SOFTRESET_REG (0x14)
52#define BMA250_INT_TYPE_REG (0x17)
53#define BMA250_INT_DST_REG (0x1A)
54#define BMA250_INT_SRC_REG (0x1E)
55
56/* masks */
57#define BMA250_STATUS_RDY_MASK (0x80)
58#define BMA250_FSR_MASK (0x0F)
59#define BMA250_ODR_MASK (0x1F)
60#define BMA250_PWR_SLEEP_MASK (0x80)
61#define BMA250_PWR_AWAKE_MASK (0x00)
62#define BMA250_SOFTRESET_MASK (0xB6)
63#define BMA250_INT_TYPE_MASK (0x10)
64#define BMA250_INT_DST_1_MASK (0x01)
65#define BMA250_INT_DST_2_MASK (0x80)
66#define BMA250_INT_SRC_MASK (0x00)
67
68/* -------------------------------------------------------------------------- */
69
70struct bma250_config {
71 unsigned int odr; /** < output data rate in mHz */
72 unsigned int fsr; /** < full scale range mg */
73 unsigned char irq_type;
74};
75
76struct bma250_private_data {
77 struct bma250_config suspend; /** < suspend configuration */
78 struct bma250_config resume; /** < resume configuration */
79};
80
81/* -------------------------------------------------------------------------- */
82/**
83 * @brief Sets the IRQ to fire when one of the IRQ events occur.
84 * Threshold and duration will not be used unless the type is MOT or
85 * NMOT.
86 *
87 * @param mlsl_handle
88 * the handle to the serial channel the device is connected to.
89 * @param pdata
90 * a pointer to the slave platform data.
91 * @param config
92 * configuration to apply to, suspend or resume
93 * @param apply
94 * whether to apply immediately or save the settings to be applied
95 * at the next resume.
96 * @param irq_type
97 * the type of IRQ. Valid values are
98 * - MPU_SLAVE_IRQ_TYPE_NONE
99 * - MPU_SLAVE_IRQ_TYPE_MOTION
100 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
101 *
102 * @return INV_SUCCESS if successful or a non-zero error code.
103 */
104static int bma250_set_irq(void *mlsl_handle,
105 struct ext_slave_platform_data *pdata,
106 struct bma250_config *config,
107 int apply, long irq_type)
108{
109 int result = INV_SUCCESS;
110 unsigned char irqtype_reg;
111 unsigned char irqdst_reg;
112 unsigned char irqsrc_reg;
113
114 switch (irq_type) {
115 case MPU_SLAVE_IRQ_TYPE_DATA_READY:
116 /* data ready int. */
117 irqtype_reg = BMA250_INT_TYPE_MASK;
118 /* routed to interrupt pin 1 */
119 irqdst_reg = BMA250_INT_DST_1_MASK;
120 /* from filtered data */
121 irqsrc_reg = BMA250_INT_SRC_MASK;
122 break;
123 /* unfinished
124 case MPU_SLAVE_IRQ_TYPE_MOTION:
125 reg1 = 0x00;
126 reg2 = config->mot_int1_cfg;
127 reg3 = ;
128 break;
129 */
130 case MPU_SLAVE_IRQ_TYPE_NONE:
131 irqtype_reg = 0x00;
132 irqdst_reg = 0x00;
133 irqsrc_reg = 0x00;
134 break;
135 default:
136 return INV_ERROR_INVALID_PARAMETER;
137 break;
138 }
139
140 config->irq_type = (unsigned char)irq_type;
141
142 if (apply) {
143 /* select the type of interrupt to use */
144 result = inv_serial_single_write(mlsl_handle, pdata->address,
145 BMA250_INT_TYPE_REG, irqtype_reg);
146 if (result) {
147 LOG_RESULT_LOCATION(result);
148 return result;
149 }
150 /* select to which interrupt pin to route it to */
151 result = inv_serial_single_write(mlsl_handle, pdata->address,
152 BMA250_INT_DST_REG, irqdst_reg);
153 if (result) {
154 LOG_RESULT_LOCATION(result);
155 return result;
156 }
157 /* select whether the interrupt works off filtered or
158 unfiltered data */
159 result = inv_serial_single_write(mlsl_handle, pdata->address,
160 BMA250_INT_SRC_REG, irqsrc_reg);
161 if (result) {
162 LOG_RESULT_LOCATION(result);
163 return result;
164 }
165 }
166 return result;
167}
168
169/**
170 * @brief Set the output data rate for the particular configuration.
171 *
172 * @param mlsl_handle
173 * the handle to the serial channel the device is connected to.
174 * @param pdata
175 * a pointer to the slave platform data.
176 * @param config
177 * Config to modify with new ODR.
178 * @param apply
179 * whether to apply immediately or save the settings to be applied
180 * at the next resume.
181 * @param odr
182 * Output data rate in units of 1/1000Hz (mHz).
183 *
184 * @return INV_SUCCESS if successful or a non-zero error code.
185 */
186static int bma250_set_odr(void *mlsl_handle,
187 struct ext_slave_platform_data *pdata,
188 struct bma250_config *config,
189 int apply,
190 long odr)
191{
192 int result = INV_SUCCESS;
193 unsigned char reg_odr;
194
195 /* Table uses bandwidth which is half the sample rate */
196 odr = odr >> 1;
197 if (odr >= 1000000) {
198 reg_odr = 0x0F;
199 config->odr = 2000000;
200 } else if (odr >= 500000) {
201 reg_odr = 0x0E;
202 config->odr = 1000000;
203 } else if (odr >= 250000) {
204 reg_odr = 0x0D;
205 config->odr = 500000;
206 } else if (odr >= 125000) {
207 reg_odr = 0x0C;
208 config->odr = 250000;
209 } else if (odr >= 62500) {
210 reg_odr = 0x0B;
211 config->odr = 125000;
212 } else if (odr >= 31250) {
213 reg_odr = 0x0A;
214 config->odr = 62500;
215 } else if (odr >= 15630) {
216 reg_odr = 0x09;
217 config->odr = 31250;
218 } else {
219 reg_odr = 0x08;
220 config->odr = 15630;
221 }
222
223 if (apply) {
224 MPL_LOGV("ODR: %d\n", config->odr);
225 result = inv_serial_single_write(mlsl_handle, pdata->address,
226 BMA250_ODR_REG, reg_odr);
227 if (result) {
228 LOG_RESULT_LOCATION(result);
229 return result;
230 }
231 }
232
233 return result;
234}
235
236/**
237 * @brief Set the full scale range of the accels
238 *
239 * @param mlsl_handle
240 * the handle to the serial channel the device is connected to.
241 * @param pdata
242 * a pointer to the slave platform data.
243 * @param config
244 * pointer to configuration.
245 * @param apply
246 * whether to apply immediately or save the settings to be applied
247 * at the next resume.
248 * @param fsr
249 * requested full scale range.
250 *
251 * @return INV_SUCCESS if successful or a non-zero error code.
252 */
253static int bma250_set_fsr(void *mlsl_handle,
254 struct ext_slave_platform_data *pdata,
255 struct bma250_config *config,
256 int apply,
257 long fsr)
258{
259 int result = INV_SUCCESS;
260 unsigned char reg_fsr_mask;
261
262 if (fsr <= 2000) {
263 reg_fsr_mask = 0x03;
264 config->fsr = 2000;
265 } else if (fsr <= 4000) {
266 reg_fsr_mask = 0x05;
267 config->fsr = 4000;
268 } else if (fsr <= 8000) {
269 reg_fsr_mask = 0x08;
270 config->fsr = 8000;
271 } else { /* 8001 -> oo */
272 reg_fsr_mask = 0x0C;
273 config->fsr = 16000;
274 }
275
276 if (apply) {
277 MPL_LOGV("FSR: %d\n", config->fsr);
278 result = inv_serial_single_write(mlsl_handle, pdata->address,
279 BMA250_FSR_REG, reg_fsr_mask);
280 if (result) {
281 LOG_RESULT_LOCATION(result);
282 return result;
283 }
284 }
285 return result;
286}
287
288/**
289 * @brief one-time device driver initialization function.
290 * If the driver is built as a kernel module, this function will be
291 * called when the module is loaded in the kernel.
292 * If the driver is built-in in the kernel, this function will be
293 * called at boot time.
294 *
295 * @param mlsl_handle
296 * the handle to the serial channel the device is connected to.
297 * @param slave
298 * a pointer to the slave descriptor data structure.
299 * @param pdata
300 * a pointer to the slave platform data.
301 *
302 * @return INV_SUCCESS if successful or a non-zero error code.
303 */
304static int bma250_init(void *mlsl_handle,
305 struct ext_slave_descr *slave,
306 struct ext_slave_platform_data *pdata)
307{
308 int result;
309 long range;
310
311 struct bma250_private_data *private_data;
312 private_data = kzalloc(sizeof(struct bma250_private_data), GFP_KERNEL);
313
314 if (!private_data)
315 return INV_ERROR_MEMORY_EXAUSTED;
316
317 pdata->private_data = private_data;
318
319 result = inv_serial_single_write(mlsl_handle, pdata->address,
320 BMA250_SOFTRESET_REG, BMA250_SOFTRESET_MASK);
321 if (result) {
322 LOG_RESULT_LOCATION(result);
323 return result;
324 }
325 msleep(1);
326
327 result = bma250_set_odr(mlsl_handle, pdata, &private_data->suspend,
328 false, 0);
329 if (result) {
330 LOG_RESULT_LOCATION(result);
331 return result;
332 }
333 result = bma250_set_odr(mlsl_handle, pdata, &private_data->resume,
334 false, 200000);
335 if (result) {
336 LOG_RESULT_LOCATION(result);
337 return result;
338 }
339
340 range = range_fixedpoint_to_long_mg(slave->range);
341 result = bma250_set_fsr(mlsl_handle, pdata, &private_data->suspend,
342 false, range);
343 result = bma250_set_fsr(mlsl_handle, pdata, &private_data->resume,
344 false, range);
345 if (result) {
346 LOG_RESULT_LOCATION(result);
347 return result;
348 }
349
350 result = bma250_set_irq(mlsl_handle, pdata, &private_data->suspend,
351 false, MPU_SLAVE_IRQ_TYPE_NONE);
352 if (result) {
353 LOG_RESULT_LOCATION(result);
354 return result;
355 }
356 result = bma250_set_irq(mlsl_handle, pdata, &private_data->resume,
357 false, MPU_SLAVE_IRQ_TYPE_NONE);
358 if (result) {
359 LOG_RESULT_LOCATION(result);
360 return result;
361 }
362
363 result = inv_serial_single_write(mlsl_handle, pdata->address,
364 BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK);
365 if (result) {
366 LOG_RESULT_LOCATION(result);
367 return result;
368 }
369
370 return result;
371}
372
373/**
374 * @brief one-time device driver exit function.
375 * If the driver is built as a kernel module, this function will be
376 * called when the module is removed from the kernel.
377 *
378 * @param mlsl_handle
379 * the handle to the serial channel the device is connected to.
380 * @param slave
381 * a pointer to the slave descriptor data structure.
382 * @param pdata
383 * a pointer to the slave platform data.
384 *
385 * @return INV_SUCCESS if successful or a non-zero error code.
386 */
387static int bma250_exit(void *mlsl_handle,
388 struct ext_slave_descr *slave,
389 struct ext_slave_platform_data *pdata)
390{
391 kfree(pdata->private_data);
392 return INV_SUCCESS;
393}
394
395/**
396 * @brief device configuration facility.
397 *
398 * @param mlsl_handle
399 * the handle to the serial channel the device is connected to.
400 * @param slave
401 * a pointer to the slave descriptor data structure.
402 * @param pdata
403 * a pointer to the slave platform data.
404 * @param data
405 * a pointer to the configuration data structure.
406 *
407 * @return INV_SUCCESS if successful or a non-zero error code.
408 */
409static int bma250_config(void *mlsl_handle,
410 struct ext_slave_descr *slave,
411 struct ext_slave_platform_data *pdata,
412 struct ext_slave_config *data)
413{
414 struct bma250_private_data *private_data =
415 (struct bma250_private_data *)(pdata->private_data);
416
417 if (!data->data)
418 return INV_ERROR_INVALID_PARAMETER;
419
420 switch (data->key) {
421 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
422 return bma250_set_odr(mlsl_handle, pdata,
423 &private_data->suspend,
424 data->apply,
425 *((long *)data->data));
426 case MPU_SLAVE_CONFIG_ODR_RESUME:
427 return bma250_set_odr(mlsl_handle, pdata,
428 &private_data->resume,
429 data->apply,
430 *((long *)data->data));
431 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
432 return bma250_set_fsr(mlsl_handle, pdata,
433 &private_data->suspend,
434 data->apply,
435 *((long *)data->data));
436 case MPU_SLAVE_CONFIG_FSR_RESUME:
437 return bma250_set_fsr(mlsl_handle, pdata,
438 &private_data->resume,
439 data->apply,
440 *((long *)data->data));
441 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
442 return bma250_set_irq(mlsl_handle, pdata,
443 &private_data->suspend,
444 data->apply,
445 *((long *)data->data));
446 case MPU_SLAVE_CONFIG_IRQ_RESUME:
447 return bma250_set_irq(mlsl_handle, pdata,
448 &private_data->resume,
449 data->apply,
450 *((long *)data->data));
451 default:
452 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
453 };
454 return INV_SUCCESS;
455}
456
457/**
458 * @brief facility to retrieve the device configuration.
459 *
460 * @param mlsl_handle
461 * the handle to the serial channel the device is connected to.
462 * @param slave
463 * a pointer to the slave descriptor data structure.
464 * @param pdata
465 * a pointer to the slave platform data.
466 * @param data
467 * a pointer to store the returned configuration data structure.
468 *
469 * @return INV_SUCCESS if successful or a non-zero error code.
470 */
471static int bma250_get_config(void *mlsl_handle,
472 struct ext_slave_descr *slave,
473 struct ext_slave_platform_data *pdata,
474 struct ext_slave_config *data)
475{
476 struct bma250_private_data *private_data =
477 (struct bma250_private_data *)(pdata->private_data);
478
479 if (!data->data)
480 return INV_ERROR_INVALID_PARAMETER;
481
482 switch (data->key) {
483 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
484 (*(unsigned long *)data->data) =
485 (unsigned long) private_data->suspend.odr;
486 break;
487 case MPU_SLAVE_CONFIG_ODR_RESUME:
488 (*(unsigned long *)data->data) =
489 (unsigned long) private_data->resume.odr;
490 break;
491 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
492 (*(unsigned long *)data->data) =
493 (unsigned long) private_data->suspend.fsr;
494 break;
495 case MPU_SLAVE_CONFIG_FSR_RESUME:
496 (*(unsigned long *)data->data) =
497 (unsigned long) private_data->resume.fsr;
498 break;
499 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
500 (*(unsigned long *)data->data) =
501 (unsigned long) private_data->suspend.irq_type;
502 break;
503 case MPU_SLAVE_CONFIG_IRQ_RESUME:
504 (*(unsigned long *)data->data) =
505 (unsigned long) private_data->resume.irq_type;
506 break;
507 default:
508 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
509 };
510
511 return INV_SUCCESS;
512}
513
514/**
515 * @brief suspends the device to put it in its lowest power mode.
516 *
517 * @param mlsl_handle
518 * the handle to the serial channel the device is connected to.
519 * @param slave
520 * a pointer to the slave descriptor data structure.
521 * @param pdata
522 * a pointer to the slave platform data.
523 *
524 * @return INV_SUCCESS if successful or a non-zero error code.
525 */
526static int bma250_suspend(void *mlsl_handle,
527 struct ext_slave_descr *slave,
528 struct ext_slave_platform_data *pdata)
529{
530 int result;
531 struct bma250_config *suspend_config =
532 &((struct bma250_private_data *)pdata->private_data)->suspend;
533
534 result = bma250_set_odr(mlsl_handle, pdata, suspend_config,
535 true, suspend_config->odr);
536 if (result) {
537 LOG_RESULT_LOCATION(result);
538 return result;
539 }
540 result = bma250_set_fsr(mlsl_handle, pdata, suspend_config,
541 true, suspend_config->fsr);
542 if (result) {
543 LOG_RESULT_LOCATION(result);
544 return result;
545 }
546 result = bma250_set_irq(mlsl_handle, pdata, suspend_config,
547 true, suspend_config->irq_type);
548 if (result) {
549 LOG_RESULT_LOCATION(result);
550 return result;
551 }
552
553 result = inv_serial_single_write(mlsl_handle, pdata->address,
554 BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK);
555 if (result) {
556 LOG_RESULT_LOCATION(result);
557 return result;
558 }
559
560 msleep(3); /* 3 ms powerup time maximum */
561 return result;
562}
563
564/**
565 * @brief resume the device in the proper power state given the configuration
566 * chosen.
567 *
568 * @param mlsl_handle
569 * the handle to the serial channel the device is connected to.
570 * @param slave
571 * a pointer to the slave descriptor data structure.
572 * @param pdata
573 * a pointer to the slave platform data.
574 *
575 * @return INV_SUCCESS if successful or a non-zero error code.
576 */
577static int bma250_resume(void *mlsl_handle,
578 struct ext_slave_descr *slave,
579 struct ext_slave_platform_data *pdata)
580{
581 int result;
582 struct bma250_config *resume_config =
583 &((struct bma250_private_data *)pdata->private_data)->resume;
584
585 result = bma250_set_odr(mlsl_handle, pdata, resume_config,
586 true, resume_config->odr);
587 if (result) {
588 LOG_RESULT_LOCATION(result);
589 return result;
590 }
591 result = bma250_set_fsr(mlsl_handle, pdata, resume_config,
592 true, resume_config->fsr);
593 if (result) {
594 LOG_RESULT_LOCATION(result);
595 return result;
596 }
597 result = bma250_set_irq(mlsl_handle, pdata, resume_config,
598 true, resume_config->irq_type);
599 if (result) {
600 LOG_RESULT_LOCATION(result);
601 return result;
602 }
603
604 result = inv_serial_single_write(mlsl_handle, pdata->address,
605 BMA250_PWR_REG, BMA250_PWR_AWAKE_MASK);
606 if (result) {
607 LOG_RESULT_LOCATION(result);
608 return result;
609 }
610
611 return result;
612}
613
614/**
615 * @brief read the sensor data from the device.
616 *
617 * @param mlsl_handle
618 * the handle to the serial channel the device is connected to.
619 * @param slave
620 * a pointer to the slave descriptor data structure.
621 * @param pdata
622 * a pointer to the slave platform data.
623 * @param data
624 * a buffer to store the data read.
625 *
626 * @return INV_SUCCESS if successful or a non-zero error code.
627 */
628static int bma250_read(void *mlsl_handle,
629 struct ext_slave_descr *slave,
630 struct ext_slave_platform_data *pdata,
631 unsigned char *data)
632{
633 int result = INV_SUCCESS;
634 result = inv_serial_read(mlsl_handle, pdata->address,
635 BMA250_STATUS_REG, 1, data);
636 if (1) { /* KLP - workaroud for small data ready window */
637 result = inv_serial_read(mlsl_handle, pdata->address,
638 slave->read_reg, slave->read_len, data);
639 return result;
640 } else
641 return INV_ERROR_ACCEL_DATA_NOT_READY;
642}
643
644static struct ext_slave_descr bma250_descr = {
645 .init = bma250_init,
646 .exit = bma250_exit,
647 .suspend = bma250_suspend,
648 .resume = bma250_resume,
649 .read = bma250_read,
650 .config = bma250_config,
651 .get_config = bma250_get_config,
652 .name = "bma250",
653 .type = EXT_SLAVE_TYPE_ACCEL,
654 .id = ACCEL_ID_BMA250,
655 .read_reg = 0x02,
656 .read_len = 6,
657 .endian = EXT_SLAVE_LITTLE_ENDIAN,
658 .range = {2, 0},
659 .trigger = NULL,
660};
661
662static
663struct ext_slave_descr *bma250_get_slave_descr(void)
664{
665 return &bma250_descr;
666}
667
668/* -------------------------------------------------------------------------- */
669
670/* Platform data for the MPU */
671struct bma250_mod_private_data {
672 struct i2c_client *client;
673 struct ext_slave_platform_data *pdata;
674};
675
676static unsigned short normal_i2c[] = { I2C_CLIENT_END };
677
678static int bma250_mod_probe(struct i2c_client *client,
679 const struct i2c_device_id *devid)
680{
681 struct ext_slave_platform_data *pdata;
682 struct bma250_mod_private_data *private_data;
683 int result = 0;
684
685 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
686
687 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
688 result = -ENODEV;
689 goto out_no_free;
690 }
691
692 pdata = client->dev.platform_data;
693 if (!pdata) {
694 dev_err(&client->adapter->dev,
695 "Missing platform data for slave %s\n", devid->name);
696 result = -EFAULT;
697 goto out_no_free;
698 }
699
700 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
701 if (!private_data) {
702 result = -ENOMEM;
703 goto out_no_free;
704 }
705
706 i2c_set_clientdata(client, private_data);
707 private_data->client = client;
708 private_data->pdata = pdata;
709
710 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
711 bma250_get_slave_descr);
712 if (result) {
713 dev_err(&client->adapter->dev,
714 "Slave registration failed: %s, %d\n",
715 devid->name, result);
716 goto out_free_memory;
717 }
718
719 return result;
720
721out_free_memory:
722 kfree(private_data);
723out_no_free:
724 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
725 return result;
726
727}
728
729static int bma250_mod_remove(struct i2c_client *client)
730{
731 struct bma250_mod_private_data *private_data =
732 i2c_get_clientdata(client);
733
734 dev_dbg(&client->adapter->dev, "%s\n", __func__);
735
736 inv_mpu_unregister_slave(client, private_data->pdata,
737 bma250_get_slave_descr);
738
739 kfree(private_data);
740 return 0;
741}
742
743static const struct i2c_device_id bma250_mod_id[] = {
744 { "bma250", ACCEL_ID_BMA250 },
745 {}
746};
747
748MODULE_DEVICE_TABLE(i2c, bma250_mod_id);
749
750static struct i2c_driver bma250_mod_driver = {
751 .class = I2C_CLASS_HWMON,
752 .probe = bma250_mod_probe,
753 .remove = bma250_mod_remove,
754 .id_table = bma250_mod_id,
755 .driver = {
756 .owner = THIS_MODULE,
757 .name = "bma250_mod",
758 },
759 .address_list = normal_i2c,
760};
761
762static int __init bma250_mod_init(void)
763{
764 int res = i2c_add_driver(&bma250_mod_driver);
765 pr_info("%s: Probe name %s\n", __func__, "bma250_mod");
766 if (res)
767 pr_err("%s failed\n", __func__);
768 return res;
769}
770
771static void __exit bma250_mod_exit(void)
772{
773 pr_info("%s\n", __func__);
774 i2c_del_driver(&bma250_mod_driver);
775}
776
777module_init(bma250_mod_init);
778module_exit(bma250_mod_exit);
779
780MODULE_AUTHOR("Invensense Corporation");
781MODULE_DESCRIPTION("Driver to integrate BMA250 sensor with the MPU");
782MODULE_LICENSE("GPL");
783MODULE_ALIAS("bma250_mod");
784
785/**
786 * @}
787 */
diff --git a/drivers/misc/inv_mpu/accel/cma3000.c b/drivers/misc/inv_mpu/accel/cma3000.c
new file mode 100644
index 00000000000..496d1f29bdc
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/cma3000.c
@@ -0,0 +1,222 @@
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 Accelerometer setup and handling methods for VTI CMA3000.
23 *
24 * @{
25 * @file cma3000.c
26 * @brief Accelerometer setup and handling methods for VTI CMA3000
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
49static int cma3000_suspend(void *mlsl_handle,
50 struct ext_slave_descr *slave,
51 struct ext_slave_platform_data *pdata)
52{
53 int result;
54 /* RAM reset */
55 result =
56 inv_serial_single_write(mlsl_handle, pdata->address, 0x1d, 0xcd);
57 return result;
58}
59
60static int cma3000_resume(void *mlsl_handle,
61 struct ext_slave_descr *slave,
62 struct ext_slave_platform_data *pdata)
63{
64
65 return INV_SUCCESS;
66}
67
68static int cma3000_read(void *mlsl_handle,
69 struct ext_slave_descr *slave,
70 struct ext_slave_platform_data *pdata,
71 unsigned char *data)
72{
73 int result;
74 result = inv_serial_read(mlsl_handle, pdata->address,
75 slave->reg, slave->len, data);
76 return result;
77}
78
79static struct ext_slave_descr cma3000_descr = {
80 .init = NULL,
81 .exit = NULL,
82 .suspend = cma3000_suspend,
83 .resume = cma3000_resume,
84 .read = cma3000_read,
85 .config = NULL,
86 .get_config = NULL,
87 .name = "cma3000",
88 .type = EXT_SLAVE_TYPE_ACCEL,
89 .id = ID_INVALID,
90 .read_reg = 0x06,
91 .read_len = 6,
92 .endian = EXT_SLAVE_LITTLE_ENDIAN,
93 .range = {2, 0},
94 .trigger = NULL,
95
96};
97
98static
99struct ext_slave_descr *cma3000_get_slave_descr(void)
100{
101 return &cma3000_descr;
102}
103
104/* -------------------------------------------------------------------------- */
105
106struct cma3000_mod_private_data {
107 struct i2c_client *client;
108 struct ext_slave_platform_data *pdata;
109};
110
111static unsigned short normal_i2c[] = { I2C_CLIENT_END };
112
113static int cma3000_mod_probe(struct i2c_client *client,
114 const struct i2c_device_id *devid)
115{
116 struct ext_slave_platform_data *pdata;
117 struct cma3000_mod_private_data *private_data;
118 int result = 0;
119
120 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
121
122 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
123 result = -ENODEV;
124 goto out_no_free;
125 }
126
127 pdata = client->dev.platform_data;
128 if (!pdata) {
129 dev_err(&client->adapter->dev,
130 "Missing platform data for slave %s\n", devid->name);
131 result = -EFAULT;
132 goto out_no_free;
133 }
134
135 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
136 if (!private_data) {
137 result = -ENOMEM;
138 goto out_no_free;
139 }
140
141 i2c_set_clientdata(client, private_data);
142 private_data->client = client;
143 private_data->pdata = pdata;
144
145 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
146 cma3000_get_slave_descr);
147 if (result) {
148 dev_err(&client->adapter->dev,
149 "Slave registration failed: %s, %d\n",
150 devid->name, result);
151 goto out_free_memory;
152 }
153
154 return result;
155
156out_free_memory:
157 kfree(private_data);
158out_no_free:
159 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
160 return result;
161
162}
163
164static int cma3000_mod_remove(struct i2c_client *client)
165{
166 struct cma3000_mod_private_data *private_data =
167 i2c_get_clientdata(client);
168
169 dev_dbg(&client->adapter->dev, "%s\n", __func__);
170
171 inv_mpu_unregister_slave(client, private_data->pdata,
172 cma3000_get_slave_descr);
173
174 kfree(private_data);
175 return 0;
176}
177
178static const struct i2c_device_id cma3000_mod_id[] = {
179 { "cma3000", ACCEL_ID_CMA3000 },
180 {}
181};
182
183MODULE_DEVICE_TABLE(i2c, cma3000_mod_id);
184
185static struct i2c_driver cma3000_mod_driver = {
186 .class = I2C_CLASS_HWMON,
187 .probe = cma3000_mod_probe,
188 .remove = cma3000_mod_remove,
189 .id_table = cma3000_mod_id,
190 .driver = {
191 .owner = THIS_MODULE,
192 .name = "cma3000_mod",
193 },
194 .address_list = normal_i2c,
195};
196
197static int __init cma3000_mod_init(void)
198{
199 int res = i2c_add_driver(&cma3000_mod_driver);
200 pr_info("%s: Probe name %s\n", __func__, "cma3000_mod");
201 if (res)
202 pr_err("%s failed\n", __func__);
203 return res;
204}
205
206static void __exit cma3000_mod_exit(void)
207{
208 pr_info("%s\n", __func__);
209 i2c_del_driver(&cma3000_mod_driver);
210}
211
212module_init(cma3000_mod_init);
213module_exit(cma3000_mod_exit);
214
215MODULE_AUTHOR("Invensense Corporation");
216MODULE_DESCRIPTION("Driver to integrate CMA3000 sensor with the MPU");
217MODULE_LICENSE("GPL");
218MODULE_ALIAS("cma3000_mod");
219
220/**
221 * @}
222 */
diff --git a/drivers/misc/inv_mpu/accel/kxsd9.c b/drivers/misc/inv_mpu/accel/kxsd9.c
new file mode 100644
index 00000000000..5cb4eaf6b4a
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/kxsd9.c
@@ -0,0 +1,264 @@
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 Accelerometer setup and handling methods for Kionix KXSD9.
23 *
24 * @{
25 * @file kxsd9.c
26 * @brief Accelerometer setup and handling methods for Kionix KXSD9.
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
49static int kxsd9_suspend(void *mlsl_handle,
50 struct ext_slave_descr *slave,
51 struct ext_slave_platform_data *pdata)
52{
53 int result;
54 /* CTRL_REGB: low-power standby mode */
55 result =
56 inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x0);
57 if (result) {
58 LOG_RESULT_LOCATION(result);
59 return result;
60 }
61 return result;
62}
63
64/* full scale setting - register and mask */
65#define ACCEL_KIONIX_CTRL_REG (0x0C)
66#define ACCEL_KIONIX_CTRL_MASK (0x3)
67
68static int kxsd9_resume(void *mlsl_handle,
69 struct ext_slave_descr *slave,
70 struct ext_slave_platform_data *pdata)
71{
72 int result = INV_SUCCESS;
73 unsigned char reg;
74
75 /* Full Scale */
76 reg = 0x0;
77 reg &= ~ACCEL_KIONIX_CTRL_MASK;
78 reg |= 0x00;
79 if (slave->range.mantissa == 4) { /* 4g scale = 4.9951 */
80 reg |= 0x2;
81 slave->range.fraction = 9951;
82 } else if (slave->range.mantissa == 7) { /* 6g scale = 7.5018 */
83 reg |= 0x1;
84 slave->range.fraction = 5018;
85 } else if (slave->range.mantissa == 9) { /* 8g scale = 9.9902 */
86 reg |= 0x0;
87 slave->range.fraction = 9902;
88 } else {
89 slave->range.mantissa = 2; /* 2g scale = 2.5006 */
90 slave->range.fraction = 5006;
91 reg |= 0x3;
92 }
93 reg |= 0xC0; /* 100Hz LPF */
94 result =
95 inv_serial_single_write(mlsl_handle, pdata->address,
96 ACCEL_KIONIX_CTRL_REG, reg);
97 if (result) {
98 LOG_RESULT_LOCATION(result);
99 return result;
100 }
101 /* normal operation */
102 result =
103 inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x40);
104 if (result) {
105 LOG_RESULT_LOCATION(result);
106 return result;
107 }
108
109 return INV_SUCCESS;
110}
111
112static int kxsd9_read(void *mlsl_handle,
113 struct ext_slave_descr *slave,
114 struct ext_slave_platform_data *pdata,
115 unsigned char *data)
116{
117 int result;
118 result = inv_serial_read(mlsl_handle, pdata->address,
119 slave->read_reg, slave->read_len, data);
120 return result;
121}
122
123static struct ext_slave_descr kxsd9_descr = {
124 .init = NULL,
125 .exit = NULL,
126 .suspend = kxsd9_suspend,
127 .resume = kxsd9_resume,
128 .read = kxsd9_read,
129 .config = NULL,
130 .get_config = NULL,
131 .name = "kxsd9",
132 .type = EXT_SLAVE_TYPE_ACCEL,
133 .id = ACCEL_ID_KXSD9,
134 .read_reg = 0x00,
135 .read_len = 6,
136 .endian = EXT_SLAVE_BIG_ENDIAN,
137 .range = {2, 5006},
138 .trigger = NULL,
139};
140
141static
142struct ext_slave_descr *kxsd9_get_slave_descr(void)
143{
144 return &kxsd9_descr;
145}
146
147/* -------------------------------------------------------------------------- */
148struct kxsd9_mod_private_data {
149 struct i2c_client *client;
150 struct ext_slave_platform_data *pdata;
151};
152
153static unsigned short normal_i2c[] = { I2C_CLIENT_END };
154
155static int kxsd9_mod_probe(struct i2c_client *client,
156 const struct i2c_device_id *devid)
157{
158 struct ext_slave_platform_data *pdata;
159 struct kxsd9_mod_private_data *private_data;
160 int result = 0;
161
162 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
163
164 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
165 result = -ENODEV;
166 goto out_no_free;
167 }
168
169 pdata = client->dev.platform_data;
170 if (!pdata) {
171 dev_err(&client->adapter->dev,
172 "Missing platform data for slave %s\n", devid->name);
173 result = -EFAULT;
174 goto out_no_free;
175 }
176
177 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
178 if (!private_data) {
179 result = -ENOMEM;
180 goto out_no_free;
181 }
182
183 i2c_set_clientdata(client, private_data);
184 private_data->client = client;
185 private_data->pdata = pdata;
186
187 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
188 kxsd9_get_slave_descr);
189 if (result) {
190 dev_err(&client->adapter->dev,
191 "Slave registration failed: %s, %d\n",
192 devid->name, result);
193 goto out_free_memory;
194 }
195
196 return result;
197
198out_free_memory:
199 kfree(private_data);
200out_no_free:
201 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
202 return result;
203
204}
205
206static int kxsd9_mod_remove(struct i2c_client *client)
207{
208 struct kxsd9_mod_private_data *private_data =
209 i2c_get_clientdata(client);
210
211 dev_dbg(&client->adapter->dev, "%s\n", __func__);
212
213 inv_mpu_unregister_slave(client, private_data->pdata,
214 kxsd9_get_slave_descr);
215
216 kfree(private_data);
217 return 0;
218}
219
220static const struct i2c_device_id kxsd9_mod_id[] = {
221 { "kxsd9", ACCEL_ID_KXSD9 },
222 {}
223};
224
225MODULE_DEVICE_TABLE(i2c, kxsd9_mod_id);
226
227static struct i2c_driver kxsd9_mod_driver = {
228 .class = I2C_CLASS_HWMON,
229 .probe = kxsd9_mod_probe,
230 .remove = kxsd9_mod_remove,
231 .id_table = kxsd9_mod_id,
232 .driver = {
233 .owner = THIS_MODULE,
234 .name = "kxsd9_mod",
235 },
236 .address_list = normal_i2c,
237};
238
239static int __init kxsd9_mod_init(void)
240{
241 int res = i2c_add_driver(&kxsd9_mod_driver);
242 pr_info("%s: Probe name %s\n", __func__, "kxsd9_mod");
243 if (res)
244 pr_err("%s failed\n", __func__);
245 return res;
246}
247
248static void __exit kxsd9_mod_exit(void)
249{
250 pr_info("%s\n", __func__);
251 i2c_del_driver(&kxsd9_mod_driver);
252}
253
254module_init(kxsd9_mod_init);
255module_exit(kxsd9_mod_exit);
256
257MODULE_AUTHOR("Invensense Corporation");
258MODULE_DESCRIPTION("Driver to integrate KXSD9 sensor with the MPU");
259MODULE_LICENSE("GPL");
260MODULE_ALIAS("kxsd9_mod");
261
262/**
263 * @}
264 */
diff --git a/drivers/misc/inv_mpu/accel/kxtf9.c b/drivers/misc/inv_mpu/accel/kxtf9.c
new file mode 100644
index 00000000000..80776f249c6
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/kxtf9.c
@@ -0,0 +1,841 @@
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 Accelerometer setup and handling methods for Kionix KXTF9.
23 *
24 * @{
25 * @file kxtf9.c
26 * @brief Accelerometer setup and handling methods for Kionix KXTF9.
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#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
51#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
52#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
53#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
54#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
55#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
56#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
57#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
58#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
59#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
60#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
61#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
62#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
63#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
64#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
65#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
66#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
67#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
68#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
69#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
70#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
71#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
72#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
73#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
74#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
75#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
76#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
77#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
78#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
79#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
80#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
81#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
82#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
83#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
84#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
85#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
86#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
87#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
88#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
89
90#define KXTF9_MAX_DUR (0xFF)
91#define KXTF9_MAX_THS (0xFF)
92#define KXTF9_THS_COUNTS_P_G (32)
93
94/* -------------------------------------------------------------------------- */
95
96struct kxtf9_config {
97 unsigned long odr; /* Output data rate mHz */
98 unsigned int fsr; /* full scale range mg */
99 unsigned int ths; /* Motion no-motion thseshold mg */
100 unsigned int dur; /* Motion no-motion duration ms */
101 unsigned int irq_type;
102 unsigned char reg_ths;
103 unsigned char reg_dur;
104 unsigned char reg_odr;
105 unsigned char reg_int_cfg1;
106 unsigned char reg_int_cfg2;
107 unsigned char ctrl_reg1;
108};
109
110struct kxtf9_private_data {
111 struct kxtf9_config suspend;
112 struct kxtf9_config resume;
113};
114
115static int kxtf9_set_ths(void *mlsl_handle,
116 struct ext_slave_platform_data *pdata,
117 struct kxtf9_config *config, int apply, long ths)
118{
119 int result = INV_SUCCESS;
120 if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS)
121 ths = (long)(KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G;
122
123 if (ths < 0)
124 ths = 0;
125
126 config->ths = ths;
127 config->reg_ths = (unsigned char)
128 ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000);
129 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
130 if (apply)
131 result = inv_serial_single_write(mlsl_handle, pdata->address,
132 KXTF9_WUF_THRESH,
133 config->reg_ths);
134 return result;
135}
136
137static int kxtf9_set_dur(void *mlsl_handle,
138 struct ext_slave_platform_data *pdata,
139 struct kxtf9_config *config, int apply, long dur)
140{
141 int result = INV_SUCCESS;
142 long reg_dur = (dur * config->odr) / 1000000L;
143 config->dur = dur;
144
145 if (reg_dur > KXTF9_MAX_DUR)
146 reg_dur = KXTF9_MAX_DUR;
147
148 config->reg_dur = (unsigned char)reg_dur;
149 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
150 if (apply)
151 result = inv_serial_single_write(mlsl_handle, pdata->address,
152 KXTF9_WUF_TIMER,
153 (unsigned char)reg_dur);
154 return result;
155}
156
157/**
158 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
159 * duration will not be used uless the type is MOT or NMOT.
160 *
161 * @param config configuration to apply to, suspend or resume
162 * @param irq_type The type of IRQ. Valid values are
163 * - MPU_SLAVE_IRQ_TYPE_NONE
164 * - MPU_SLAVE_IRQ_TYPE_MOTION
165 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
166 */
167static int kxtf9_set_irq(void *mlsl_handle,
168 struct ext_slave_platform_data *pdata,
169 struct kxtf9_config *config, int apply, long irq_type)
170{
171 int result = INV_SUCCESS;
172 struct kxtf9_private_data *private_data = pdata->private_data;
173
174 config->irq_type = (unsigned char)irq_type;
175 config->ctrl_reg1 &= ~0x22;
176 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
177 config->ctrl_reg1 |= 0x20;
178 config->reg_int_cfg1 = 0x38;
179 config->reg_int_cfg2 = 0x00;
180 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
181 config->ctrl_reg1 |= 0x02;
182 if ((unsigned long)config ==
183 (unsigned long)&private_data->suspend)
184 config->reg_int_cfg1 = 0x34;
185 else
186 config->reg_int_cfg1 = 0x24;
187 config->reg_int_cfg2 = 0xE0;
188 } else {
189 config->reg_int_cfg1 = 0x00;
190 config->reg_int_cfg2 = 0x00;
191 }
192
193 if (apply) {
194 /* Must clear bit 7 before writing new configuration */
195 result = inv_serial_single_write(mlsl_handle, pdata->address,
196 KXTF9_CTRL_REG1, 0x40);
197 result = inv_serial_single_write(mlsl_handle, pdata->address,
198 KXTF9_INT_CTRL_REG1,
199 config->reg_int_cfg1);
200 result = inv_serial_single_write(mlsl_handle, pdata->address,
201 KXTF9_INT_CTRL_REG2,
202 config->reg_int_cfg2);
203 result = inv_serial_single_write(mlsl_handle, pdata->address,
204 KXTF9_CTRL_REG1,
205 config->ctrl_reg1);
206 }
207 MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n",
208 (unsigned long)config->ctrl_reg1,
209 (unsigned long)config->reg_int_cfg1,
210 (unsigned long)config->reg_int_cfg2);
211
212 return result;
213}
214
215/**
216 * Set the Output data rate for the particular configuration
217 *
218 * @param config Config to modify with new ODR
219 * @param odr Output data rate in units of 1/1000Hz
220 */
221static int kxtf9_set_odr(void *mlsl_handle,
222 struct ext_slave_platform_data *pdata,
223 struct kxtf9_config *config, int apply, long odr)
224{
225 unsigned char bits;
226 int result = INV_SUCCESS;
227
228 /* Data sheet says there is 12.5 hz, but that seems to produce a single
229 * correct data value, thus we remove it from the table */
230 if (odr > 400000L) {
231 config->odr = 800000L;
232 bits = 0x06;
233 } else if (odr > 200000L) {
234 config->odr = 400000L;
235 bits = 0x05;
236 } else if (odr > 100000L) {
237 config->odr = 200000L;
238 bits = 0x04;
239 } else if (odr > 50000) {
240 config->odr = 100000L;
241 bits = 0x03;
242 } else if (odr > 25000) {
243 config->odr = 50000;
244 bits = 0x02;
245 } else if (odr != 0) {
246 config->odr = 25000;
247 bits = 0x01;
248 } else {
249 config->odr = 0;
250 bits = 0;
251 }
252
253 if (odr != 0)
254 config->ctrl_reg1 |= 0x80;
255 else
256 config->ctrl_reg1 &= ~0x80;
257
258 config->reg_odr = bits;
259 kxtf9_set_dur(mlsl_handle, pdata, config, apply, config->dur);
260 MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
261 if (apply) {
262 result = inv_serial_single_write(mlsl_handle, pdata->address,
263 KXTF9_DATA_CTRL_REG,
264 config->reg_odr);
265 result = inv_serial_single_write(mlsl_handle, pdata->address,
266 KXTF9_CTRL_REG1, 0x40);
267 result = inv_serial_single_write(mlsl_handle, pdata->address,
268 KXTF9_CTRL_REG1,
269 config->ctrl_reg1);
270 }
271 return result;
272}
273
274/**
275 * Set the full scale range of the accels
276 *
277 * @param config pointer to configuration
278 * @param fsr requested full scale range
279 */
280static int kxtf9_set_fsr(void *mlsl_handle,
281 struct ext_slave_platform_data *pdata,
282 struct kxtf9_config *config, int apply, long fsr)
283{
284 int result = INV_SUCCESS;
285
286 config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7);
287 if (fsr <= 2000) {
288 config->fsr = 2000;
289 config->ctrl_reg1 |= 0x00;
290 } else if (fsr <= 4000) {
291 config->fsr = 4000;
292 config->ctrl_reg1 |= 0x08;
293 } else {
294 config->fsr = 8000;
295 config->ctrl_reg1 |= 0x10;
296 }
297
298 MPL_LOGV("FSR: %d\n", config->fsr);
299 if (apply) {
300 /* Must clear bit 7 before writing new configuration */
301 result = inv_serial_single_write(mlsl_handle, pdata->address,
302 KXTF9_CTRL_REG1, 0x40);
303 result = inv_serial_single_write(mlsl_handle, pdata->address,
304 KXTF9_CTRL_REG1,
305 config->ctrl_reg1);
306 }
307 return result;
308}
309
310static int kxtf9_suspend(void *mlsl_handle,
311 struct ext_slave_descr *slave,
312 struct ext_slave_platform_data *pdata)
313{
314 int result;
315 unsigned char data;
316 struct kxtf9_private_data *private_data = pdata->private_data;
317
318 /* Wake up */
319 result = inv_serial_single_write(mlsl_handle, pdata->address,
320 KXTF9_CTRL_REG1, 0x40);
321 if (result) {
322 LOG_RESULT_LOCATION(result);
323 return result;
324 }
325 /* INT_CTRL_REG1: */
326 result = inv_serial_single_write(mlsl_handle, pdata->address,
327 KXTF9_INT_CTRL_REG1,
328 private_data->suspend.reg_int_cfg1);
329 if (result) {
330 LOG_RESULT_LOCATION(result);
331 return result;
332 }
333 /* WUF_THRESH: */
334 result = inv_serial_single_write(mlsl_handle, pdata->address,
335 KXTF9_WUF_THRESH,
336 private_data->suspend.reg_ths);
337 if (result) {
338 LOG_RESULT_LOCATION(result);
339 return result;
340 }
341 /* DATA_CTRL_REG */
342 result = inv_serial_single_write(mlsl_handle, pdata->address,
343 KXTF9_DATA_CTRL_REG,
344 private_data->suspend.reg_odr);
345 if (result) {
346 LOG_RESULT_LOCATION(result);
347 return result;
348 }
349 /* WUF_TIMER */
350 result = inv_serial_single_write(mlsl_handle, pdata->address,
351 KXTF9_WUF_TIMER,
352 private_data->suspend.reg_dur);
353 if (result) {
354 LOG_RESULT_LOCATION(result);
355 return result;
356 }
357
358 /* Normal operation */
359 result = inv_serial_single_write(mlsl_handle, pdata->address,
360 KXTF9_CTRL_REG1,
361 private_data->suspend.ctrl_reg1);
362 if (result) {
363 LOG_RESULT_LOCATION(result);
364 return result;
365 }
366 result = inv_serial_read(mlsl_handle, pdata->address,
367 KXTF9_INT_REL, 1, &data);
368 if (result) {
369 LOG_RESULT_LOCATION(result);
370 return result;
371 }
372
373 return result;
374}
375
376/* full scale setting - register and mask */
377#define ACCEL_KIONIX_CTRL_REG (0x1b)
378#define ACCEL_KIONIX_CTRL_MASK (0x18)
379
380static int kxtf9_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 data;
386 struct kxtf9_private_data *private_data = pdata->private_data;
387
388 /* Wake up */
389 result = inv_serial_single_write(mlsl_handle, pdata->address,
390 KXTF9_CTRL_REG1, 0x40);
391 if (result) {
392 LOG_RESULT_LOCATION(result);
393 return result;
394 }
395 /* INT_CTRL_REG1: */
396 result = inv_serial_single_write(mlsl_handle, pdata->address,
397 KXTF9_INT_CTRL_REG1,
398 private_data->resume.reg_int_cfg1);
399 if (result) {
400 LOG_RESULT_LOCATION(result);
401 return result;
402 }
403 /* WUF_THRESH: */
404 result = inv_serial_single_write(mlsl_handle, pdata->address,
405 KXTF9_WUF_THRESH,
406 private_data->resume.reg_ths);
407 if (result) {
408 LOG_RESULT_LOCATION(result);
409 return result;
410 }
411 /* DATA_CTRL_REG */
412 result = inv_serial_single_write(mlsl_handle, pdata->address,
413 KXTF9_DATA_CTRL_REG,
414 private_data->resume.reg_odr);
415 if (result) {
416 LOG_RESULT_LOCATION(result);
417 return result;
418 }
419 /* WUF_TIMER */
420 result = inv_serial_single_write(mlsl_handle, pdata->address,
421 KXTF9_WUF_TIMER,
422 private_data->resume.reg_dur);
423 if (result) {
424 LOG_RESULT_LOCATION(result);
425 return result;
426 }
427
428 /* Normal operation */
429 result = inv_serial_single_write(mlsl_handle, pdata->address,
430 KXTF9_CTRL_REG1,
431 private_data->resume.ctrl_reg1);
432 if (result) {
433 LOG_RESULT_LOCATION(result);
434 return result;
435 }
436 result = inv_serial_read(mlsl_handle, pdata->address,
437 KXTF9_INT_REL, 1, &data);
438 if (result) {
439 LOG_RESULT_LOCATION(result);
440 return result;
441 }
442
443 return INV_SUCCESS;
444}
445
446static int kxtf9_init(void *mlsl_handle,
447 struct ext_slave_descr *slave,
448 struct ext_slave_platform_data *pdata)
449{
450
451 struct kxtf9_private_data *private_data;
452 int result = INV_SUCCESS;
453
454 private_data = (struct kxtf9_private_data *)
455 kzalloc(sizeof(struct kxtf9_private_data), GFP_KERNEL);
456
457 if (!private_data)
458 return INV_ERROR_MEMORY_EXAUSTED;
459
460 /* RAM reset */
461 /* Fastest Reset */
462 result = inv_serial_single_write(mlsl_handle, pdata->address,
463 KXTF9_CTRL_REG1, 0x40);
464 if (result) {
465 LOG_RESULT_LOCATION(result);
466 return result;
467 }
468 /* Fastest Reset */
469 result = inv_serial_single_write(mlsl_handle, pdata->address,
470 KXTF9_DATA_CTRL_REG, 0x36);
471 if (result) {
472 LOG_RESULT_LOCATION(result);
473 return result;
474 }
475 /* Reset */
476 result = inv_serial_single_write(mlsl_handle, pdata->address,
477 KXTF9_CTRL_REG3, 0xcd);
478 if (result) {
479 LOG_RESULT_LOCATION(result);
480 return result;
481 }
482 msleep(2);
483
484 pdata->private_data = private_data;
485
486 private_data->resume.ctrl_reg1 = 0xC0;
487 private_data->suspend.ctrl_reg1 = 0x40;
488
489 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend,
490 false, 1000);
491 if (result) {
492 LOG_RESULT_LOCATION(result);
493 return result;
494 }
495 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume,
496 false, 2540);
497 if (result) {
498 LOG_RESULT_LOCATION(result);
499 return result;
500 }
501
502 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend,
503 false, 50000);
504 if (result) {
505 LOG_RESULT_LOCATION(result);
506 return result;
507 }
508 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume,
509 false, 200000L);
510
511 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend,
512 false, 2000);
513 if (result) {
514 LOG_RESULT_LOCATION(result);
515 return result;
516 }
517 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume,
518 false, 2000);
519 if (result) {
520 LOG_RESULT_LOCATION(result);
521 return result;
522 }
523
524 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend,
525 false, 80);
526 if (result) {
527 LOG_RESULT_LOCATION(result);
528 return result;
529 }
530 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume,
531 false, 40);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536
537 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend,
538 false, MPU_SLAVE_IRQ_TYPE_NONE);
539 if (result) {
540 LOG_RESULT_LOCATION(result);
541 return result;
542 }
543 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume,
544 false, MPU_SLAVE_IRQ_TYPE_NONE);
545 if (result) {
546 LOG_RESULT_LOCATION(result);
547 return result;
548 }
549 return result;
550}
551
552static int kxtf9_exit(void *mlsl_handle,
553 struct ext_slave_descr *slave,
554 struct ext_slave_platform_data *pdata)
555{
556 kfree(pdata->private_data);
557 return INV_SUCCESS;
558}
559
560static int kxtf9_config(void *mlsl_handle,
561 struct ext_slave_descr *slave,
562 struct ext_slave_platform_data *pdata,
563 struct ext_slave_config *data)
564{
565 struct kxtf9_private_data *private_data = pdata->private_data;
566 if (!data->data)
567 return INV_ERROR_INVALID_PARAMETER;
568
569 switch (data->key) {
570 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
571 return kxtf9_set_odr(mlsl_handle, pdata,
572 &private_data->suspend,
573 data->apply, *((long *)data->data));
574 case MPU_SLAVE_CONFIG_ODR_RESUME:
575 return kxtf9_set_odr(mlsl_handle, pdata,
576 &private_data->resume,
577 data->apply, *((long *)data->data));
578 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
579 return kxtf9_set_fsr(mlsl_handle, pdata,
580 &private_data->suspend,
581 data->apply, *((long *)data->data));
582 case MPU_SLAVE_CONFIG_FSR_RESUME:
583 return kxtf9_set_fsr(mlsl_handle, pdata,
584 &private_data->resume,
585 data->apply, *((long *)data->data));
586 case MPU_SLAVE_CONFIG_MOT_THS:
587 return kxtf9_set_ths(mlsl_handle, pdata,
588 &private_data->suspend,
589 data->apply, *((long *)data->data));
590 case MPU_SLAVE_CONFIG_NMOT_THS:
591 return kxtf9_set_ths(mlsl_handle, pdata,
592 &private_data->resume,
593 data->apply, *((long *)data->data));
594 case MPU_SLAVE_CONFIG_MOT_DUR:
595 return kxtf9_set_dur(mlsl_handle, pdata,
596 &private_data->suspend,
597 data->apply, *((long *)data->data));
598 case MPU_SLAVE_CONFIG_NMOT_DUR:
599 return kxtf9_set_dur(mlsl_handle, pdata,
600 &private_data->resume,
601 data->apply, *((long *)data->data));
602 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
603 return kxtf9_set_irq(mlsl_handle, pdata,
604 &private_data->suspend,
605 data->apply, *((long *)data->data));
606 case MPU_SLAVE_CONFIG_IRQ_RESUME:
607 return kxtf9_set_irq(mlsl_handle, pdata,
608 &private_data->resume,
609 data->apply, *((long *)data->data));
610 default:
611 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
612 };
613
614 return INV_SUCCESS;
615}
616
617static int kxtf9_get_config(void *mlsl_handle,
618 struct ext_slave_descr *slave,
619 struct ext_slave_platform_data *pdata,
620 struct ext_slave_config *data)
621{
622 struct kxtf9_private_data *private_data = pdata->private_data;
623 if (!data->data)
624 return INV_ERROR_INVALID_PARAMETER;
625
626 switch (data->key) {
627 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
628 (*(unsigned long *)data->data) =
629 (unsigned long)private_data->suspend.odr;
630 break;
631 case MPU_SLAVE_CONFIG_ODR_RESUME:
632 (*(unsigned long *)data->data) =
633 (unsigned long)private_data->resume.odr;
634 break;
635 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
636 (*(unsigned long *)data->data) =
637 (unsigned long)private_data->suspend.fsr;
638 break;
639 case MPU_SLAVE_CONFIG_FSR_RESUME:
640 (*(unsigned long *)data->data) =
641 (unsigned long)private_data->resume.fsr;
642 break;
643 case MPU_SLAVE_CONFIG_MOT_THS:
644 (*(unsigned long *)data->data) =
645 (unsigned long)private_data->suspend.ths;
646 break;
647 case MPU_SLAVE_CONFIG_NMOT_THS:
648 (*(unsigned long *)data->data) =
649 (unsigned long)private_data->resume.ths;
650 break;
651 case MPU_SLAVE_CONFIG_MOT_DUR:
652 (*(unsigned long *)data->data) =
653 (unsigned long)private_data->suspend.dur;
654 break;
655 case MPU_SLAVE_CONFIG_NMOT_DUR:
656 (*(unsigned long *)data->data) =
657 (unsigned long)private_data->resume.dur;
658 break;
659 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
660 (*(unsigned long *)data->data) =
661 (unsigned long)private_data->suspend.irq_type;
662 break;
663 case MPU_SLAVE_CONFIG_IRQ_RESUME:
664 (*(unsigned long *)data->data) =
665 (unsigned long)private_data->resume.irq_type;
666 break;
667 default:
668 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
669 };
670
671 return INV_SUCCESS;
672}
673
674static int kxtf9_read(void *mlsl_handle,
675 struct ext_slave_descr *slave,
676 struct ext_slave_platform_data *pdata,
677 unsigned char *data)
678{
679 int result;
680 unsigned char reg;
681 result = inv_serial_read(mlsl_handle, pdata->address,
682 KXTF9_INT_SRC_REG2, 1, &reg);
683 if (result) {
684 LOG_RESULT_LOCATION(result);
685 return result;
686 }
687
688 if (!(reg & 0x10))
689 return INV_ERROR_ACCEL_DATA_NOT_READY;
690
691 result = inv_serial_read(mlsl_handle, pdata->address,
692 slave->read_reg, slave->read_len, data);
693 if (result) {
694 LOG_RESULT_LOCATION(result);
695 return result;
696 }
697 return result;
698}
699
700static struct ext_slave_descr kxtf9_descr = {
701 .init = kxtf9_init,
702 .exit = kxtf9_exit,
703 .suspend = kxtf9_suspend,
704 .resume = kxtf9_resume,
705 .read = kxtf9_read,
706 .config = kxtf9_config,
707 .get_config = kxtf9_get_config,
708 .name = "kxtf9",
709 .type = EXT_SLAVE_TYPE_ACCEL,
710 .id = ACCEL_ID_KXTF9,
711 .read_reg = 0x06,
712 .read_len = 6,
713 .endian = EXT_SLAVE_LITTLE_ENDIAN,
714 .range = {2, 0},
715 .trigger = NULL,
716};
717
718static
719struct ext_slave_descr *kxtf9_get_slave_descr(void)
720{
721 return &kxtf9_descr;
722}
723
724/* -------------------------------------------------------------------------- */
725struct kxtf9_mod_private_data {
726 struct i2c_client *client;
727 struct ext_slave_platform_data *pdata;
728};
729
730static unsigned short normal_i2c[] = { I2C_CLIENT_END };
731
732static int kxtf9_mod_probe(struct i2c_client *client,
733 const struct i2c_device_id *devid)
734{
735 struct ext_slave_platform_data *pdata;
736 struct kxtf9_mod_private_data *private_data;
737 int result = 0;
738
739 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
740
741 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
742 result = -ENODEV;
743 goto out_no_free;
744 }
745
746 pdata = client->dev.platform_data;
747 if (!pdata) {
748 dev_err(&client->adapter->dev,
749 "Missing platform data for slave %s\n", devid->name);
750 result = -EFAULT;
751 goto out_no_free;
752 }
753
754 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
755 if (!private_data) {
756 result = -ENOMEM;
757 goto out_no_free;
758 }
759
760 i2c_set_clientdata(client, private_data);
761 private_data->client = client;
762 private_data->pdata = pdata;
763
764 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
765 kxtf9_get_slave_descr);
766 if (result) {
767 dev_err(&client->adapter->dev,
768 "Slave registration failed: %s, %d\n",
769 devid->name, result);
770 goto out_free_memory;
771 }
772
773 return result;
774
775out_free_memory:
776 kfree(private_data);
777out_no_free:
778 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
779 return result;
780
781}
782
783static int kxtf9_mod_remove(struct i2c_client *client)
784{
785 struct kxtf9_mod_private_data *private_data =
786 i2c_get_clientdata(client);
787
788 dev_dbg(&client->adapter->dev, "%s\n", __func__);
789
790 inv_mpu_unregister_slave(client, private_data->pdata,
791 kxtf9_get_slave_descr);
792
793 kfree(private_data);
794 return 0;
795}
796
797static const struct i2c_device_id kxtf9_mod_id[] = {
798 { "kxtf9", ACCEL_ID_KXTF9 },
799 {}
800};
801
802MODULE_DEVICE_TABLE(i2c, kxtf9_mod_id);
803
804static struct i2c_driver kxtf9_mod_driver = {
805 .class = I2C_CLASS_HWMON,
806 .probe = kxtf9_mod_probe,
807 .remove = kxtf9_mod_remove,
808 .id_table = kxtf9_mod_id,
809 .driver = {
810 .owner = THIS_MODULE,
811 .name = "kxtf9_mod",
812 },
813 .address_list = normal_i2c,
814};
815
816static int __init kxtf9_mod_init(void)
817{
818 int res = i2c_add_driver(&kxtf9_mod_driver);
819 pr_info("%s: Probe name %s\n", __func__, "kxtf9_mod");
820 if (res)
821 pr_err("%s failed\n", __func__);
822 return res;
823}
824
825static void __exit kxtf9_mod_exit(void)
826{
827 pr_info("%s\n", __func__);
828 i2c_del_driver(&kxtf9_mod_driver);
829}
830
831module_init(kxtf9_mod_init);
832module_exit(kxtf9_mod_exit);
833
834MODULE_AUTHOR("Invensense Corporation");
835MODULE_DESCRIPTION("Driver to integrate KXTF9 sensor with the MPU");
836MODULE_LICENSE("GPL");
837MODULE_ALIAS("kxtf9_mod");
838
839/**
840 * @}
841 */
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 */
diff --git a/drivers/misc/inv_mpu/accel/lis3dh.c b/drivers/misc/inv_mpu/accel/lis3dh.c
new file mode 100644
index 00000000000..27206e4b847
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/lis3dh.c
@@ -0,0 +1,728 @@
1/*
2 $License:
3 Copyright (C) 2011 InvenSense Corporation, All Rights Reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 $
18 */
19
20/**
21 * @addtogroup ACCELDL
22 * @brief Provides the interface to setup and handle an accelerometer.
23 *
24 * @{
25 * @file lis3dh.c
26 * @brief Accelerometer setup and handling methods for ST LIS3DH.
27 */
28
29/* -------------------------------------------------------------------------- */
30
31#undef MPL_LOG_NDEBUG
32#define MPL_LOG_NDEBUG 0
33
34#include <linux/i2c.h>
35#include <linux/module.h>
36#include <linux/moduleparam.h>
37#include <linux/kernel.h>
38#include <linux/errno.h>
39#include <linux/slab.h>
40#include <linux/delay.h>
41#include "mpu-dev.h"
42
43#include <log.h>
44#include <linux/mpu.h>
45#include "mlsl.h"
46#include "mldl_cfg.h"
47#undef MPL_LOG_TAG
48#define MPL_LOG_TAG "MPL-acc"
49
50/* full scale setting - register & mask */
51#define LIS3DH_CTRL_REG1 (0x20)
52#define LIS3DH_CTRL_REG2 (0x21)
53#define LIS3DH_CTRL_REG3 (0x22)
54#define LIS3DH_CTRL_REG4 (0x23)
55#define LIS3DH_CTRL_REG5 (0x24)
56#define LIS3DH_CTRL_REG6 (0x25)
57#define LIS3DH_REFERENCE (0x26)
58#define LIS3DH_STATUS_REG (0x27)
59#define LIS3DH_OUT_X_L (0x28)
60#define LIS3DH_OUT_X_H (0x29)
61#define LIS3DH_OUT_Y_L (0x2a)
62#define LIS3DH_OUT_Y_H (0x2b)
63#define LIS3DH_OUT_Z_L (0x2c)
64#define LIS3DH_OUT_Z_H (0x2d)
65
66#define LIS3DH_INT1_CFG (0x30)
67#define LIS3DH_INT1_SRC (0x31)
68#define LIS3DH_INT1_THS (0x32)
69#define LIS3DH_INT1_DURATION (0x33)
70
71#define LIS3DH_MAX_DUR (0x7F)
72
73/* -------------------------------------------------------------------------- */
74
75struct lis3dh_config {
76 unsigned long odr;
77 unsigned int fsr; /* full scale range mg */
78 unsigned int ths; /* Motion no-motion thseshold mg */
79 unsigned int dur; /* Motion no-motion duration ms */
80 unsigned char reg_ths;
81 unsigned char reg_dur;
82 unsigned char ctrl_reg1;
83 unsigned char irq_type;
84 unsigned char mot_int1_cfg;
85};
86
87struct lis3dh_private_data {
88 struct lis3dh_config suspend;
89 struct lis3dh_config resume;
90};
91
92/* -------------------------------------------------------------------------- */
93
94static int lis3dh_set_ths(void *mlsl_handle,
95 struct ext_slave_platform_data *pdata,
96 struct lis3dh_config *config, int apply, long ths)
97{
98 int result = INV_SUCCESS;
99 if ((unsigned int)ths > 1000 * config->fsr)
100 ths = (long)1000 * config->fsr;
101
102 if (ths < 0)
103 ths = 0;
104
105 config->ths = ths;
106 config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
107 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
108 if (apply)
109 result = inv_serial_single_write(mlsl_handle, pdata->address,
110 LIS3DH_INT1_THS,
111 config->reg_ths);
112 return result;
113}
114
115static int lis3dh_set_dur(void *mlsl_handle,
116 struct ext_slave_platform_data *pdata,
117 struct lis3dh_config *config, int apply, long dur)
118{
119 int result = INV_SUCCESS;
120 long reg_dur = (dur * config->odr) / 1000000L;
121 config->dur = dur;
122
123 if (reg_dur > LIS3DH_MAX_DUR)
124 reg_dur = LIS3DH_MAX_DUR;
125
126 config->reg_dur = (unsigned char)reg_dur;
127 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
128 if (apply)
129 result = inv_serial_single_write(mlsl_handle, pdata->address,
130 LIS3DH_INT1_DURATION,
131 (unsigned char)reg_dur);
132 return result;
133}
134
135/**
136 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
137 * duration will not be used uless the type is MOT or NMOT.
138 *
139 * @param config configuration to apply to, suspend or resume
140 * @param irq_type The type of IRQ. Valid values are
141 * - MPU_SLAVE_IRQ_TYPE_NONE
142 * - MPU_SLAVE_IRQ_TYPE_MOTION
143 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
144 */
145static int lis3dh_set_irq(void *mlsl_handle,
146 struct ext_slave_platform_data *pdata,
147 struct lis3dh_config *config,
148 int apply, long irq_type)
149{
150 int result = INV_SUCCESS;
151 unsigned char reg1;
152 unsigned char reg2;
153
154 config->irq_type = (unsigned char)irq_type;
155 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
156 reg1 = 0x10;
157 reg2 = 0x00;
158 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
159 reg1 = 0x40;
160 reg2 = config->mot_int1_cfg;
161 } else {
162 reg1 = 0x00;
163 reg2 = 0x00;
164 }
165
166 if (apply) {
167 result = inv_serial_single_write(mlsl_handle, pdata->address,
168 LIS3DH_CTRL_REG3, reg1);
169 result = inv_serial_single_write(mlsl_handle, pdata->address,
170 LIS3DH_INT1_CFG, reg2);
171 }
172
173 return result;
174}
175
176/**
177 * Set the Output data rate for the particular configuration
178 *
179 * @param config Config to modify with new ODR
180 * @param odr Output data rate in units of 1/1000Hz
181 */
182static int lis3dh_set_odr(void *mlsl_handle,
183 struct ext_slave_platform_data *pdata,
184 struct lis3dh_config *config, int apply, long odr)
185{
186 unsigned char bits;
187 int result = INV_SUCCESS;
188
189 if (odr > 400000L) {
190 config->odr = 1250000L;
191 bits = 0x90;
192 } else if (odr > 200000L) {
193 config->odr = 400000L;
194 bits = 0x70;
195 } else if (odr > 100000L) {
196 config->odr = 200000L;
197 bits = 0x60;
198 } else if (odr > 50000) {
199 config->odr = 100000L;
200 bits = 0x50;
201 } else if (odr > 25000) {
202 config->odr = 50000;
203 bits = 0x40;
204 } else if (odr > 10000) {
205 config->odr = 25000;
206 bits = 0x30;
207 } else if (odr > 1000) {
208 config->odr = 10000;
209 bits = 0x20;
210 } else if (odr > 500) {
211 config->odr = 1000;
212 bits = 0x10;
213 } else {
214 config->odr = 0;
215 bits = 0;
216 }
217
218 config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xf);
219 lis3dh_set_dur(mlsl_handle, pdata, config, apply, config->dur);
220 MPL_LOGV("ODR: %ld, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
221 if (apply)
222 result = inv_serial_single_write(mlsl_handle, pdata->address,
223 LIS3DH_CTRL_REG1,
224 config->ctrl_reg1);
225 return result;
226}
227
228/**
229 * Set the full scale range of the accels
230 *
231 * @param config pointer to configuration
232 * @param fsr requested full scale range
233 */
234static int lis3dh_set_fsr(void *mlsl_handle,
235 struct ext_slave_platform_data *pdata,
236 struct lis3dh_config *config, int apply, long fsr)
237{
238 int result = INV_SUCCESS;
239 unsigned char reg1 = 0x48;
240
241 if (fsr <= 2048) {
242 config->fsr = 2048;
243 } else if (fsr <= 4096) {
244 reg1 |= 0x10;
245 config->fsr = 4096;
246 } else if (fsr <= 8192) {
247 reg1 |= 0x20;
248 config->fsr = 8192;
249 } else {
250 reg1 |= 0x30;
251 config->fsr = 16348;
252 }
253
254 lis3dh_set_ths(mlsl_handle, pdata, config, apply, config->ths);
255 MPL_LOGV("FSR: %d\n", config->fsr);
256 if (apply)
257 result = inv_serial_single_write(mlsl_handle, pdata->address,
258 LIS3DH_CTRL_REG4, reg1);
259
260 return result;
261}
262
263static int lis3dh_suspend(void *mlsl_handle,
264 struct ext_slave_descr *slave,
265 struct ext_slave_platform_data *pdata)
266{
267 int result = INV_SUCCESS;
268 unsigned char reg1;
269 unsigned char reg2;
270 struct lis3dh_private_data *private_data = pdata->private_data;
271
272 result = inv_serial_single_write(mlsl_handle, pdata->address,
273 LIS3DH_CTRL_REG1,
274 private_data->suspend.ctrl_reg1);
275
276 reg1 = 0x48;
277 if (private_data->suspend.fsr == 16384)
278 reg1 |= 0x30;
279 else if (private_data->suspend.fsr == 8192)
280 reg1 |= 0x20;
281 else if (private_data->suspend.fsr == 4096)
282 reg1 |= 0x10;
283 else if (private_data->suspend.fsr == 2048)
284 reg1 |= 0x00;
285
286 result = inv_serial_single_write(mlsl_handle, pdata->address,
287 LIS3DH_CTRL_REG4, reg1);
288 result = inv_serial_single_write(mlsl_handle, pdata->address,
289 LIS3DH_INT1_THS,
290 private_data->suspend.reg_ths);
291 result = inv_serial_single_write(mlsl_handle, pdata->address,
292 LIS3DH_INT1_DURATION,
293 private_data->suspend.reg_dur);
294
295 if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
296 reg1 = 0x10;
297 reg2 = 0x00;
298 } else if (private_data->suspend.irq_type ==
299 MPU_SLAVE_IRQ_TYPE_MOTION) {
300 reg1 = 0x40;
301 reg2 = private_data->suspend.mot_int1_cfg;
302 } else {
303 reg1 = 0x00;
304 reg2 = 0x00;
305 }
306 result = inv_serial_single_write(mlsl_handle, pdata->address,
307 LIS3DH_CTRL_REG3, reg1);
308 result = inv_serial_single_write(mlsl_handle, pdata->address,
309 LIS3DH_INT1_CFG, reg2);
310 result = inv_serial_read(mlsl_handle, pdata->address,
311 LIS3DH_CTRL_REG6, 1, &reg1);
312
313 return result;
314}
315
316static int lis3dh_resume(void *mlsl_handle,
317 struct ext_slave_descr *slave,
318 struct ext_slave_platform_data *pdata)
319{
320 int result;
321 unsigned char reg1;
322 unsigned char reg2;
323 struct lis3dh_private_data *private_data = pdata->private_data;
324
325 result = inv_serial_single_write(mlsl_handle, pdata->address,
326 LIS3DH_CTRL_REG1,
327 private_data->resume.ctrl_reg1);
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332 msleep(6);
333
334 /* Full Scale */
335 reg1 = 0x48;
336 if (private_data->suspend.fsr == 16384)
337 reg1 |= 0x30;
338 else if (private_data->suspend.fsr == 8192)
339 reg1 |= 0x20;
340 else if (private_data->suspend.fsr == 4096)
341 reg1 |= 0x10;
342 else if (private_data->suspend.fsr == 2048)
343 reg1 |= 0x00;
344
345 result = inv_serial_single_write(mlsl_handle, pdata->address,
346 LIS3DH_CTRL_REG4, reg1);
347 if (result) {
348 LOG_RESULT_LOCATION(result);
349 return result;
350 }
351
352 if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
353 reg1 = 0x10;
354 reg2 = 0x00;
355 } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
356 reg1 = 0x40;
357 reg2 = private_data->resume.mot_int1_cfg;
358 } else {
359 reg1 = 0x00;
360 reg2 = 0x00;
361 }
362 result = inv_serial_single_write(mlsl_handle, pdata->address,
363 LIS3DH_CTRL_REG3, reg1);
364 if (result) {
365 LOG_RESULT_LOCATION(result);
366 return result;
367 }
368 result = inv_serial_single_write(mlsl_handle, pdata->address,
369 LIS3DH_INT1_THS,
370 private_data->resume.reg_ths);
371 if (result) {
372 LOG_RESULT_LOCATION(result);
373 return result;
374 }
375 result = inv_serial_single_write(mlsl_handle, pdata->address,
376 LIS3DH_INT1_DURATION,
377 private_data->resume.reg_dur);
378 if (result) {
379 LOG_RESULT_LOCATION(result);
380 return result;
381 }
382 result = inv_serial_single_write(mlsl_handle, pdata->address,
383 LIS3DH_INT1_CFG, reg2);
384 if (result) {
385 LOG_RESULT_LOCATION(result);
386 return result;
387 }
388 result = inv_serial_read(mlsl_handle, pdata->address,
389 LIS3DH_CTRL_REG6, 1, &reg1);
390 if (result) {
391 LOG_RESULT_LOCATION(result);
392 return result;
393 }
394 return result;
395}
396
397static int lis3dh_read(void *mlsl_handle,
398 struct ext_slave_descr *slave,
399 struct ext_slave_platform_data *pdata,
400 unsigned char *data)
401{
402 int result = INV_SUCCESS;
403 result = inv_serial_read(mlsl_handle, pdata->address,
404 LIS3DH_STATUS_REG, 1, data);
405 if (data[0] & 0x0F) {
406 result = inv_serial_read(mlsl_handle, pdata->address,
407 slave->read_reg, slave->read_len,
408 data);
409 return result;
410 } else
411 return INV_ERROR_ACCEL_DATA_NOT_READY;
412}
413
414static int lis3dh_init(void *mlsl_handle,
415 struct ext_slave_descr *slave,
416 struct ext_slave_platform_data *pdata)
417{
418 int result;
419 long range;
420 struct lis3dh_private_data *private_data;
421 private_data = (struct lis3dh_private_data *)
422 kzalloc(sizeof(struct lis3dh_private_data), GFP_KERNEL);
423
424 if (!private_data)
425 return INV_ERROR_MEMORY_EXAUSTED;
426
427 pdata->private_data = private_data;
428
429 private_data->resume.ctrl_reg1 = 0x67;
430 private_data->suspend.ctrl_reg1 = 0x18;
431 private_data->resume.mot_int1_cfg = 0x95;
432 private_data->suspend.mot_int1_cfg = 0x2a;
433
434 lis3dh_set_odr(mlsl_handle, pdata, &private_data->suspend, false, 0);
435 lis3dh_set_odr(mlsl_handle, pdata, &private_data->resume,
436 false, 200000L);
437
438 range = range_fixedpoint_to_long_mg(slave->range);
439 lis3dh_set_fsr(mlsl_handle, pdata, &private_data->suspend,
440 false, range);
441 lis3dh_set_fsr(mlsl_handle, pdata, &private_data->resume,
442 false, range);
443
444 lis3dh_set_ths(mlsl_handle, pdata, &private_data->suspend,
445 false, 80);
446 lis3dh_set_ths(mlsl_handle, pdata, &private_data->resume,
447 false, 40);
448
449 lis3dh_set_dur(mlsl_handle, pdata, &private_data->suspend,
450 false, 1000);
451 lis3dh_set_dur(mlsl_handle, pdata, &private_data->resume,
452 false, 2540);
453
454 lis3dh_set_irq(mlsl_handle, pdata, &private_data->suspend,
455 false, MPU_SLAVE_IRQ_TYPE_NONE);
456 lis3dh_set_irq(mlsl_handle, pdata, &private_data->resume,
457 false, MPU_SLAVE_IRQ_TYPE_NONE);
458
459 result = inv_serial_single_write(mlsl_handle, pdata->address,
460 LIS3DH_CTRL_REG1, 0x07);
461 msleep(6);
462
463 return INV_SUCCESS;
464}
465
466static int lis3dh_exit(void *mlsl_handle,
467 struct ext_slave_descr *slave,
468 struct ext_slave_platform_data *pdata)
469{
470 kfree(pdata->private_data);
471 return INV_SUCCESS;
472}
473
474static int lis3dh_config(void *mlsl_handle,
475 struct ext_slave_descr *slave,
476 struct ext_slave_platform_data *pdata,
477 struct ext_slave_config *data)
478{
479 struct lis3dh_private_data *private_data = pdata->private_data;
480 if (!data->data)
481 return INV_ERROR_INVALID_PARAMETER;
482
483 switch (data->key) {
484 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
485 return lis3dh_set_odr(mlsl_handle, pdata,
486 &private_data->suspend,
487 data->apply, *((long *)data->data));
488 case MPU_SLAVE_CONFIG_ODR_RESUME:
489 return lis3dh_set_odr(mlsl_handle, pdata,
490 &private_data->resume,
491 data->apply, *((long *)data->data));
492 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
493 return lis3dh_set_fsr(mlsl_handle, pdata,
494 &private_data->suspend,
495 data->apply, *((long *)data->data));
496 case MPU_SLAVE_CONFIG_FSR_RESUME:
497 return lis3dh_set_fsr(mlsl_handle, pdata,
498 &private_data->resume,
499 data->apply, *((long *)data->data));
500 case MPU_SLAVE_CONFIG_MOT_THS:
501 return lis3dh_set_ths(mlsl_handle, pdata,
502 &private_data->suspend,
503 data->apply, *((long *)data->data));
504 case MPU_SLAVE_CONFIG_NMOT_THS:
505 return lis3dh_set_ths(mlsl_handle, pdata,
506 &private_data->resume,
507 data->apply, *((long *)data->data));
508 case MPU_SLAVE_CONFIG_MOT_DUR:
509 return lis3dh_set_dur(mlsl_handle, pdata,
510 &private_data->suspend,
511 data->apply, *((long *)data->data));
512 case MPU_SLAVE_CONFIG_NMOT_DUR:
513 return lis3dh_set_dur(mlsl_handle, pdata,
514 &private_data->resume,
515 data->apply, *((long *)data->data));
516 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
517 return lis3dh_set_irq(mlsl_handle, pdata,
518 &private_data->suspend,
519 data->apply, *((long *)data->data));
520 case MPU_SLAVE_CONFIG_IRQ_RESUME:
521 return lis3dh_set_irq(mlsl_handle, pdata,
522 &private_data->resume,
523 data->apply, *((long *)data->data));
524 default:
525 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
526 };
527 return INV_SUCCESS;
528}
529
530static int lis3dh_get_config(void *mlsl_handle,
531 struct ext_slave_descr *slave,
532 struct ext_slave_platform_data *pdata,
533 struct ext_slave_config *data)
534{
535 struct lis3dh_private_data *private_data = pdata->private_data;
536 if (!data->data)
537 return INV_ERROR_INVALID_PARAMETER;
538
539 switch (data->key) {
540 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
541 (*(unsigned long *)data->data) =
542 (unsigned long)private_data->suspend.odr;
543 break;
544 case MPU_SLAVE_CONFIG_ODR_RESUME:
545 (*(unsigned long *)data->data) =
546 (unsigned long)private_data->resume.odr;
547 break;
548 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
549 (*(unsigned long *)data->data) =
550 (unsigned long)private_data->suspend.fsr;
551 break;
552 case MPU_SLAVE_CONFIG_FSR_RESUME:
553 (*(unsigned long *)data->data) =
554 (unsigned long)private_data->resume.fsr;
555 break;
556 case MPU_SLAVE_CONFIG_MOT_THS:
557 (*(unsigned long *)data->data) =
558 (unsigned long)private_data->suspend.ths;
559 break;
560 case MPU_SLAVE_CONFIG_NMOT_THS:
561 (*(unsigned long *)data->data) =
562 (unsigned long)private_data->resume.ths;
563 break;
564 case MPU_SLAVE_CONFIG_MOT_DUR:
565 (*(unsigned long *)data->data) =
566 (unsigned long)private_data->suspend.dur;
567 break;
568 case MPU_SLAVE_CONFIG_NMOT_DUR:
569 (*(unsigned long *)data->data) =
570 (unsigned long)private_data->resume.dur;
571 break;
572 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
573 (*(unsigned long *)data->data) =
574 (unsigned long)private_data->suspend.irq_type;
575 break;
576 case MPU_SLAVE_CONFIG_IRQ_RESUME:
577 (*(unsigned long *)data->data) =
578 (unsigned long)private_data->resume.irq_type;
579 break;
580 default:
581 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
582 };
583
584 return INV_SUCCESS;
585}
586
587static struct ext_slave_descr lis3dh_descr = {
588 .init = lis3dh_init,
589 .exit = lis3dh_exit,
590 .suspend = lis3dh_suspend,
591 .resume = lis3dh_resume,
592 .read = lis3dh_read,
593 .config = lis3dh_config,
594 .get_config = lis3dh_get_config,
595 .name = "lis3dh",
596 .type = EXT_SLAVE_TYPE_ACCEL,
597 .id = ACCEL_ID_LIS3DH,
598 .read_reg = 0x28 | 0x80, /* 0x80 for burst reads */
599 .read_len = 6,
600 .endian = EXT_SLAVE_BIG_ENDIAN,
601 .range = {2, 480},
602 .trigger = NULL,
603};
604
605static
606struct ext_slave_descr *lis3dh_get_slave_descr(void)
607{
608 return &lis3dh_descr;
609}
610
611/* -------------------------------------------------------------------------- */
612struct lis3dh_mod_private_data {
613 struct i2c_client *client;
614 struct ext_slave_platform_data *pdata;
615};
616
617static unsigned short normal_i2c[] = { I2C_CLIENT_END };
618
619static int lis3dh_mod_probe(struct i2c_client *client,
620 const struct i2c_device_id *devid)
621{
622 struct ext_slave_platform_data *pdata;
623 struct lis3dh_mod_private_data *private_data;
624 int result = 0;
625
626 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
627
628 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
629 result = -ENODEV;
630 goto out_no_free;
631 }
632
633 pdata = client->dev.platform_data;
634 if (!pdata) {
635 dev_err(&client->adapter->dev,
636 "Missing platform data for slave %s\n", devid->name);
637 result = -EFAULT;
638 goto out_no_free;
639 }
640
641 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
642 if (!private_data) {
643 result = -ENOMEM;
644 goto out_no_free;
645 }
646
647 i2c_set_clientdata(client, private_data);
648 private_data->client = client;
649 private_data->pdata = pdata;
650
651 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
652 lis3dh_get_slave_descr);
653 if (result) {
654 dev_err(&client->adapter->dev,
655 "Slave registration failed: %s, %d\n",
656 devid->name, result);
657 goto out_free_memory;
658 }
659
660 return result;
661
662out_free_memory:
663 kfree(private_data);
664out_no_free:
665 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
666 return result;
667
668}
669
670static int lis3dh_mod_remove(struct i2c_client *client)
671{
672 struct lis3dh_mod_private_data *private_data =
673 i2c_get_clientdata(client);
674
675 dev_dbg(&client->adapter->dev, "%s\n", __func__);
676
677 inv_mpu_unregister_slave(client, private_data->pdata,
678 lis3dh_get_slave_descr);
679
680 kfree(private_data);
681 return 0;
682}
683
684static const struct i2c_device_id lis3dh_mod_id[] = {
685 { "lis3dh", ACCEL_ID_LIS3DH },
686 {}
687};
688
689MODULE_DEVICE_TABLE(i2c, lis3dh_mod_id);
690
691static struct i2c_driver lis3dh_mod_driver = {
692 .class = I2C_CLASS_HWMON,
693 .probe = lis3dh_mod_probe,
694 .remove = lis3dh_mod_remove,
695 .id_table = lis3dh_mod_id,
696 .driver = {
697 .owner = THIS_MODULE,
698 .name = "lis3dh_mod",
699 },
700 .address_list = normal_i2c,
701};
702
703static int __init lis3dh_mod_init(void)
704{
705 int res = i2c_add_driver(&lis3dh_mod_driver);
706 pr_info("%s: Probe name %s\n", __func__, "lis3dh_mod");
707 if (res)
708 pr_err("%s failed\n", __func__);
709 return res;
710}
711
712static void __exit lis3dh_mod_exit(void)
713{
714 pr_info("%s\n", __func__);
715 i2c_del_driver(&lis3dh_mod_driver);
716}
717
718module_init(lis3dh_mod_init);
719module_exit(lis3dh_mod_exit);
720
721MODULE_AUTHOR("Invensense Corporation");
722MODULE_DESCRIPTION("Driver to integrate LIS3DH sensor with the MPU");
723MODULE_LICENSE("GPL");
724MODULE_ALIAS("lis3dh_mod");
725
726/*
727 * @}
728 */
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 */
diff --git a/drivers/misc/inv_mpu/accel/mma8450.c b/drivers/misc/inv_mpu/accel/mma8450.c
new file mode 100644
index 00000000000..f698ee98bf5
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mma8450.c
@@ -0,0 +1,804 @@
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 mma8450.c
26 * @brief Accelerometer setup and handling methods for Freescale MMA8450.
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/* full scale setting - register & mask */
48#define ACCEL_MMA8450_XYZ_DATA_CFG (0x16)
49
50#define ACCEL_MMA8450_CTRL_REG1 (0x38)
51#define ACCEL_MMA8450_CTRL_REG2 (0x39)
52#define ACCEL_MMA8450_CTRL_REG4 (0x3B)
53#define ACCEL_MMA8450_CTRL_REG5 (0x3C)
54
55#define ACCEL_MMA8450_CTRL_REG (0x38)
56#define ACCEL_MMA8450_CTRL_MASK (0x03)
57
58#define ACCEL_MMA8450_SLEEP_MASK (0x03)
59
60/* -------------------------------------------------------------------------- */
61
62struct mma8450_config {
63 unsigned int odr;
64 unsigned int fsr; /** < full scale range mg */
65 unsigned int ths; /** < Motion no-motion thseshold mg */
66 unsigned int dur; /** < Motion no-motion duration ms */
67 unsigned char reg_ths;
68 unsigned char reg_dur;
69 unsigned char ctrl_reg1;
70 unsigned char irq_type;
71 unsigned char mot_int1_cfg;
72};
73
74struct mma8450_private_data {
75 struct mma8450_config suspend;
76 struct mma8450_config resume;
77};
78
79
80/* -------------------------------------------------------------------------- */
81
82static int mma8450_set_ths(void *mlsl_handle,
83 struct ext_slave_platform_data *pdata,
84 struct mma8450_config *config,
85 int apply,
86 long ths)
87{
88 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
89}
90
91static int mma8450_set_dur(void *mlsl_handle,
92 struct ext_slave_platform_data *pdata,
93 struct mma8450_config *config,
94 int apply,
95 long dur)
96{
97 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
98}
99
100/**
101 * @brief Sets the IRQ to fire when one of the IRQ events occur.
102 * Threshold and duration will not be used unless the type is MOT or
103 * NMOT.
104 *
105 * @param mlsl_handle
106 * the handle to the serial channel the device is connected to.
107 * @param pdata
108 * a pointer to the slave platform data.
109 * @param config
110 * configuration to apply to, suspend or resume
111 * @param apply
112 * whether to apply immediately or save the settings to be applied
113 * at the next resume.
114 * @param irq_type
115 * the type of IRQ. Valid values are
116 * - MPU_SLAVE_IRQ_TYPE_NONE
117 * - MPU_SLAVE_IRQ_TYPE_MOTION
118 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
119 *
120 * @return INV_SUCCESS if successful or a non-zero error code.
121 */
122static int mma8450_set_irq(void *mlsl_handle,
123 struct ext_slave_platform_data *pdata,
124 struct mma8450_config *config,
125 int apply,
126 long irq_type)
127{
128 int result = INV_SUCCESS;
129 unsigned char reg1;
130 unsigned char reg2;
131 unsigned char reg3;
132
133 config->irq_type = (unsigned char)irq_type;
134 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
135 reg1 = 0x01;
136 reg2 = 0x01;
137 reg3 = 0x07;
138 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) {
139 reg1 = 0x00;
140 reg2 = 0x00;
141 reg3 = 0x00;
142 } else {
143 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
144 }
145
146 if (apply) {
147 /* XYZ_DATA_CFG: event flag enabled on Z axis */
148 result = inv_serial_single_write(mlsl_handle, pdata->address,
149 ACCEL_MMA8450_XYZ_DATA_CFG, reg3);
150 if (result) {
151 LOG_RESULT_LOCATION(result);
152 return result;
153 }
154 result = inv_serial_single_write(mlsl_handle, pdata->address,
155 ACCEL_MMA8450_CTRL_REG4, reg1);
156 if (result) {
157 LOG_RESULT_LOCATION(result);
158 return result;
159 }
160 result = inv_serial_single_write(mlsl_handle, pdata->address,
161 ACCEL_MMA8450_CTRL_REG5, reg2);
162 if (result) {
163 LOG_RESULT_LOCATION(result);
164 return result;
165 }
166 }
167
168 return result;
169}
170
171/**
172 * @brief Set the output data rate for the particular configuration.
173 *
174 * @param mlsl_handle
175 * the handle to the serial channel the device is connected to.
176 * @param pdata
177 * a pointer to the slave platform data.
178 * @param config
179 * Config to modify with new ODR.
180 * @param apply
181 * whether to apply immediately or save the settings to be applied
182 * at the next resume.
183 * @param odr
184 * Output data rate in units of 1/1000Hz (mHz).
185 *
186 * @return INV_SUCCESS if successful or a non-zero error code.
187 */
188static int mma8450_set_odr(void *mlsl_handle,
189 struct ext_slave_platform_data *pdata,
190 struct mma8450_config *config,
191 int apply,
192 long odr)
193{
194 unsigned char bits;
195 int result = INV_SUCCESS;
196
197 if (odr > 200000) {
198 config->odr = 400000;
199 bits = 0x00;
200 } else if (odr > 100000) {
201 config->odr = 200000;
202 bits = 0x04;
203 } else if (odr > 50000) {
204 config->odr = 100000;
205 bits = 0x08;
206 } else if (odr > 25000) {
207 config->odr = 50000;
208 bits = 0x0C;
209 } else if (odr > 12500) {
210 config->odr = 25000;
211 bits = 0x40; /* Sleep -> Auto wake mode */
212 } else if (odr > 1563) {
213 config->odr = 12500;
214 bits = 0x10;
215 } else if (odr > 0) {
216 config->odr = 1563;
217 bits = 0x14;
218 } else {
219 config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */
220 config->odr = 0;
221 bits = 0;
222 }
223
224 config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x3);
225 if (apply) {
226 result = inv_serial_single_write(mlsl_handle, pdata->address,
227 ACCEL_MMA8450_CTRL_REG1, 0);
228 if (result) {
229 LOG_RESULT_LOCATION(result);
230 return result;
231 }
232 result = inv_serial_single_write(mlsl_handle, pdata->address,
233 ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1);
234 if (result) {
235 LOG_RESULT_LOCATION(result);
236 return result;
237 }
238 MPL_LOGV("ODR: %d mHz, 0x%02x\n",
239 config->odr, (int)config->ctrl_reg1);
240 }
241 return result;
242}
243
244/**
245 * @brief Set the full scale range of the accels
246 *
247 * @param mlsl_handle
248 * the handle to the serial channel the device is connected to.
249 * @param pdata
250 * a pointer to the slave platform data.
251 * @param config
252 * pointer to configuration.
253 * @param apply
254 * whether to apply immediately or save the settings to be applied
255 * at the next resume.
256 * @param fsr
257 * requested full scale range.
258 *
259 * @return INV_SUCCESS if successful or a non-zero error code.
260 */
261static int mma8450_set_fsr(void *mlsl_handle,
262 struct ext_slave_platform_data *pdata,
263 struct mma8450_config *config,
264 int apply,
265 long fsr)
266{
267 unsigned char bits;
268 int result = INV_SUCCESS;
269
270 if (fsr <= 2000) {
271 bits = 0x01;
272 config->fsr = 2000;
273 } else if (fsr <= 4000) {
274 bits = 0x02;
275 config->fsr = 4000;
276 } else {
277 bits = 0x03;
278 config->fsr = 8000;
279 }
280
281 config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xFC);
282 if (apply) {
283 result = inv_serial_single_write(mlsl_handle, pdata->address,
284 ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1);
285 if (result) {
286 LOG_RESULT_LOCATION(result);
287 return result;
288 }
289 MPL_LOGV("FSR: %d mg\n", config->fsr);
290 }
291 return result;
292}
293
294/**
295 * @brief suspends the device to put it in its lowest power mode.
296 *
297 * @param mlsl_handle
298 * the handle to the serial channel the device is connected to.
299 * @param slave
300 * a pointer to the slave descriptor data structure.
301 * @param pdata
302 * a pointer to the slave platform data.
303 *
304 * @return INV_SUCCESS if successful or a non-zero error code.
305 */
306static int mma8450_suspend(void *mlsl_handle,
307 struct ext_slave_descr *slave,
308 struct ext_slave_platform_data *pdata)
309{
310 int result;
311 struct mma8450_private_data *private_data = pdata->private_data;
312
313 if (private_data->suspend.fsr == 4000)
314 slave->range.mantissa = 4;
315 else if (private_data->suspend.fsr == 8000)
316 slave->range.mantissa = 8;
317 else
318 slave->range.mantissa = 2;
319 slave->range.fraction = 0;
320
321 result = inv_serial_single_write(mlsl_handle, pdata->address,
322 ACCEL_MMA8450_CTRL_REG1, 0);
323 if (result) {
324 LOG_RESULT_LOCATION(result);
325 return result;
326 }
327 if (private_data->suspend.ctrl_reg1) {
328 result = inv_serial_single_write(mlsl_handle, pdata->address,
329 ACCEL_MMA8450_CTRL_REG1,
330 private_data->suspend.ctrl_reg1);
331 if (result) {
332 LOG_RESULT_LOCATION(result);
333 return result;
334 }
335 }
336
337 result = mma8450_set_irq(mlsl_handle, pdata,
338 &private_data->suspend,
339 true, private_data->suspend.irq_type);
340 if (result) {
341 LOG_RESULT_LOCATION(result);
342 return result;
343 }
344 return result;
345}
346
347/**
348 * @brief resume the device in the proper power state given the configuration
349 * chosen.
350 *
351 * @param mlsl_handle
352 * the handle to the serial channel the device is connected to.
353 * @param slave
354 * a pointer to the slave descriptor data structure.
355 * @param pdata
356 * a pointer to the slave platform data.
357 *
358 * @return INV_SUCCESS if successful or a non-zero error code.
359 */
360static int mma8450_resume(void *mlsl_handle,
361 struct ext_slave_descr *slave,
362 struct ext_slave_platform_data *pdata)
363{
364 int result = INV_SUCCESS;
365 struct mma8450_private_data *private_data = pdata->private_data;
366
367 /* Full Scale */
368 if (private_data->resume.fsr == 4000)
369 slave->range.mantissa = 4;
370 else if (private_data->resume.fsr == 8000)
371 slave->range.mantissa = 8;
372 else
373 slave->range.mantissa = 2;
374 slave->range.fraction = 0;
375
376 if (result) {
377 LOG_RESULT_LOCATION(result);
378 return result;
379 }
380 result = inv_serial_single_write(mlsl_handle, pdata->address,
381 ACCEL_MMA8450_CTRL_REG1, 0);
382 if (result) {
383 LOG_RESULT_LOCATION(result);
384 return result;
385 }
386 if (private_data->resume.ctrl_reg1) {
387 result = inv_serial_single_write(mlsl_handle, pdata->address,
388 ACCEL_MMA8450_CTRL_REG1,
389 private_data->resume.ctrl_reg1);
390 if (result) {
391 LOG_RESULT_LOCATION(result);
392 return result;
393 }
394 }
395 result = mma8450_set_irq(mlsl_handle, pdata,
396 &private_data->resume,
397 true, private_data->resume.irq_type);
398 if (result) {
399 LOG_RESULT_LOCATION(result);
400 return result;
401 }
402
403 return result;
404}
405
406/**
407 * @brief read the sensor data from the device.
408 *
409 * @param mlsl_handle
410 * the handle to the serial channel the device is connected to.
411 * @param slave
412 * a pointer to the slave descriptor data structure.
413 * @param pdata
414 * a pointer to the slave platform data.
415 * @param data
416 * a buffer to store the data read.
417 *
418 * @return INV_SUCCESS if successful or a non-zero error code.
419 */
420static int mma8450_read(void *mlsl_handle,
421 struct ext_slave_descr *slave,
422 struct ext_slave_platform_data *pdata, unsigned char *data)
423{
424 int result;
425 unsigned char local_data[4]; /* Status register + 3 bytes data */
426 result = inv_serial_read(mlsl_handle, pdata->address,
427 0x00, sizeof(local_data), local_data);
428 if (result) {
429 LOG_RESULT_LOCATION(result);
430 return result;
431 }
432 memcpy(data, &local_data[1], (slave->read_len) - 1);
433
434 MPL_LOGV("Data Not Ready: %02x %02x %02x %02x\n",
435 local_data[0], local_data[1],
436 local_data[2], local_data[3]);
437
438 return result;
439}
440
441/**
442 * @brief one-time device driver initialization function.
443 * If the driver is built as a kernel module, this function will be
444 * called when the module is loaded in the kernel.
445 * If the driver is built-in in the kernel, this function will be
446 * called at boot time.
447 *
448 * @param mlsl_handle
449 * the handle to the serial channel the device is connected to.
450 * @param slave
451 * a pointer to the slave descriptor data structure.
452 * @param pdata
453 * a pointer to the slave platform data.
454 *
455 * @return INV_SUCCESS if successful or a non-zero error code.
456 */
457static int mma8450_init(void *mlsl_handle,
458 struct ext_slave_descr *slave,
459 struct ext_slave_platform_data *pdata)
460{
461 struct mma8450_private_data *private_data;
462 private_data = (struct mma8450_private_data *)
463 kzalloc(sizeof(struct mma8450_private_data), GFP_KERNEL);
464
465 if (!private_data)
466 return INV_ERROR_MEMORY_EXAUSTED;
467
468 pdata->private_data = private_data;
469
470 mma8450_set_odr(mlsl_handle, pdata, &private_data->suspend,
471 false, 0);
472 mma8450_set_odr(mlsl_handle, pdata, &private_data->resume,
473 false, 200000);
474 mma8450_set_fsr(mlsl_handle, pdata, &private_data->suspend,
475 false, 2000);
476 mma8450_set_fsr(mlsl_handle, pdata, &private_data->resume,
477 false, 2000);
478 mma8450_set_irq(mlsl_handle, pdata, &private_data->suspend,
479 false,
480 MPU_SLAVE_IRQ_TYPE_NONE);
481 mma8450_set_irq(mlsl_handle, pdata, &private_data->resume,
482 false,
483 MPU_SLAVE_IRQ_TYPE_NONE);
484 return INV_SUCCESS;
485}
486
487/**
488 * @brief one-time device driver exit function.
489 * If the driver is built as a kernel module, this function will be
490 * called when the module is removed from the kernel.
491 *
492 * @param mlsl_handle
493 * the handle to the serial channel the device is connected to.
494 * @param slave
495 * a pointer to the slave descriptor data structure.
496 * @param pdata
497 * a pointer to the slave platform data.
498 *
499 * @return INV_SUCCESS if successful or a non-zero error code.
500 */
501static int mma8450_exit(void *mlsl_handle,
502 struct ext_slave_descr *slave,
503 struct ext_slave_platform_data *pdata)
504{
505 kfree(pdata->private_data);
506 return INV_SUCCESS;
507}
508
509/**
510 * @brief device configuration facility.
511 *
512 * @param mlsl_handle
513 * the handle to the serial channel the device is connected to.
514 * @param slave
515 * a pointer to the slave descriptor data structure.
516 * @param pdata
517 * a pointer to the slave platform data.
518 * @param data
519 * a pointer to the configuration data structure.
520 *
521 * @return INV_SUCCESS if successful or a non-zero error code.
522 */
523static int mma8450_config(void *mlsl_handle,
524 struct ext_slave_descr *slave,
525 struct ext_slave_platform_data *pdata,
526 struct ext_slave_config *data)
527{
528 struct mma8450_private_data *private_data = pdata->private_data;
529 if (!data->data)
530 return INV_ERROR_INVALID_PARAMETER;
531
532 switch (data->key) {
533 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
534 return mma8450_set_odr(mlsl_handle, pdata,
535 &private_data->suspend,
536 data->apply,
537 *((long *)data->data));
538 case MPU_SLAVE_CONFIG_ODR_RESUME:
539 return mma8450_set_odr(mlsl_handle, pdata,
540 &private_data->resume,
541 data->apply,
542 *((long *)data->data));
543 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
544 return mma8450_set_fsr(mlsl_handle, pdata,
545 &private_data->suspend,
546 data->apply,
547 *((long *)data->data));
548 case MPU_SLAVE_CONFIG_FSR_RESUME:
549 return mma8450_set_fsr(mlsl_handle, pdata,
550 &private_data->resume,
551 data->apply,
552 *((long *)data->data));
553 case MPU_SLAVE_CONFIG_MOT_THS:
554 return mma8450_set_ths(mlsl_handle, pdata,
555 &private_data->suspend,
556 data->apply,
557 *((long *)data->data));
558 case MPU_SLAVE_CONFIG_NMOT_THS:
559 return mma8450_set_ths(mlsl_handle, pdata,
560 &private_data->resume,
561 data->apply,
562 *((long *)data->data));
563 case MPU_SLAVE_CONFIG_MOT_DUR:
564 return mma8450_set_dur(mlsl_handle, pdata,
565 &private_data->suspend,
566 data->apply,
567 *((long *)data->data));
568 case MPU_SLAVE_CONFIG_NMOT_DUR:
569 return mma8450_set_dur(mlsl_handle, pdata,
570 &private_data->resume,
571 data->apply,
572 *((long *)data->data));
573 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
574 return mma8450_set_irq(mlsl_handle, pdata,
575 &private_data->suspend,
576 data->apply,
577 *((long *)data->data));
578 case MPU_SLAVE_CONFIG_IRQ_RESUME:
579 return mma8450_set_irq(mlsl_handle, pdata,
580 &private_data->resume,
581 data->apply,
582 *((long *)data->data));
583 default:
584 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
585 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
586 };
587
588 return INV_SUCCESS;
589}
590
591/**
592 * @brief facility to retrieve the device configuration.
593 *
594 * @param mlsl_handle
595 * the handle to the serial channel the device is connected to.
596 * @param slave
597 * a pointer to the slave descriptor data structure.
598 * @param pdata
599 * a pointer to the slave platform data.
600 * @param data
601 * a pointer to store the returned configuration data structure.
602 *
603 * @return INV_SUCCESS if successful or a non-zero error code.
604 */
605static int mma8450_get_config(void *mlsl_handle,
606 struct ext_slave_descr *slave,
607 struct ext_slave_platform_data *pdata,
608 struct ext_slave_config *data)
609{
610 struct mma8450_private_data *private_data = pdata->private_data;
611 if (!data->data)
612 return INV_ERROR_INVALID_PARAMETER;
613
614 switch (data->key) {
615 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
616 (*(unsigned long *)data->data) =
617 (unsigned long) private_data->suspend.odr;
618 break;
619 case MPU_SLAVE_CONFIG_ODR_RESUME:
620 (*(unsigned long *)data->data) =
621 (unsigned long) private_data->resume.odr;
622 break;
623 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
624 (*(unsigned long *)data->data) =
625 (unsigned long) private_data->suspend.fsr;
626 break;
627 case MPU_SLAVE_CONFIG_FSR_RESUME:
628 (*(unsigned long *)data->data) =
629 (unsigned long) private_data->resume.fsr;
630 break;
631 case MPU_SLAVE_CONFIG_MOT_THS:
632 (*(unsigned long *)data->data) =
633 (unsigned long) private_data->suspend.ths;
634 break;
635 case MPU_SLAVE_CONFIG_NMOT_THS:
636 (*(unsigned long *)data->data) =
637 (unsigned long) private_data->resume.ths;
638 break;
639 case MPU_SLAVE_CONFIG_MOT_DUR:
640 (*(unsigned long *)data->data) =
641 (unsigned long) private_data->suspend.dur;
642 break;
643 case MPU_SLAVE_CONFIG_NMOT_DUR:
644 (*(unsigned long *)data->data) =
645 (unsigned long) private_data->resume.dur;
646 break;
647 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
648 (*(unsigned long *)data->data) =
649 (unsigned long) private_data->suspend.irq_type;
650 break;
651 case MPU_SLAVE_CONFIG_IRQ_RESUME:
652 (*(unsigned long *)data->data) =
653 (unsigned long) private_data->resume.irq_type;
654 break;
655 default:
656 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
657 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
658 };
659
660 return INV_SUCCESS;
661}
662
663static struct ext_slave_descr mma8450_descr = {
664 .init = mma8450_init,
665 .exit = mma8450_exit,
666 .suspend = mma8450_suspend,
667 .resume = mma8450_resume,
668 .read = mma8450_read,
669 .config = mma8450_config,
670 .get_config = mma8450_get_config,
671 .name = "mma8450",
672 .type = EXT_SLAVE_TYPE_ACCEL,
673 .id = ACCEL_ID_MMA8450,
674 .read_reg = 0x00,
675 .read_len = 4,
676 .endian = EXT_SLAVE_FS8_BIG_ENDIAN,
677 .range = {2, 0},
678 .trigger = NULL,
679};
680
681static
682struct ext_slave_descr *mma8450_get_slave_descr(void)
683{
684 return &mma8450_descr;
685}
686
687/* -------------------------------------------------------------------------- */
688struct mma8450_mod_private_data {
689 struct i2c_client *client;
690 struct ext_slave_platform_data *pdata;
691};
692
693static unsigned short normal_i2c[] = { I2C_CLIENT_END };
694
695static int mma8450_mod_probe(struct i2c_client *client,
696 const struct i2c_device_id *devid)
697{
698 struct ext_slave_platform_data *pdata;
699 struct mma8450_mod_private_data *private_data;
700 int result = 0;
701
702 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
703
704 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
705 result = -ENODEV;
706 goto out_no_free;
707 }
708
709 pdata = client->dev.platform_data;
710 if (!pdata) {
711 dev_err(&client->adapter->dev,
712 "Missing platform data for slave %s\n", devid->name);
713 result = -EFAULT;
714 goto out_no_free;
715 }
716
717 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
718 if (!private_data) {
719 result = -ENOMEM;
720 goto out_no_free;
721 }
722
723 i2c_set_clientdata(client, private_data);
724 private_data->client = client;
725 private_data->pdata = pdata;
726
727 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
728 mma8450_get_slave_descr);
729 if (result) {
730 dev_err(&client->adapter->dev,
731 "Slave registration failed: %s, %d\n",
732 devid->name, result);
733 goto out_free_memory;
734 }
735
736 return result;
737
738out_free_memory:
739 kfree(private_data);
740out_no_free:
741 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
742 return result;
743
744}
745
746static int mma8450_mod_remove(struct i2c_client *client)
747{
748 struct mma8450_mod_private_data *private_data =
749 i2c_get_clientdata(client);
750
751 dev_dbg(&client->adapter->dev, "%s\n", __func__);
752
753 inv_mpu_unregister_slave(client, private_data->pdata,
754 mma8450_get_slave_descr);
755
756 kfree(private_data);
757 return 0;
758}
759
760static const struct i2c_device_id mma8450_mod_id[] = {
761 { "mma8450", ACCEL_ID_MMA8450 },
762 {}
763};
764
765MODULE_DEVICE_TABLE(i2c, mma8450_mod_id);
766
767static struct i2c_driver mma8450_mod_driver = {
768 .class = I2C_CLASS_HWMON,
769 .probe = mma8450_mod_probe,
770 .remove = mma8450_mod_remove,
771 .id_table = mma8450_mod_id,
772 .driver = {
773 .owner = THIS_MODULE,
774 .name = "mma8450_mod",
775 },
776 .address_list = normal_i2c,
777};
778
779static int __init mma8450_mod_init(void)
780{
781 int res = i2c_add_driver(&mma8450_mod_driver);
782 pr_info("%s: Probe name %s\n", __func__, "mma8450_mod");
783 if (res)
784 pr_err("%s failed\n", __func__);
785 return res;
786}
787
788static void __exit mma8450_mod_exit(void)
789{
790 pr_info("%s\n", __func__);
791 i2c_del_driver(&mma8450_mod_driver);
792}
793
794module_init(mma8450_mod_init);
795module_exit(mma8450_mod_exit);
796
797MODULE_AUTHOR("Invensense Corporation");
798MODULE_DESCRIPTION("Driver to integrate MMA8450 sensor with the MPU");
799MODULE_LICENSE("GPL");
800MODULE_ALIAS("mma8450_mod");
801
802/**
803 * @}
804 */
diff --git a/drivers/misc/inv_mpu/accel/mma845x.c b/drivers/misc/inv_mpu/accel/mma845x.c
new file mode 100644
index 00000000000..5f62b22388b
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mma845x.c
@@ -0,0 +1,713 @@
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 mma845x.c
26 * @brief Accelerometer setup and handling methods for Freescale MMA845X
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#define ACCEL_MMA845X_XYZ_DATA_CFG (0x0E)
48#define ACCEL_MMA845X_CTRL_REG1 (0x2A)
49#define ACCEL_MMA845X_CTRL_REG4 (0x2D)
50#define ACCEL_MMA845X_CTRL_REG5 (0x2E)
51
52#define ACCEL_MMA845X_SLEEP_MASK (0x01)
53
54/* full scale setting - register & mask */
55#define ACCEL_MMA845X_CFG_REG (0x0E)
56#define ACCEL_MMA845X_CTRL_MASK (0x03)
57
58/* -------------------------------------------------------------------------- */
59
60struct mma845x_config {
61 unsigned int odr;
62 unsigned int fsr; /** < full scale range mg */
63 unsigned int ths; /** < Motion no-motion thseshold mg */
64 unsigned int dur; /** < Motion no-motion duration ms */
65 unsigned char reg_ths;
66 unsigned char reg_dur;
67 unsigned char ctrl_reg1;
68 unsigned char irq_type;
69 unsigned char mot_int1_cfg;
70};
71
72struct mma845x_private_data {
73 struct mma845x_config suspend;
74 struct mma845x_config resume;
75};
76
77/* -------------------------------------------------------------------------- */
78
79static int mma845x_set_ths(void *mlsl_handle,
80 struct ext_slave_platform_data *pdata,
81 struct mma845x_config *config,
82 int apply,
83 long ths)
84{
85 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
86}
87
88static int mma845x_set_dur(void *mlsl_handle,
89 struct ext_slave_platform_data *pdata,
90 struct mma845x_config *config,
91 int apply,
92 long dur)
93{
94 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
95}
96
97/**
98 * @brief Sets the IRQ to fire when one of the IRQ events occur.
99 * Threshold and duration will not be used unless the type is MOT or
100 * NMOT.
101 *
102 * @param mlsl_handle
103 * the handle to the serial channel the device is connected to.
104 * @param pdata
105 * a pointer to the slave platform data.
106 * @param config
107 * configuration to apply to, suspend or resume
108 * @param apply
109 * whether to apply immediately or save the settings to be applied
110 * at the next resume.
111 * @param irq_type
112 * the type of IRQ. Valid values are
113 * - MPU_SLAVE_IRQ_TYPE_NONE
114 * - MPU_SLAVE_IRQ_TYPE_MOTION
115 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
116 *
117 * @return INV_SUCCESS if successful or a non-zero error code.
118 */
119static int mma845x_set_irq(void *mlsl_handle,
120 struct ext_slave_platform_data *pdata,
121 struct mma845x_config *config,
122 int apply,
123 long irq_type)
124{
125 int result = INV_SUCCESS;
126 unsigned char reg1;
127 unsigned char reg2;
128
129 config->irq_type = (unsigned char)irq_type;
130 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
131 reg1 = 0x01;
132 reg2 = 0x01;
133 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) {
134 reg1 = 0x00;
135 reg2 = 0x00;
136 } else {
137 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
138 }
139
140 if (apply) {
141 result = inv_serial_single_write(mlsl_handle, pdata->address,
142 ACCEL_MMA845X_CTRL_REG4, reg1);
143 if (result) {
144 LOG_RESULT_LOCATION(result);
145 return result;
146 }
147 result = inv_serial_single_write(mlsl_handle, pdata->address,
148 ACCEL_MMA845X_CTRL_REG5, reg2);
149 if (result) {
150 LOG_RESULT_LOCATION(result);
151 return result;
152 }
153 }
154
155 return result;
156}
157
158/**
159 * @brief Set the output data rate for the particular configuration.
160 *
161 * @param mlsl_handle
162 * the handle to the serial channel the device is connected to.
163 * @param pdata
164 * a pointer to the slave platform data.
165 * @param config
166 * Config to modify with new ODR.
167 * @param apply
168 * whether to apply immediately or save the settings to be applied
169 * at the next resume.
170 * @param odr
171 * Output data rate in units of 1/1000Hz (mHz).
172 *
173 * @return INV_SUCCESS if successful or a non-zero error code.
174 */
175static int mma845x_set_odr(void *mlsl_handle,
176 struct ext_slave_platform_data *pdata,
177 struct mma845x_config *config,
178 int apply,
179 long odr)
180{
181 unsigned char bits;
182 int result = INV_SUCCESS;
183
184 if (odr > 400000) {
185 config->odr = 800000;
186 bits = 0x01;
187 } else if (odr > 200000) {
188 config->odr = 400000;
189 bits = 0x09;
190 } else if (odr > 100000) {
191 config->odr = 200000;
192 bits = 0x11;
193 } else if (odr > 50000) {
194 config->odr = 100000;
195 bits = 0x19;
196 } else if (odr > 12500) {
197 config->odr = 50000;
198 bits = 0x21;
199 } else if (odr > 6250) {
200 config->odr = 12500;
201 bits = 0x29;
202 } else if (odr > 1560) {
203 config->odr = 6250;
204 bits = 0x31;
205 } else if (odr > 0) {
206 config->odr = 1560;
207 bits = 0x39;
208 } else {
209 config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */
210 config->odr = 0;
211 bits = 0;
212 }
213
214 config->ctrl_reg1 = bits;
215 if (apply) {
216 result = inv_serial_single_write(mlsl_handle, pdata->address,
217 ACCEL_MMA845X_CTRL_REG1,
218 config->ctrl_reg1);
219 if (result) {
220 LOG_RESULT_LOCATION(result);
221 return result;
222 }
223 MPL_LOGV("ODR: %d mHz, 0x%02x\n", config->odr,
224 (int)config->ctrl_reg1);
225 }
226 return result;
227}
228
229/**
230 * @brief Set the full scale range of the accels
231 *
232 * @param mlsl_handle
233 * the handle to the serial channel the device is connected to.
234 * @param pdata
235 * a pointer to the slave platform data.
236 * @param config
237 * pointer to configuration.
238 * @param apply
239 * whether to apply immediately or save the settings to be applied
240 * at the next resume.
241 * @param fsr
242 * requested full scale range.
243 *
244 * @return INV_SUCCESS if successful or a non-zero error code.
245 */
246static int mma845x_set_fsr(void *mlsl_handle,
247 struct ext_slave_platform_data *pdata,
248 struct mma845x_config *config,
249 int apply,
250 long fsr)
251{
252 unsigned char bits;
253 int result = INV_SUCCESS;
254
255 if (fsr <= 2000) {
256 bits = 0x00;
257 config->fsr = 2000;
258 } else if (fsr <= 4000) {
259 bits = 0x01;
260 config->fsr = 4000;
261 } else {
262 bits = 0x02;
263 config->fsr = 8000;
264 }
265
266 if (apply) {
267 result = inv_serial_single_write(mlsl_handle, pdata->address,
268 ACCEL_MMA845X_XYZ_DATA_CFG,
269 bits);
270 if (result) {
271 LOG_RESULT_LOCATION(result);
272 return result;
273 }
274 MPL_LOGV("FSR: %d mg\n", config->fsr);
275 }
276 return result;
277}
278
279/**
280 * @brief suspends the device to put it in its lowest power mode.
281 *
282 * @param mlsl_handle
283 * the handle to the serial channel the device is connected to.
284 * @param slave
285 * a pointer to the slave descriptor data structure.
286 * @param pdata
287 * a pointer to the slave platform data.
288 *
289 * @return INV_SUCCESS if successful or a non-zero error code.
290 */
291static int mma845x_suspend(void *mlsl_handle,
292 struct ext_slave_descr *slave,
293 struct ext_slave_platform_data *pdata)
294{
295 int result;
296 struct mma845x_private_data *private_data = pdata->private_data;
297
298 /* Full Scale */
299 if (private_data->suspend.fsr == 4000)
300 slave->range.mantissa = 4;
301 else if (private_data->suspend.fsr == 8000)
302 slave->range.mantissa = 8;
303 else
304 slave->range.mantissa = 2;
305
306 slave->range.fraction = 0;
307
308 result = mma845x_set_fsr(mlsl_handle, pdata,
309 &private_data->suspend,
310 true, private_data->suspend.fsr);
311 if (result) {
312 LOG_RESULT_LOCATION(result);
313 return result;
314 }
315 result = inv_serial_single_write(mlsl_handle, pdata->address,
316 ACCEL_MMA845X_CTRL_REG1,
317 private_data->suspend.ctrl_reg1);
318 if (result) {
319 LOG_RESULT_LOCATION(result);
320 return result;
321 }
322
323 return result;
324}
325
326/**
327 * @brief resume the device in the proper power state given the configuration
328 * chosen.
329 *
330 * @param mlsl_handle
331 * the handle to the serial channel the device is connected to.
332 * @param slave
333 * a pointer to the slave descriptor data structure.
334 * @param pdata
335 * a pointer to the slave platform data.
336 *
337 * @return INV_SUCCESS if successful or a non-zero error code.
338 */
339static int mma845x_resume(void *mlsl_handle,
340 struct ext_slave_descr *slave,
341 struct ext_slave_platform_data *pdata)
342{
343 int result = INV_SUCCESS;
344 struct mma845x_private_data *private_data = pdata->private_data;
345
346 /* Full Scale */
347 if (private_data->resume.fsr == 4000)
348 slave->range.mantissa = 4;
349 else if (private_data->resume.fsr == 8000)
350 slave->range.mantissa = 8;
351 else
352 slave->range.mantissa = 2;
353
354 slave->range.fraction = 0;
355
356 result = mma845x_set_fsr(mlsl_handle, pdata,
357 &private_data->resume,
358 true, private_data->resume.fsr);
359 if (result) {
360 LOG_RESULT_LOCATION(result);
361 return result;
362 }
363 result = inv_serial_single_write(mlsl_handle, pdata->address,
364 ACCEL_MMA845X_CTRL_REG1,
365 private_data->resume.ctrl_reg1);
366 if (result) {
367 LOG_RESULT_LOCATION(result);
368 return result;
369 }
370
371 return result;
372}
373
374/**
375 * @brief read the sensor data from the device.
376 *
377 * @param mlsl_handle
378 * the handle to the serial channel the device is connected to.
379 * @param slave
380 * a pointer to the slave descriptor data structure.
381 * @param pdata
382 * a pointer to the slave platform data.
383 * @param data
384 * a buffer to store the data read.
385 *
386 * @return INV_SUCCESS if successful or a non-zero error code.
387 */
388static int mma845x_read(void *mlsl_handle,
389 struct ext_slave_descr *slave,
390 struct ext_slave_platform_data *pdata, unsigned char *data)
391{
392 int result;
393 unsigned char local_data[7]; /* Status register + 6 bytes data */
394 result = inv_serial_read(mlsl_handle, pdata->address,
395 slave->read_reg, sizeof(local_data),
396 local_data);
397 if (result) {
398 LOG_RESULT_LOCATION(result);
399 return result;
400 }
401 memcpy(data, &local_data[1], slave->read_len);
402 return result;
403}
404
405static int mma845x_init(void *mlsl_handle,
406 struct ext_slave_descr *slave,
407 struct ext_slave_platform_data *pdata)
408{
409 long range;
410 struct mma845x_private_data *private_data;
411 private_data = (struct mma845x_private_data *)
412 kzalloc(sizeof(struct mma845x_private_data), GFP_KERNEL);
413
414 if (!private_data)
415 return INV_ERROR_MEMORY_EXAUSTED;
416
417 pdata->private_data = private_data;
418
419 mma845x_set_odr(mlsl_handle, pdata, &private_data->suspend,
420 false, 0);
421 mma845x_set_odr(mlsl_handle, pdata, &private_data->resume,
422 false, 200000);
423
424 range = range_fixedpoint_to_long_mg(slave->range);
425 mma845x_set_fsr(mlsl_handle, pdata, &private_data->suspend,
426 false, range);
427 mma845x_set_fsr(mlsl_handle, pdata, &private_data->resume,
428 false, range);
429
430 mma845x_set_irq(mlsl_handle, pdata, &private_data->suspend,
431 false, MPU_SLAVE_IRQ_TYPE_NONE);
432 mma845x_set_irq(mlsl_handle, pdata, &private_data->resume,
433 false, MPU_SLAVE_IRQ_TYPE_NONE);
434 return INV_SUCCESS;
435}
436
437static int mma845x_exit(void *mlsl_handle,
438 struct ext_slave_descr *slave,
439 struct ext_slave_platform_data *pdata)
440{
441 kfree(pdata->private_data);
442 return INV_SUCCESS;
443}
444
445static int mma845x_config(void *mlsl_handle,
446 struct ext_slave_descr *slave,
447 struct ext_slave_platform_data *pdata,
448 struct ext_slave_config *data)
449{
450 struct mma845x_private_data *private_data = pdata->private_data;
451 if (!data->data)
452 return INV_ERROR_INVALID_PARAMETER;
453
454 switch (data->key) {
455 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
456 return mma845x_set_odr(mlsl_handle, pdata,
457 &private_data->suspend,
458 data->apply,
459 *((long *)data->data));
460 case MPU_SLAVE_CONFIG_ODR_RESUME:
461 return mma845x_set_odr(mlsl_handle, pdata,
462 &private_data->resume,
463 data->apply,
464 *((long *)data->data));
465 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
466 return mma845x_set_fsr(mlsl_handle, pdata,
467 &private_data->suspend,
468 data->apply,
469 *((long *)data->data));
470 case MPU_SLAVE_CONFIG_FSR_RESUME:
471 return mma845x_set_fsr(mlsl_handle, pdata,
472 &private_data->resume,
473 data->apply,
474 *((long *)data->data));
475 case MPU_SLAVE_CONFIG_MOT_THS:
476 return mma845x_set_ths(mlsl_handle, pdata,
477 &private_data->suspend,
478 data->apply,
479 *((long *)data->data));
480 case MPU_SLAVE_CONFIG_NMOT_THS:
481 return mma845x_set_ths(mlsl_handle, pdata,
482 &private_data->resume,
483 data->apply,
484 *((long *)data->data));
485 case MPU_SLAVE_CONFIG_MOT_DUR:
486 return mma845x_set_dur(mlsl_handle, pdata,
487 &private_data->suspend,
488 data->apply,
489 *((long *)data->data));
490 case MPU_SLAVE_CONFIG_NMOT_DUR:
491 return mma845x_set_dur(mlsl_handle, pdata,
492 &private_data->resume,
493 data->apply,
494 *((long *)data->data));
495 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
496 return mma845x_set_irq(mlsl_handle, pdata,
497 &private_data->suspend,
498 data->apply,
499 *((long *)data->data));
500 case MPU_SLAVE_CONFIG_IRQ_RESUME:
501 return mma845x_set_irq(mlsl_handle, pdata,
502 &private_data->resume,
503 data->apply,
504 *((long *)data->data));
505 default:
506 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
507 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
508 };
509
510 return INV_SUCCESS;
511}
512
513static int mma845x_get_config(void *mlsl_handle,
514 struct ext_slave_descr *slave,
515 struct ext_slave_platform_data *pdata,
516 struct ext_slave_config *data)
517{
518 struct mma845x_private_data *private_data = pdata->private_data;
519 if (!data->data)
520 return INV_ERROR_INVALID_PARAMETER;
521
522 switch (data->key) {
523 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
524 (*(unsigned long *)data->data) =
525 (unsigned long) private_data->suspend.odr;
526 break;
527 case MPU_SLAVE_CONFIG_ODR_RESUME:
528 (*(unsigned long *)data->data) =
529 (unsigned long) private_data->resume.odr;
530 break;
531 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
532 (*(unsigned long *)data->data) =
533 (unsigned long) private_data->suspend.fsr;
534 break;
535 case MPU_SLAVE_CONFIG_FSR_RESUME:
536 (*(unsigned long *)data->data) =
537 (unsigned long) private_data->resume.fsr;
538 break;
539 case MPU_SLAVE_CONFIG_MOT_THS:
540 (*(unsigned long *)data->data) =
541 (unsigned long) private_data->suspend.ths;
542 break;
543 case MPU_SLAVE_CONFIG_NMOT_THS:
544 (*(unsigned long *)data->data) =
545 (unsigned long) private_data->resume.ths;
546 break;
547 case MPU_SLAVE_CONFIG_MOT_DUR:
548 (*(unsigned long *)data->data) =
549 (unsigned long) private_data->suspend.dur;
550 break;
551 case MPU_SLAVE_CONFIG_NMOT_DUR:
552 (*(unsigned long *)data->data) =
553 (unsigned long) private_data->resume.dur;
554 break;
555 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
556 (*(unsigned long *)data->data) =
557 (unsigned long) private_data->suspend.irq_type;
558 break;
559 case MPU_SLAVE_CONFIG_IRQ_RESUME:
560 (*(unsigned long *)data->data) =
561 (unsigned long) private_data->resume.irq_type;
562 break;
563 default:
564 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
565 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
566 };
567
568 return INV_SUCCESS;
569}
570
571static struct ext_slave_descr mma845x_descr = {
572 .init = mma845x_init,
573 .exit = mma845x_exit,
574 .suspend = mma845x_suspend,
575 .resume = mma845x_resume,
576 .read = mma845x_read,
577 .config = mma845x_config,
578 .get_config = mma845x_get_config,
579 .name = "mma845x",
580 .type = EXT_SLAVE_TYPE_ACCEL,
581 .id = ACCEL_ID_MMA845X,
582 .read_reg = 0x00,
583 .read_len = 6,
584 .endian = EXT_SLAVE_FS16_BIG_ENDIAN,
585 .range = {2, 0},
586 .trigger = NULL,
587};
588
589static
590struct ext_slave_descr *mma845x_get_slave_descr(void)
591{
592 return &mma845x_descr;
593}
594
595/* -------------------------------------------------------------------------- */
596struct mma845x_mod_private_data {
597 struct i2c_client *client;
598 struct ext_slave_platform_data *pdata;
599};
600
601static unsigned short normal_i2c[] = { I2C_CLIENT_END };
602
603static int mma845x_mod_probe(struct i2c_client *client,
604 const struct i2c_device_id *devid)
605{
606 struct ext_slave_platform_data *pdata;
607 struct mma845x_mod_private_data *private_data;
608 int result = 0;
609
610 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
611
612 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
613 result = -ENODEV;
614 goto out_no_free;
615 }
616
617 pdata = client->dev.platform_data;
618 if (!pdata) {
619 dev_err(&client->adapter->dev,
620 "Missing platform data for slave %s\n", devid->name);
621 result = -EFAULT;
622 goto out_no_free;
623 }
624
625 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
626 if (!private_data) {
627 result = -ENOMEM;
628 goto out_no_free;
629 }
630
631 i2c_set_clientdata(client, private_data);
632 private_data->client = client;
633 private_data->pdata = pdata;
634
635 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
636 mma845x_get_slave_descr);
637 if (result) {
638 dev_err(&client->adapter->dev,
639 "Slave registration failed: %s, %d\n",
640 devid->name, result);
641 goto out_free_memory;
642 }
643
644 return result;
645
646out_free_memory:
647 kfree(private_data);
648out_no_free:
649 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
650 return result;
651
652}
653
654static int mma845x_mod_remove(struct i2c_client *client)
655{
656 struct mma845x_mod_private_data *private_data =
657 i2c_get_clientdata(client);
658
659 dev_dbg(&client->adapter->dev, "%s\n", __func__);
660
661 inv_mpu_unregister_slave(client, private_data->pdata,
662 mma845x_get_slave_descr);
663
664 kfree(private_data);
665 return 0;
666}
667
668static const struct i2c_device_id mma845x_mod_id[] = {
669 { "mma845x", ACCEL_ID_MMA845X },
670 {}
671};
672
673MODULE_DEVICE_TABLE(i2c, mma845x_mod_id);
674
675static struct i2c_driver mma845x_mod_driver = {
676 .class = I2C_CLASS_HWMON,
677 .probe = mma845x_mod_probe,
678 .remove = mma845x_mod_remove,
679 .id_table = mma845x_mod_id,
680 .driver = {
681 .owner = THIS_MODULE,
682 .name = "mma845x_mod",
683 },
684 .address_list = normal_i2c,
685};
686
687static int __init mma845x_mod_init(void)
688{
689 int res = i2c_add_driver(&mma845x_mod_driver);
690 pr_info("%s: Probe name %s\n", __func__, "mma845x_mod");
691 if (res)
692 pr_err("%s failed\n", __func__);
693 return res;
694}
695
696static void __exit mma845x_mod_exit(void)
697{
698 pr_info("%s\n", __func__);
699 i2c_del_driver(&mma845x_mod_driver);
700}
701
702module_init(mma845x_mod_init);
703module_exit(mma845x_mod_exit);
704
705MODULE_AUTHOR("Invensense Corporation");
706MODULE_DESCRIPTION("Driver to integrate MMA845X sensor with the MPU");
707MODULE_LICENSE("GPL");
708MODULE_ALIAS("mma845x_mod");
709
710
711/**
712 * @}
713 */
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.c b/drivers/misc/inv_mpu/accel/mpu6050.c
new file mode 100644
index 00000000000..c5bb6784a41
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mpu6050.c
@@ -0,0 +1,695 @@
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 mpu6050.c
26 * @brief Accelerometer setup and handling methods for Invensense MPU6050
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/delay.h>
37#include <linux/slab.h>
38#include "mpu-dev.h"
39
40#include <log.h>
41#include <linux/mpu.h>
42#include "mpu6050b1.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
50struct mpu6050_config {
51 unsigned int odr; /**< output data rate 1/1000 Hz */
52 unsigned int fsr; /**< full scale range mg */
53 unsigned int ths; /**< mot/no-mot thseshold mg */
54 unsigned int dur; /**< mot/no-mot duration ms */
55 unsigned int irq_type; /**< irq type */
56};
57
58struct mpu6050_private_data {
59 struct mpu6050_config suspend;
60 struct mpu6050_config resume;
61 struct mldl_cfg *mldl_cfg_ref;
62};
63
64/* -------------------------------------------------------------------------- */
65
66static int mpu6050_set_mldl_cfg_ref(void *mlsl_handle,
67 struct ext_slave_platform_data *pdata,
68 struct mldl_cfg *mldl_cfg_ref)
69{
70 struct mpu6050_private_data *private_data =
71 (struct mpu6050_private_data *)pdata->private_data;
72 private_data->mldl_cfg_ref = mldl_cfg_ref;
73 return 0;
74}
75static int mpu6050_set_lp_mode(void *mlsl_handle,
76 struct ext_slave_platform_data *pdata,
77 unsigned char lpa_freq)
78{
79 unsigned char b = 0;
80 /* Reducing the duration setting for lp mode */
81 b = 1;
82 inv_serial_single_write(mlsl_handle, pdata->address,
83 MPUREG_ACCEL_MOT_DUR, b);
84 /* Setting the cycle bit and LPA wake up freq */
85 inv_serial_read(mlsl_handle, pdata->address, MPUREG_PWR_MGMT_1, 1,
86 &b);
87 b |= BIT_CYCLE | BIT_PD_PTAT;
88 inv_serial_single_write(mlsl_handle, pdata->address,
89 MPUREG_PWR_MGMT_1,
90 b);
91 inv_serial_read(mlsl_handle, pdata->address,
92 MPUREG_PWR_MGMT_2, 1, &b);
93 b |= lpa_freq & BITS_LPA_WAKE_CTRL;
94 inv_serial_single_write(mlsl_handle, pdata->address,
95 MPUREG_PWR_MGMT_2, b);
96
97 return INV_SUCCESS;
98}
99
100static int mpu6050_set_fp_mode(void *mlsl_handle,
101 struct ext_slave_platform_data *pdata)
102{
103 unsigned char b;
104 struct mpu6050_private_data *private_data =
105 (struct mpu6050_private_data *)pdata->private_data;
106 /* Resetting the cycle bit and LPA wake up freq */
107 inv_serial_read(mlsl_handle, pdata->address,
108 MPUREG_PWR_MGMT_1, 1, &b);
109 b &= ~BIT_CYCLE & ~BIT_PD_PTAT;
110 inv_serial_single_write(mlsl_handle, pdata->address,
111 MPUREG_PWR_MGMT_1, b);
112 inv_serial_read(mlsl_handle, pdata->address,
113 MPUREG_PWR_MGMT_2, 1, &b);
114 b &= ~BITS_LPA_WAKE_CTRL;
115 inv_serial_single_write(mlsl_handle, pdata->address,
116 MPUREG_PWR_MGMT_2, b);
117 /* Resetting the duration setting for fp mode */
118 b = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
119 inv_serial_single_write(mlsl_handle, pdata->address,
120 MPUREG_ACCEL_MOT_DUR, b);
121
122 return INV_SUCCESS;
123}
124/**
125 * Record the odr for use in computing duration values.
126 *
127 * @param config Config to set, suspend or resume structure
128 * @param odr output data rate in 1/1000 hz
129 */
130static int mpu6050_set_odr(void *mlsl_handle,
131 struct ext_slave_platform_data *pdata,
132 struct mpu6050_config *config, long apply, long odr)
133{
134 int result;
135 unsigned char b;
136 unsigned char lpa_freq = 1; /* Default value */
137 long base;
138 int total_divider;
139 struct mpu6050_private_data *private_data =
140 (struct mpu6050_private_data *)pdata->private_data;
141 struct mldl_cfg *mldl_cfg_ref =
142 (struct mldl_cfg *)private_data->mldl_cfg_ref;
143
144 if (mldl_cfg_ref) {
145 base = 1000 *
146 inv_mpu_get_sampling_rate_hz(mldl_cfg_ref->mpu_gyro_cfg)
147 * (mldl_cfg_ref->mpu_gyro_cfg->divider + 1);
148 } else {
149 /* have no reference to mldl_cfg => assume base rate is 1000 */
150 base = 1000000L;
151 }
152
153 if (odr != 0) {
154 total_divider = (base / odr) - 1;
155 /* final odr MAY be different from requested odr due to
156 integer truncation */
157 config->odr = base / (total_divider + 1);
158 } else {
159 config->odr = 0;
160 return 0;
161 }
162
163 /* if the DMP and/or gyros are on, don't set the ODR =>
164 the DMP/gyro mldl_cfg->divider setting will handle it */
165 if (apply
166 && (mldl_cfg_ref &&
167 !(mldl_cfg_ref->inv_mpu_cfg->requested_sensors &
168 INV_DMP_PROCESSOR))) {
169 result = inv_serial_single_write(mlsl_handle, pdata->address,
170 MPUREG_SMPLRT_DIV,
171 (unsigned char)total_divider);
172 if (result) {
173 LOG_RESULT_LOCATION(result);
174 return result;
175 }
176 MPL_LOGI("ODR : %d mHz\n", config->odr);
177 }
178 /* Decide whether to put accel in LP mode or pull out of LP mode
179 based on the odr. */
180 switch (odr) {
181 case 1000:
182 lpa_freq = BITS_LPA_WAKE_1HZ;
183 break;
184 case 2000:
185 lpa_freq = BITS_LPA_WAKE_2HZ;
186 break;
187 case 10000:
188 lpa_freq = BITS_LPA_WAKE_10HZ;
189 break;
190 case 40000:
191 lpa_freq = BITS_LPA_WAKE_40HZ;
192 break;
193 default:
194 inv_serial_read(mlsl_handle, pdata->address,
195 MPUREG_PWR_MGMT_1, 1, &b);
196 b &= BIT_CYCLE;
197 if (b == BIT_CYCLE) {
198 MPL_LOGI(" Accel LP - > FP mode. \n ");
199 mpu6050_set_fp_mode(mlsl_handle, pdata);
200 }
201 }
202 /* If lpa_freq default value was changed, set into LP mode */
203 if (lpa_freq != 1) {
204 MPL_LOGI(" Accel FP - > LP mode. \n ");
205 mpu6050_set_lp_mode(mlsl_handle, pdata, lpa_freq);
206 }
207 return 0;
208}
209
210static int mpu6050_set_fsr(void *mlsl_handle,
211 struct ext_slave_platform_data *pdata,
212 struct mpu6050_config *config, long apply, long fsr)
213{
214 unsigned char fsr_mask;
215 int result;
216
217 if (fsr <= 2000) {
218 config->fsr = 2000;
219 fsr_mask = 0x00;
220 } else if (fsr <= 4000) {
221 config->fsr = 4000;
222 fsr_mask = 0x08;
223 } else if (fsr <= 8000) {
224 config->fsr = 8000;
225 fsr_mask = 0x10;
226 } else { /* fsr = [8001, oo) */
227 config->fsr = 16000;
228 fsr_mask = 0x18;
229 }
230
231 if (apply) {
232 unsigned char reg;
233 result = inv_serial_read(mlsl_handle, pdata->address,
234 MPUREG_ACCEL_CONFIG, 1, &reg);
235 if (result) {
236 LOG_RESULT_LOCATION(result);
237 return result;
238 }
239 result = inv_serial_single_write(mlsl_handle, pdata->address,
240 MPUREG_ACCEL_CONFIG,
241 reg | fsr_mask);
242 if (result) {
243 LOG_RESULT_LOCATION(result);
244 return result;
245 }
246 MPL_LOGV("FSR: %d\n", config->fsr);
247 }
248 return 0;
249}
250
251static int mpu6050_set_irq(void *mlsl_handle,
252 struct ext_slave_platform_data *pdata,
253 struct mpu6050_config *config, long apply,
254 long irq_type)
255{
256 int result;
257 unsigned char reg_int_cfg;
258
259 switch (irq_type) {
260 case MPU_SLAVE_IRQ_TYPE_DATA_READY:
261 config->irq_type = irq_type;
262 reg_int_cfg = BIT_RAW_RDY_EN;
263 break;
264 /* todo: add MOTION, NO_MOTION, and FREEFALL */
265 case MPU_SLAVE_IRQ_TYPE_NONE:
266 /* Do nothing, not even set the interrupt because it is
267 shared with the gyro */
268 config->irq_type = irq_type;
269 return 0;
270 default:
271 return INV_ERROR_INVALID_PARAMETER;
272 }
273
274 if (apply) {
275 result = inv_serial_single_write(mlsl_handle, pdata->address,
276 MPUREG_INT_ENABLE,
277 reg_int_cfg);
278 if (result) {
279 LOG_RESULT_LOCATION(result);
280 return result;
281 }
282 MPL_LOGV("irq_type: %d\n", config->irq_type);
283 }
284
285 return 0;
286}
287
288static int mpu6050_set_ths(void *mlsl_handle,
289 struct ext_slave_platform_data *slave,
290 struct mpu6050_config *config, long apply, long ths)
291{
292 if (ths < 0)
293 ths = 0;
294
295 config->ths = ths;
296 MPL_LOGV("THS: %d\n", config->ths);
297 return 0;
298}
299
300static int mpu6050_set_dur(void *mlsl_handle,
301 struct ext_slave_platform_data *slave,
302 struct mpu6050_config *config, long apply, long dur)
303{
304 if (dur < 0)
305 dur = 0;
306
307 config->dur = dur;
308 MPL_LOGV("DUR: %d\n", config->dur);
309 return 0;
310}
311
312
313static int mpu6050_init(void *mlsl_handle,
314 struct ext_slave_descr *slave,
315 struct ext_slave_platform_data *pdata)
316{
317 int result;
318 struct mpu6050_private_data *private_data;
319
320
321 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
322
323 if (!private_data)
324 return INV_ERROR_MEMORY_EXAUSTED;
325
326 pdata->private_data = private_data;
327
328 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
329 false, 0);
330 if (result) {
331 LOG_RESULT_LOCATION(result);
332 return result;
333 }
334 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
335 false, 200000);
336 if (result) {
337 LOG_RESULT_LOCATION(result);
338 return result;
339 }
340 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend,
341 false, 2000);
342 if (result) {
343 LOG_RESULT_LOCATION(result);
344 return result;
345 }
346 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
347 false, 2000);
348 if (result) {
349 LOG_RESULT_LOCATION(result);
350 return result;
351 }
352
353 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
354 false, MPU_SLAVE_IRQ_TYPE_NONE);
355 if (result) {
356 LOG_RESULT_LOCATION(result);
357 return result;
358 }
359 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
360 false, MPU_SLAVE_IRQ_TYPE_NONE);
361 if (result) {
362 LOG_RESULT_LOCATION(result);
363 return result;
364 }
365
366 result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend,
367 false, 80);
368 if (result) {
369 LOG_RESULT_LOCATION(result);
370 return result;
371 }
372 result = mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume,
373 false, 40);
374 if (result) {
375 LOG_RESULT_LOCATION(result);
376 return result;
377 }
378 result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend,
379 false, 1000);
380 if (result) {
381 LOG_RESULT_LOCATION(result);
382 return result;
383 }
384 result = mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume,
385 false, 2540);
386 if (result) {
387 LOG_RESULT_LOCATION(result);
388 return result;
389 }
390
391 return 0;
392}
393
394static int mpu6050_exit(void *mlsl_handle,
395 struct ext_slave_descr *slave,
396 struct ext_slave_platform_data *pdata)
397{
398 kfree(pdata->private_data);
399 pdata->private_data = NULL;
400 return 0;
401}
402
403static int mpu6050_suspend(void *mlsl_handle,
404 struct ext_slave_descr *slave,
405 struct ext_slave_platform_data *pdata)
406{
407 unsigned char reg;
408 int result;
409 struct mpu6050_private_data *private_data =
410 (struct mpu6050_private_data *)pdata->private_data;
411
412 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend,
413 true, private_data->suspend.odr);
414 if (result) {
415 LOG_RESULT_LOCATION(result);
416 return result;
417 }
418
419 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->suspend,
420 true, private_data->suspend.irq_type);
421 if (result) {
422 LOG_RESULT_LOCATION(result);
423 return result;
424 }
425
426 result = inv_serial_read(mlsl_handle, pdata->address,
427 MPUREG_PWR_MGMT_2, 1, &reg);
428 if (result) {
429 LOG_RESULT_LOCATION(result);
430 return result;
431 }
432 reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
433
434 result = inv_serial_single_write(mlsl_handle, pdata->address,
435 MPUREG_PWR_MGMT_2, reg);
436 if (result) {
437 LOG_RESULT_LOCATION(result);
438 return result;
439 }
440
441 return 0;
442}
443
444static int mpu6050_resume(void *mlsl_handle,
445 struct ext_slave_descr *slave,
446 struct ext_slave_platform_data *pdata)
447{
448 int result;
449 unsigned char reg;
450 struct mpu6050_private_data *private_data =
451 (struct mpu6050_private_data *)pdata->private_data;
452
453 result = inv_serial_read(mlsl_handle, pdata->address,
454 MPUREG_PWR_MGMT_1, 1, &reg);
455 if (result) {
456 LOG_RESULT_LOCATION(result);
457 return result;
458 }
459
460 if (reg & BIT_SLEEP) {
461 result = inv_serial_single_write(mlsl_handle, pdata->address,
462 MPUREG_PWR_MGMT_1, reg & ~BIT_SLEEP);
463 if (result) {
464 LOG_RESULT_LOCATION(result);
465 return result;
466 }
467 }
468 msleep(2);
469
470 result = inv_serial_read(mlsl_handle, pdata->address,
471 MPUREG_PWR_MGMT_2, 1, &reg);
472 if (result) {
473 LOG_RESULT_LOCATION(result);
474 return result;
475 }
476 reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
477 result = inv_serial_single_write(mlsl_handle, pdata->address,
478 MPUREG_PWR_MGMT_2, reg);
479 if (result) {
480 LOG_RESULT_LOCATION(result);
481 return result;
482 }
483
484 /* settings */
485
486 result = mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume,
487 true, private_data->resume.fsr);
488 if (result) {
489 LOG_RESULT_LOCATION(result);
490 return result;
491 }
492 result = mpu6050_set_odr(mlsl_handle, pdata, &private_data->resume,
493 true, private_data->resume.odr);
494 if (result) {
495 LOG_RESULT_LOCATION(result);
496 return result;
497 }
498 result = mpu6050_set_irq(mlsl_handle, pdata, &private_data->resume,
499 true, private_data->resume.irq_type);
500
501 /* motion, no_motion */
502 /* TODO : port these in their respective _set_thrs and _set_dur
503 functions and use the APPLY paremeter to apply just like
504 _set_odr, _set_irq, and _set_fsr. */
505 reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB;
506 result = inv_serial_single_write(mlsl_handle, pdata->address,
507 MPUREG_ACCEL_MOT_THR, reg);
508 if (result) {
509 LOG_RESULT_LOCATION(result);
510 return result;
511 }
512 reg = (unsigned char)
513 ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths);
514 result = inv_serial_single_write(mlsl_handle, pdata->address,
515 MPUREG_ACCEL_ZRMOT_THR, reg);
516 if (result) {
517 LOG_RESULT_LOCATION(result);
518 return result;
519 }
520 reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
521 result = inv_serial_single_write(mlsl_handle, pdata->address,
522 MPUREG_ACCEL_MOT_DUR, reg);
523 if (result) {
524 LOG_RESULT_LOCATION(result);
525 return result;
526 }
527 reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB;
528 result = inv_serial_single_write(mlsl_handle, pdata->address,
529 MPUREG_ACCEL_ZRMOT_DUR, reg);
530 if (result) {
531 LOG_RESULT_LOCATION(result);
532 return result;
533 }
534 return 0;
535}
536
537static int mpu6050_read(void *mlsl_handle,
538 struct ext_slave_descr *slave,
539 struct ext_slave_platform_data *pdata,
540 unsigned char *data)
541{
542 int result;
543 result = inv_serial_read(mlsl_handle, pdata->address,
544 slave->read_reg, slave->read_len, data);
545 return result;
546}
547
548static int mpu6050_config(void *mlsl_handle,
549 struct ext_slave_descr *slave,
550 struct ext_slave_platform_data *pdata,
551 struct ext_slave_config *data)
552{
553 struct mpu6050_private_data *private_data =
554 (struct mpu6050_private_data *)pdata->private_data;
555 if (!data->data)
556 return INV_ERROR_INVALID_PARAMETER;
557
558 switch (data->key) {
559 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
560 return mpu6050_set_odr(mlsl_handle, pdata,
561 &private_data->suspend,
562 data->apply, *((long *)data->data));
563 case MPU_SLAVE_CONFIG_ODR_RESUME:
564 return mpu6050_set_odr(mlsl_handle, pdata,
565 &private_data->resume,
566 data->apply, *((long *)data->data));
567 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
568 return mpu6050_set_fsr(mlsl_handle, pdata,
569 &private_data->suspend,
570 data->apply, *((long *)data->data));
571 case MPU_SLAVE_CONFIG_FSR_RESUME:
572 return mpu6050_set_fsr(mlsl_handle, pdata,
573 &private_data->resume,
574 data->apply, *((long *)data->data));
575 case MPU_SLAVE_CONFIG_MOT_THS:
576 return mpu6050_set_ths(mlsl_handle, pdata,
577 &private_data->suspend,
578 data->apply, *((long *)data->data));
579 case MPU_SLAVE_CONFIG_NMOT_THS:
580 return mpu6050_set_ths(mlsl_handle, pdata,
581 &private_data->resume,
582 data->apply, *((long *)data->data));
583 case MPU_SLAVE_CONFIG_MOT_DUR:
584 return mpu6050_set_dur(mlsl_handle, pdata,
585 &private_data->suspend,
586 data->apply, *((long *)data->data));
587 case MPU_SLAVE_CONFIG_NMOT_DUR:
588 return mpu6050_set_dur(mlsl_handle, pdata,
589 &private_data->resume,
590 data->apply, *((long *)data->data));
591 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
592 return mpu6050_set_irq(mlsl_handle, pdata,
593 &private_data->suspend,
594 data->apply, *((long *)data->data));
595 break;
596 case MPU_SLAVE_CONFIG_IRQ_RESUME:
597 return mpu6050_set_irq(mlsl_handle, pdata,
598 &private_data->resume,
599 data->apply, *((long *)data->data));
600 case MPU_SLAVE_CONFIG_INTERNAL_REFERENCE:
601 return mpu6050_set_mldl_cfg_ref(mlsl_handle, pdata,
602 (struct mldl_cfg *)data->data);
603 break;
604
605 default:
606 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
607 };
608
609 return 0;
610}
611
612static int mpu6050_get_config(void *mlsl_handle,
613 struct ext_slave_descr *slave,
614 struct ext_slave_platform_data *pdata,
615 struct ext_slave_config *data)
616{
617 struct mpu6050_private_data *private_data =
618 (struct mpu6050_private_data *)pdata->private_data;
619 if (!data->data)
620 return INV_ERROR_INVALID_PARAMETER;
621
622 switch (data->key) {
623 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
624 (*(unsigned long *)data->data) =
625 (unsigned long)private_data->suspend.odr;
626 break;
627 case MPU_SLAVE_CONFIG_ODR_RESUME:
628 (*(unsigned long *)data->data) =
629 (unsigned long)private_data->resume.odr;
630 break;
631 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
632 (*(unsigned long *)data->data) =
633 (unsigned long)private_data->suspend.fsr;
634 break;
635 case MPU_SLAVE_CONFIG_FSR_RESUME:
636 (*(unsigned long *)data->data) =
637 (unsigned long)private_data->resume.fsr;
638 break;
639 case MPU_SLAVE_CONFIG_MOT_THS:
640 (*(unsigned long *)data->data) =
641 (unsigned long)private_data->suspend.ths;
642 break;
643 case MPU_SLAVE_CONFIG_NMOT_THS:
644 (*(unsigned long *)data->data) =
645 (unsigned long)private_data->resume.ths;
646 break;
647 case MPU_SLAVE_CONFIG_MOT_DUR:
648 (*(unsigned long *)data->data) =
649 (unsigned long)private_data->suspend.dur;
650 break;
651 case MPU_SLAVE_CONFIG_NMOT_DUR:
652 (*(unsigned long *)data->data) =
653 (unsigned long)private_data->resume.dur;
654 break;
655 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
656 (*(unsigned long *)data->data) =
657 (unsigned long)private_data->suspend.irq_type;
658 break;
659 case MPU_SLAVE_CONFIG_IRQ_RESUME:
660 (*(unsigned long *)data->data) =
661 (unsigned long)private_data->resume.irq_type;
662 break;
663 default:
664 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
665 };
666
667 return 0;
668}
669
670static struct ext_slave_descr mpu6050_descr = {
671 .init = mpu6050_init,
672 .exit = mpu6050_exit,
673 .suspend = mpu6050_suspend,
674 .resume = mpu6050_resume,
675 .read = mpu6050_read,
676 .config = mpu6050_config,
677 .get_config = mpu6050_get_config,
678 .name = "mpu6050",
679 .type = EXT_SLAVE_TYPE_ACCEL,
680 .id = ACCEL_ID_MPU6050,
681 .read_reg = 0x3B,
682 .read_len = 6,
683 .endian = EXT_SLAVE_BIG_ENDIAN,
684 .range = {2, 0},
685 .trigger = NULL,
686};
687
688struct ext_slave_descr *mpu6050_get_slave_descr(void)
689{
690 return &mpu6050_descr;
691}
692
693/**
694 * @}
695 */
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.h b/drivers/misc/inv_mpu/accel/mpu6050.h
new file mode 100644
index 00000000000..c347bcb4d77
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mpu6050.h
@@ -0,0 +1,28 @@
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#ifndef __MPU6050_H__
22#define __MPU6050_H__
23
24#include <linux/mpu.h>
25
26struct ext_slave_descr *mpu6050_get_slave_descr(void);
27
28#endif
diff --git a/drivers/misc/inv_mpu/compass/Kconfig b/drivers/misc/inv_mpu/compass/Kconfig
new file mode 100644
index 00000000000..7e6bac87d31
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/Kconfig
@@ -0,0 +1,130 @@
1menuconfig INV_SENSORS_COMPASS
2 bool "Compass Slave Sensors"
3 default y
4 ---help---
5 Say Y here to get to see options for device drivers for various
6 compasses. This option alone does not add any kernel code.
7
8 If you say N, all options in this submenu will be skipped and disabled.
9
10if INV_SENSORS_COMPASS
11
12config MPU_SENSORS_AK8963
13 tristate "AKM ak8963"
14 help
15 This enables support for the AKM ak8963 compass
16 This support is for integration with the MPU3050 or MPU6050 gyroscope
17 device driver. Only one compass can be registered at a time.
18 Specifying more that one compass in the board file will result
19 in runtime errors.
20
21config MPU_SENSORS_AK8975
22 tristate "AKM ak8975"
23 help
24 This enables support for the AKM ak8975 compass
25 This support is for integration with the MPU3050 or MPU6050 gyroscope
26 device driver. Only one compass can be registered at a time.
27 Specifying more that one compass in the board file will result
28 in runtime errors.
29
30config MPU_SENSORS_AK8972
31 tristate "AKM ak8972"
32 help
33 This enables support for the AKM ak8972 compass
34 This support is for integration with the MPU3050 or MPU6050 gyroscope
35 device driver. Only one compass can be registered at a time.
36 Specifying more that one compass in the board file will result
37 in runtime errors.
38
39config MPU_SENSORS_MMC314X
40 tristate "MEMSIC mmc314x"
41 help
42 This enables support for the MEMSIC mmc314x compass
43 This support is for integration with the MPU3050 or MPU6050 gyroscope
44 device driver. Only one compass can be registered at a time.
45 Specifying more that one compass in the board file will result
46 in runtime errors.
47
48config MPU_SENSORS_AMI30X
49 tristate "Aichi Steel ami30X"
50 help
51 This enables support for the Aichi Steel ami304/ami305 compass
52 This support is for integration with the MPU3050 or MPU6050 gyroscope
53 device driver. Only one compass can be registered at a time.
54 Specifying more that one compass in the board file will result
55 in runtime errors.
56
57config MPU_SENSORS_AMI306
58 tristate "Aichi Steel ami306"
59 help
60 This enables support for the Aichi Steel ami306 compass
61 This support is for integration with the MPU3050 or MPU6050 gyroscope
62 device driver. Only one compass can be registered at a time.
63 Specifying more that one compass in the board file will result
64 in runtime errors.
65
66config MPU_SENSORS_HMC5883
67 tristate "Honeywell hmc5883"
68 help
69 This enables support for the Honeywell hmc5883 compass
70 This support is for integration with the MPU3050 or MPU6050 gyroscope
71 device driver. Only one compass can be registered at a time.
72 Specifying more that one compass in the board file will result
73 in runtime errors.
74
75config MPU_SENSORS_LSM303DLX_M
76 tristate "ST lsm303dlx"
77 help
78 This enables support for the ST lsm303dlh and lsm303dlm compasses
79 This support is for integration with the MPU3050 or MPU6050 gyroscope
80 device driver. Only one compass can be registered at a time.
81 Specifying more that one compass in the board file will result
82 in runtime errors.
83
84config MPU_SENSORS_MMC314XMS
85 tristate "MEMSIC mmc314xMS"
86 help
87 This enables support for the MEMSIC mmc314xMS compass
88 This support is for integration with the MPU3050 or MPU6050 gyroscope
89 device driver. Only one compass can be registered at a time.
90 Specifying more that one compass in the board file will result
91 in runtime errors.
92
93config MPU_SENSORS_YAS529
94 tristate "Yamaha yas529"
95 depends on INPUT_YAS_MAGNETOMETER
96 help
97 This enables support for the Yamaha yas529 compass
98 This support is for integration with the MPU3050 or MPU6050 gyroscope
99 device driver. Only one compass can be registered at a time.
100 Specifying more that one compass in the board file will result
101 in runtime errors.
102
103config MPU_SENSORS_YAS530
104 tristate "Yamaha yas530"
105 help
106 This enables support for the Yamaha yas530 compass
107 This support is for integration with the MPU3050 or MPU6050 gyroscope
108 device driver. Only one compass can be registered at a time.
109 Specifying more that one compass in the board file will result
110 in runtime errors.
111
112config MPU_SENSORS_HSCDTD002B
113 tristate "Alps hscdtd002b"
114 help
115 This enables support for the Alps hscdtd002b compass
116 This support is for integration with the MPU3050 or MPU6050 gyroscope
117 device driver. Only one compass can be registered at a time.
118 Specifying more that one compass in the board file will result
119 in runtime errors.
120
121config MPU_SENSORS_HSCDTD004A
122 tristate "Alps hscdtd004a"
123 help
124 This enables support for the Alps hscdtd004a compass
125 This support is for integration with the MPU3050 or MPU6050 gyroscope
126 device driver. Only one compass can be registered at a time.
127 Specifying more that one compass in the board file will result
128 in runtime errors.
129
130endif
diff --git a/drivers/misc/inv_mpu/compass/Makefile b/drivers/misc/inv_mpu/compass/Makefile
new file mode 100644
index 00000000000..d76ce1b06f3
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/Makefile
@@ -0,0 +1,41 @@
1#
2# Compass Slaves MPUxxxx
3#
4obj-$(CONFIG_MPU_SENSORS_AMI30X) += inv_mpu_ami30x.o
5inv_mpu_ami30x-objs += ami30x.o
6
7obj-$(CONFIG_MPU_SENSORS_AMI306) += inv_mpu_ami306.o
8inv_mpu_ami306-objs += ami306.o
9
10obj-$(CONFIG_MPU_SENSORS_HMC5883) += inv_mpu_hmc5883.o
11inv_mpu_hmc5883-objs += hmc5883.o
12
13obj-$(CONFIG_MPU_SENSORS_LSM303DLX_M) += inv_mpu_lsm303dlx_m.o
14inv_mpu_lsm303dlx_m-objs += lsm303dlx_m.o
15
16obj-$(CONFIG_MPU_SENSORS_MMC314X) += inv_mpu_mmc314x.o
17inv_mpu_mmc314x-objs += mmc314x.o
18
19obj-$(CONFIG_MPU_SENSORS_YAS529) += inv_mpu_yas529.o
20inv_mpu_yas529-objs += yas529-kernel.o
21
22obj-$(CONFIG_MPU_SENSORS_YAS530) += inv_mpu_yas530.o
23inv_mpu_yas530-objs += yas530.o
24
25obj-$(CONFIG_MPU_SENSORS_HSCDTD002B) += inv_mpu_hscdtd002b.o
26inv_mpu_hscdtd002b-objs += hscdtd002b.o
27
28obj-$(CONFIG_MPU_SENSORS_HSCDTD004A) += inv_mpu_hscdtd004a.o
29inv_mpu_hscdtd004a-objs += hscdtd004a.o
30
31obj-$(CONFIG_MPU_SENSORS_AK8963) += inv_mpu_ak89xx.o
32inv_mpu_ak89xx-objs += ak89xx.o
33
34obj-$(CONFIG_MPU_SENSORS_AK8975) += inv_mpu_ak8975.o
35inv_mpu_ak8975-objs += ak8975.o
36
37obj-$(CONFIG_MPU_SENSORS_AK8972) += inv_mpu_ak8972.o
38inv_mpu_ak8972-objs += ak8972.o
39
40EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
41EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
diff --git a/drivers/misc/inv_mpu/compass/ak8972.c b/drivers/misc/inv_mpu/compass/ak8972.c
new file mode 100644
index 00000000000..7eb15b44039
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ak8972.c
@@ -0,0 +1,499 @@
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 COMPASSDL
22 *
23 * @{
24 * @file ak8972.c
25 * @brief Magnetometer setup and handling methods for the AKM AK8972 compass device.
26 */
27
28/* -------------------------------------------------------------------------- */
29
30#include <linux/i2c.h>
31#include <linux/module.h>
32#include <linux/moduleparam.h>
33#include <linux/kernel.h>
34#include <linux/errno.h>
35#include <linux/slab.h>
36#include <linux/delay.h>
37#include "mpu-dev.h"
38
39#include <log.h>
40#include <linux/mpu.h>
41#include "mlsl.h"
42#include "mldl_cfg.h"
43#undef MPL_LOG_TAG
44#define MPL_LOG_TAG "MPL-compass"
45
46/* -------------------------------------------------------------------------- */
47#define AK8972_REG_ST1 (0x02)
48#define AK8972_REG_HXL (0x03)
49#define AK8972_REG_ST2 (0x09)
50
51#define AK8972_REG_CNTL (0x0A)
52#define AK8972_REG_ASAX (0x10)
53#define AK8972_REG_ASAY (0x11)
54#define AK8972_REG_ASAZ (0x12)
55
56#define AK8972_CNTL_MODE_POWER_DOWN (0x00)
57#define AK8972_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
58#define AK8972_CNTL_MODE_FUSE_ROM_ACCESS (0x0f)
59
60/* -------------------------------------------------------------------------- */
61struct ak8972_config {
62 char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */
63};
64
65struct ak8972_private_data {
66 struct ak8972_config init;
67};
68
69/* -------------------------------------------------------------------------- */
70static int ak8972_init(void *mlsl_handle,
71 struct ext_slave_descr *slave,
72 struct ext_slave_platform_data *pdata)
73{
74 int result;
75 unsigned char serial_data[COMPASS_NUM_AXES];
76
77 struct ak8972_private_data *private_data;
78 private_data = (struct ak8972_private_data *)
79 kzalloc(sizeof(struct ak8972_private_data), GFP_KERNEL);
80
81 if (!private_data)
82 return INV_ERROR_MEMORY_EXAUSTED;
83
84 result = inv_serial_single_write(mlsl_handle, pdata->address,
85 AK8972_REG_CNTL,
86 AK8972_CNTL_MODE_POWER_DOWN);
87 if (result) {
88 LOG_RESULT_LOCATION(result);
89 return result;
90 }
91 /* Wait at least 100us */
92 udelay(100);
93
94 result = inv_serial_single_write(mlsl_handle, pdata->address,
95 AK8972_REG_CNTL,
96 AK8972_CNTL_MODE_FUSE_ROM_ACCESS);
97 if (result) {
98 LOG_RESULT_LOCATION(result);
99 return result;
100 }
101
102 /* Wait at least 200us */
103 udelay(200);
104
105 result = inv_serial_read(mlsl_handle, pdata->address,
106 AK8972_REG_ASAX,
107 COMPASS_NUM_AXES, serial_data);
108 if (result) {
109 LOG_RESULT_LOCATION(result);
110 return result;
111 }
112
113 pdata->private_data = private_data;
114
115 private_data->init.asa[0] = serial_data[0];
116 private_data->init.asa[1] = serial_data[1];
117 private_data->init.asa[2] = serial_data[2];
118
119 result = inv_serial_single_write(mlsl_handle, pdata->address,
120 AK8972_REG_CNTL,
121 AK8972_CNTL_MODE_POWER_DOWN);
122 if (result) {
123 LOG_RESULT_LOCATION(result);
124 return result;
125 }
126
127 udelay(100);
128 return INV_SUCCESS;
129}
130
131static int ak8972_exit(void *mlsl_handle,
132 struct ext_slave_descr *slave,
133 struct ext_slave_platform_data *pdata)
134{
135 kfree(pdata->private_data);
136 return INV_SUCCESS;
137}
138
139static int ak8972_suspend(void *mlsl_handle,
140 struct ext_slave_descr *slave,
141 struct ext_slave_platform_data *pdata)
142{
143 int result = INV_SUCCESS;
144 result =
145 inv_serial_single_write(mlsl_handle, pdata->address,
146 AK8972_REG_CNTL,
147 AK8972_CNTL_MODE_POWER_DOWN);
148 msleep(1); /* wait at least 100us */
149 if (result) {
150 LOG_RESULT_LOCATION(result);
151 return result;
152 }
153 return result;
154}
155
156static int ak8972_resume(void *mlsl_handle,
157 struct ext_slave_descr *slave,
158 struct ext_slave_platform_data *pdata)
159{
160 int result = INV_SUCCESS;
161 result =
162 inv_serial_single_write(mlsl_handle, pdata->address,
163 AK8972_REG_CNTL,
164 AK8972_CNTL_MODE_SINGLE_MEASUREMENT);
165 if (result) {
166 LOG_RESULT_LOCATION(result);
167 return result;
168 }
169 return result;
170}
171
172static int ak8972_read(void *mlsl_handle,
173 struct ext_slave_descr *slave,
174 struct ext_slave_platform_data *pdata, unsigned char *data)
175{
176 unsigned char regs[8];
177 unsigned char *stat = &regs[0];
178 unsigned char *stat2 = &regs[7];
179 int result = INV_SUCCESS;
180 int status = INV_SUCCESS;
181
182 result =
183 inv_serial_read(mlsl_handle, pdata->address, AK8972_REG_ST1,
184 8, regs);
185 if (result) {
186 LOG_RESULT_LOCATION(result);
187 return result;
188 }
189
190 /* Always return the data and the status registers */
191 memcpy(data, &regs[1], 6);
192 data[6] = regs[0];
193 data[7] = regs[7];
194
195 /*
196 * ST : data ready -
197 * Measurement has been completed and data is ready to be read.
198 */
199 if (*stat & 0x01)
200 status = INV_SUCCESS;
201
202 /*
203 * ST2 : data error -
204 * occurs when data read is started outside of a readable period;
205 * data read would not be correct.
206 * Valid in continuous measurement mode only.
207 * In single measurement mode this error should not occour but we
208 * stil account for it and return an error, since the data would be
209 * corrupted.
210 * DERR bit is self-clearing when ST2 register is read.
211 */
212 if (*stat2 & 0x04)
213 status = INV_ERROR_COMPASS_DATA_ERROR;
214 /*
215 * ST2 : overflow -
216 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
217 * This is likely to happen in presence of an external magnetic
218 * disturbance; it indicates, the sensor data is incorrect and should
219 * be ignored.
220 * An error is returned.
221 * HOFL bit clears when a new measurement starts.
222 */
223 if (*stat2 & 0x08)
224 status = INV_ERROR_COMPASS_DATA_OVERFLOW;
225 /*
226 * ST : overrun -
227 * the previous sample was not fetched and lost.
228 * Valid in continuous measurement mode only.
229 * In single measurement mode this error should not occour and we
230 * don't consider this condition an error.
231 * DOR bit is self-clearing when ST2 or any meas. data register is
232 * read.
233 */
234 if (*stat & 0x02) {
235 /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
236 status = INV_SUCCESS;
237 }
238
239 /*
240 * trigger next measurement if:
241 * - stat is non zero;
242 * - if stat is zero and stat2 is non zero.
243 * Won't trigger if data is not ready and there was no error.
244 */
245 if (*stat != 0x00 || *stat2 != 0x00) {
246 result = inv_serial_single_write(
247 mlsl_handle, pdata->address,
248 AK8972_REG_CNTL, AK8972_CNTL_MODE_SINGLE_MEASUREMENT);
249 if (result) {
250 LOG_RESULT_LOCATION(result);
251 return result;
252 }
253 }
254
255 return status;
256}
257
258static int ak8972_config(void *mlsl_handle,
259 struct ext_slave_descr *slave,
260 struct ext_slave_platform_data *pdata,
261 struct ext_slave_config *data)
262{
263 int result;
264 if (!data->data)
265 return INV_ERROR_INVALID_PARAMETER;
266
267 switch (data->key) {
268 case MPU_SLAVE_WRITE_REGISTERS:
269 result = inv_serial_write(mlsl_handle, pdata->address,
270 data->len,
271 (unsigned char *)data->data);
272 if (result) {
273 LOG_RESULT_LOCATION(result);
274 return result;
275 }
276 break;
277 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
278 case MPU_SLAVE_CONFIG_ODR_RESUME:
279 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
280 case MPU_SLAVE_CONFIG_FSR_RESUME:
281 case MPU_SLAVE_CONFIG_MOT_THS:
282 case MPU_SLAVE_CONFIG_NMOT_THS:
283 case MPU_SLAVE_CONFIG_MOT_DUR:
284 case MPU_SLAVE_CONFIG_NMOT_DUR:
285 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
286 case MPU_SLAVE_CONFIG_IRQ_RESUME:
287 default:
288 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
289 };
290
291 return INV_SUCCESS;
292}
293
294static int ak8972_get_config(void *mlsl_handle,
295 struct ext_slave_descr *slave,
296 struct ext_slave_platform_data *pdata,
297 struct ext_slave_config *data)
298{
299 struct ak8972_private_data *private_data = pdata->private_data;
300 int result;
301 if (!data->data)
302 return INV_ERROR_INVALID_PARAMETER;
303
304 switch (data->key) {
305 case MPU_SLAVE_READ_REGISTERS:
306 {
307 unsigned char *serial_data =
308 (unsigned char *)data->data;
309 result =
310 inv_serial_read(mlsl_handle, pdata->address,
311 serial_data[0], data->len - 1,
312 &serial_data[1]);
313 if (result) {
314 LOG_RESULT_LOCATION(result);
315 return result;
316 }
317 break;
318 }
319 case MPU_SLAVE_READ_SCALE:
320 {
321 unsigned char *serial_data =
322 (unsigned char *)data->data;
323 serial_data[0] = private_data->init.asa[0];
324 serial_data[1] = private_data->init.asa[1];
325 serial_data[2] = private_data->init.asa[2];
326 result = INV_SUCCESS;
327 if (result) {
328 LOG_RESULT_LOCATION(result);
329 return result;
330 }
331 break;
332 }
333 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
334 (*(unsigned long *)data->data) = 0;
335 break;
336 case MPU_SLAVE_CONFIG_ODR_RESUME:
337 (*(unsigned long *)data->data) = 8000;
338 break;
339 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
340 case MPU_SLAVE_CONFIG_FSR_RESUME:
341 case MPU_SLAVE_CONFIG_MOT_THS:
342 case MPU_SLAVE_CONFIG_NMOT_THS:
343 case MPU_SLAVE_CONFIG_MOT_DUR:
344 case MPU_SLAVE_CONFIG_NMOT_DUR:
345 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
346 case MPU_SLAVE_CONFIG_IRQ_RESUME:
347 default:
348 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
349 };
350
351 return INV_SUCCESS;
352}
353
354static struct ext_slave_read_trigger ak8972_read_trigger = {
355 /*.reg = */ 0x0A,
356 /*.value = */ 0x01
357};
358
359static struct ext_slave_descr ak8972_descr = {
360 .init = ak8972_init,
361 .exit = ak8972_exit,
362 .suspend = ak8972_suspend,
363 .resume = ak8972_resume,
364 .read = ak8972_read,
365 .config = ak8972_config,
366 .get_config = ak8972_get_config,
367 .name = "ak8972",
368 .type = EXT_SLAVE_TYPE_COMPASS,
369 .id = COMPASS_ID_AK8972,
370 .read_reg = 0x01,
371 .read_len = 9,
372 .endian = EXT_SLAVE_LITTLE_ENDIAN,
373 .range = {39321, 6000},
374 .trigger = &ak8972_read_trigger,
375};
376
377static
378struct ext_slave_descr *ak8972_get_slave_descr(void)
379{
380 return &ak8972_descr;
381}
382
383/* -------------------------------------------------------------------------- */
384struct ak8972_mod_private_data {
385 struct i2c_client *client;
386 struct ext_slave_platform_data *pdata;
387};
388
389static unsigned short normal_i2c[] = { I2C_CLIENT_END };
390
391static int ak8972_mod_probe(struct i2c_client *client,
392 const struct i2c_device_id *devid)
393{
394 struct ext_slave_platform_data *pdata;
395 struct ak8972_mod_private_data *private_data;
396 int result = 0;
397
398 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
399
400 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
401 result = -ENODEV;
402 goto out_no_free;
403 }
404
405 pdata = client->dev.platform_data;
406 if (!pdata) {
407 dev_err(&client->adapter->dev,
408 "Missing platform data for slave %s\n", devid->name);
409 result = -EFAULT;
410 goto out_no_free;
411 }
412
413 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
414 if (!private_data) {
415 result = -ENOMEM;
416 goto out_no_free;
417 }
418
419 i2c_set_clientdata(client, private_data);
420 private_data->client = client;
421 private_data->pdata = pdata;
422
423 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
424 ak8972_get_slave_descr);
425 if (result) {
426 dev_err(&client->adapter->dev,
427 "Slave registration failed: %s, %d\n",
428 devid->name, result);
429 goto out_free_memory;
430 }
431
432 return result;
433
434out_free_memory:
435 kfree(private_data);
436out_no_free:
437 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
438 return result;
439
440}
441
442static int ak8972_mod_remove(struct i2c_client *client)
443{
444 struct ak8972_mod_private_data *private_data =
445 i2c_get_clientdata(client);
446
447 dev_dbg(&client->adapter->dev, "%s\n", __func__);
448 inv_mpu_unregister_slave(client, private_data->pdata,
449 ak8972_get_slave_descr);
450
451 kfree(private_data);
452 return 0;
453}
454
455static const struct i2c_device_id ak8972_mod_id[] = {
456 { "ak8972", COMPASS_ID_AK8972 },
457 {}
458};
459
460MODULE_DEVICE_TABLE(i2c, ak8972_mod_id);
461
462static struct i2c_driver ak8972_mod_driver = {
463 .class = I2C_CLASS_HWMON,
464 .probe = ak8972_mod_probe,
465 .remove = ak8972_mod_remove,
466 .id_table = ak8972_mod_id,
467 .driver = {
468 .owner = THIS_MODULE,
469 .name = "ak8972_mod",
470 },
471 .address_list = normal_i2c,
472};
473
474static int __init ak8972_mod_init(void)
475{
476 int res = i2c_add_driver(&ak8972_mod_driver);
477 pr_info("%s: Probe name %s\n", __func__, "ak8972_mod");
478 if (res)
479 pr_err("%s failed\n", __func__);
480 return res;
481}
482
483static void __exit ak8972_mod_exit(void)
484{
485 pr_info("%s\n", __func__);
486 i2c_del_driver(&ak8972_mod_driver);
487}
488
489module_init(ak8972_mod_init);
490module_exit(ak8972_mod_exit);
491
492MODULE_AUTHOR("Invensense Corporation");
493MODULE_DESCRIPTION("Driver to integrate AK8972 sensor with the MPU");
494MODULE_LICENSE("GPL");
495MODULE_ALIAS("ak8972_mod");
496
497/**
498 * @}
499 */
diff --git a/drivers/misc/inv_mpu/compass/ak8975.c b/drivers/misc/inv_mpu/compass/ak8975.c
new file mode 100644
index 00000000000..3642e29e89a
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ak8975.c
@@ -0,0 +1,500 @@
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 COMPASSDL
22 *
23 * @{
24 * @file ak8975.c
25 * @brief Magnetometer setup and handling methods for the AKM AK8975,
26 * AKM AK8975B, and AKM AK8975C compass devices.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48#define AK8975_REG_ST1 (0x02)
49#define AK8975_REG_HXL (0x03)
50#define AK8975_REG_ST2 (0x09)
51
52#define AK8975_REG_CNTL (0x0A)
53#define AK8975_REG_ASAX (0x10)
54#define AK8975_REG_ASAY (0x11)
55#define AK8975_REG_ASAZ (0x12)
56
57#define AK8975_CNTL_MODE_POWER_DOWN (0x00)
58#define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
59#define AK8975_CNTL_MODE_FUSE_ROM_ACCESS (0x0f)
60
61/* -------------------------------------------------------------------------- */
62struct ak8975_config {
63 char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */
64};
65
66struct ak8975_private_data {
67 struct ak8975_config init;
68};
69
70/* -------------------------------------------------------------------------- */
71static int ak8975_init(void *mlsl_handle,
72 struct ext_slave_descr *slave,
73 struct ext_slave_platform_data *pdata)
74{
75 int result;
76 unsigned char serial_data[COMPASS_NUM_AXES];
77
78 struct ak8975_private_data *private_data;
79 private_data = (struct ak8975_private_data *)
80 kzalloc(sizeof(struct ak8975_private_data), GFP_KERNEL);
81
82 if (!private_data)
83 return INV_ERROR_MEMORY_EXAUSTED;
84
85 result = inv_serial_single_write(mlsl_handle, pdata->address,
86 AK8975_REG_CNTL,
87 AK8975_CNTL_MODE_POWER_DOWN);
88 if (result) {
89 LOG_RESULT_LOCATION(result);
90 return result;
91 }
92 /* Wait at least 100us */
93 udelay(100);
94
95 result = inv_serial_single_write(mlsl_handle, pdata->address,
96 AK8975_REG_CNTL,
97 AK8975_CNTL_MODE_FUSE_ROM_ACCESS);
98 if (result) {
99 LOG_RESULT_LOCATION(result);
100 return result;
101 }
102
103 /* Wait at least 200us */
104 udelay(200);
105
106 result = inv_serial_read(mlsl_handle, pdata->address,
107 AK8975_REG_ASAX,
108 COMPASS_NUM_AXES, serial_data);
109 if (result) {
110 LOG_RESULT_LOCATION(result);
111 return result;
112 }
113
114 pdata->private_data = private_data;
115
116 private_data->init.asa[0] = serial_data[0];
117 private_data->init.asa[1] = serial_data[1];
118 private_data->init.asa[2] = serial_data[2];
119
120 result = inv_serial_single_write(mlsl_handle, pdata->address,
121 AK8975_REG_CNTL,
122 AK8975_CNTL_MODE_POWER_DOWN);
123 if (result) {
124 LOG_RESULT_LOCATION(result);
125 return result;
126 }
127
128 udelay(100);
129 return INV_SUCCESS;
130}
131
132static int ak8975_exit(void *mlsl_handle,
133 struct ext_slave_descr *slave,
134 struct ext_slave_platform_data *pdata)
135{
136 kfree(pdata->private_data);
137 return INV_SUCCESS;
138}
139
140static int ak8975_suspend(void *mlsl_handle,
141 struct ext_slave_descr *slave,
142 struct ext_slave_platform_data *pdata)
143{
144 int result = INV_SUCCESS;
145 result =
146 inv_serial_single_write(mlsl_handle, pdata->address,
147 AK8975_REG_CNTL,
148 AK8975_CNTL_MODE_POWER_DOWN);
149 msleep(1); /* wait at least 100us */
150 if (result) {
151 LOG_RESULT_LOCATION(result);
152 return result;
153 }
154 return result;
155}
156
157static int ak8975_resume(void *mlsl_handle,
158 struct ext_slave_descr *slave,
159 struct ext_slave_platform_data *pdata)
160{
161 int result = INV_SUCCESS;
162 result =
163 inv_serial_single_write(mlsl_handle, pdata->address,
164 AK8975_REG_CNTL,
165 AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
166 if (result) {
167 LOG_RESULT_LOCATION(result);
168 return result;
169 }
170 return result;
171}
172
173static int ak8975_read(void *mlsl_handle,
174 struct ext_slave_descr *slave,
175 struct ext_slave_platform_data *pdata, unsigned char *data)
176{
177 unsigned char regs[8];
178 unsigned char *stat = &regs[0];
179 unsigned char *stat2 = &regs[7];
180 int result = INV_SUCCESS;
181 int status = INV_SUCCESS;
182
183 result =
184 inv_serial_read(mlsl_handle, pdata->address, AK8975_REG_ST1,
185 8, regs);
186 if (result) {
187 LOG_RESULT_LOCATION(result);
188 return result;
189 }
190
191 /* Always return the data and the status registers */
192 memcpy(data, &regs[1], 6);
193 data[6] = regs[0];
194 data[7] = regs[7];
195
196 /*
197 * ST : data ready -
198 * Measurement has been completed and data is ready to be read.
199 */
200 if (*stat & 0x01)
201 status = INV_SUCCESS;
202
203 /*
204 * ST2 : data error -
205 * occurs when data read is started outside of a readable period;
206 * data read would not be correct.
207 * Valid in continuous measurement mode only.
208 * In single measurement mode this error should not occour but we
209 * stil account for it and return an error, since the data would be
210 * corrupted.
211 * DERR bit is self-clearing when ST2 register is read.
212 */
213 if (*stat2 & 0x04)
214 status = INV_ERROR_COMPASS_DATA_ERROR;
215 /*
216 * ST2 : overflow -
217 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
218 * This is likely to happen in presence of an external magnetic
219 * disturbance; it indicates, the sensor data is incorrect and should
220 * be ignored.
221 * An error is returned.
222 * HOFL bit clears when a new measurement starts.
223 */
224 if (*stat2 & 0x08)
225 status = INV_ERROR_COMPASS_DATA_OVERFLOW;
226 /*
227 * ST : overrun -
228 * the previous sample was not fetched and lost.
229 * Valid in continuous measurement mode only.
230 * In single measurement mode this error should not occour and we
231 * don't consider this condition an error.
232 * DOR bit is self-clearing when ST2 or any meas. data register is
233 * read.
234 */
235 if (*stat & 0x02) {
236 /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
237 status = INV_SUCCESS;
238 }
239
240 /*
241 * trigger next measurement if:
242 * - stat is non zero;
243 * - if stat is zero and stat2 is non zero.
244 * Won't trigger if data is not ready and there was no error.
245 */
246 if (*stat != 0x00 || *stat2 != 0x00) {
247 result = inv_serial_single_write(
248 mlsl_handle, pdata->address,
249 AK8975_REG_CNTL, AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
250 if (result) {
251 LOG_RESULT_LOCATION(result);
252 return result;
253 }
254 }
255
256 return status;
257}
258
259static int ak8975_config(void *mlsl_handle,
260 struct ext_slave_descr *slave,
261 struct ext_slave_platform_data *pdata,
262 struct ext_slave_config *data)
263{
264 int result;
265 if (!data->data)
266 return INV_ERROR_INVALID_PARAMETER;
267
268 switch (data->key) {
269 case MPU_SLAVE_WRITE_REGISTERS:
270 result = inv_serial_write(mlsl_handle, pdata->address,
271 data->len,
272 (unsigned char *)data->data);
273 if (result) {
274 LOG_RESULT_LOCATION(result);
275 return result;
276 }
277 break;
278 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
279 case MPU_SLAVE_CONFIG_ODR_RESUME:
280 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
281 case MPU_SLAVE_CONFIG_FSR_RESUME:
282 case MPU_SLAVE_CONFIG_MOT_THS:
283 case MPU_SLAVE_CONFIG_NMOT_THS:
284 case MPU_SLAVE_CONFIG_MOT_DUR:
285 case MPU_SLAVE_CONFIG_NMOT_DUR:
286 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
287 case MPU_SLAVE_CONFIG_IRQ_RESUME:
288 default:
289 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
290 };
291
292 return INV_SUCCESS;
293}
294
295static int ak8975_get_config(void *mlsl_handle,
296 struct ext_slave_descr *slave,
297 struct ext_slave_platform_data *pdata,
298 struct ext_slave_config *data)
299{
300 struct ak8975_private_data *private_data = pdata->private_data;
301 int result;
302 if (!data->data)
303 return INV_ERROR_INVALID_PARAMETER;
304
305 switch (data->key) {
306 case MPU_SLAVE_READ_REGISTERS:
307 {
308 unsigned char *serial_data =
309 (unsigned char *)data->data;
310 result =
311 inv_serial_read(mlsl_handle, pdata->address,
312 serial_data[0], data->len - 1,
313 &serial_data[1]);
314 if (result) {
315 LOG_RESULT_LOCATION(result);
316 return result;
317 }
318 break;
319 }
320 case MPU_SLAVE_READ_SCALE:
321 {
322 unsigned char *serial_data =
323 (unsigned char *)data->data;
324 serial_data[0] = private_data->init.asa[0];
325 serial_data[1] = private_data->init.asa[1];
326 serial_data[2] = private_data->init.asa[2];
327 result = INV_SUCCESS;
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332 break;
333 }
334 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
335 (*(unsigned long *)data->data) = 0;
336 break;
337 case MPU_SLAVE_CONFIG_ODR_RESUME:
338 (*(unsigned long *)data->data) = 8000;
339 break;
340 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
341 case MPU_SLAVE_CONFIG_FSR_RESUME:
342 case MPU_SLAVE_CONFIG_MOT_THS:
343 case MPU_SLAVE_CONFIG_NMOT_THS:
344 case MPU_SLAVE_CONFIG_MOT_DUR:
345 case MPU_SLAVE_CONFIG_NMOT_DUR:
346 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
347 case MPU_SLAVE_CONFIG_IRQ_RESUME:
348 default:
349 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
350 };
351
352 return INV_SUCCESS;
353}
354
355static struct ext_slave_read_trigger ak8975_read_trigger = {
356 /*.reg = */ 0x0A,
357 /*.value = */ 0x01
358};
359
360static struct ext_slave_descr ak8975_descr = {
361 .init = ak8975_init,
362 .exit = ak8975_exit,
363 .suspend = ak8975_suspend,
364 .resume = ak8975_resume,
365 .read = ak8975_read,
366 .config = ak8975_config,
367 .get_config = ak8975_get_config,
368 .name = "ak8975",
369 .type = EXT_SLAVE_TYPE_COMPASS,
370 .id = COMPASS_ID_AK8975,
371 .read_reg = 0x01,
372 .read_len = 10,
373 .endian = EXT_SLAVE_LITTLE_ENDIAN,
374 .range = {9830, 4000},
375 .trigger = &ak8975_read_trigger,
376};
377
378static
379struct ext_slave_descr *ak8975_get_slave_descr(void)
380{
381 return &ak8975_descr;
382}
383
384/* -------------------------------------------------------------------------- */
385struct ak8975_mod_private_data {
386 struct i2c_client *client;
387 struct ext_slave_platform_data *pdata;
388};
389
390static unsigned short normal_i2c[] = { I2C_CLIENT_END };
391
392static int ak8975_mod_probe(struct i2c_client *client,
393 const struct i2c_device_id *devid)
394{
395 struct ext_slave_platform_data *pdata;
396 struct ak8975_mod_private_data *private_data;
397 int result = 0;
398
399 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
400
401 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
402 result = -ENODEV;
403 goto out_no_free;
404 }
405
406 pdata = client->dev.platform_data;
407 if (!pdata) {
408 dev_err(&client->adapter->dev,
409 "Missing platform data for slave %s\n", devid->name);
410 result = -EFAULT;
411 goto out_no_free;
412 }
413
414 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
415 if (!private_data) {
416 result = -ENOMEM;
417 goto out_no_free;
418 }
419
420 i2c_set_clientdata(client, private_data);
421 private_data->client = client;
422 private_data->pdata = pdata;
423
424 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
425 ak8975_get_slave_descr);
426 if (result) {
427 dev_err(&client->adapter->dev,
428 "Slave registration failed: %s, %d\n",
429 devid->name, result);
430 goto out_free_memory;
431 }
432
433 return result;
434
435out_free_memory:
436 kfree(private_data);
437out_no_free:
438 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
439 return result;
440
441}
442
443static int ak8975_mod_remove(struct i2c_client *client)
444{
445 struct ak8975_mod_private_data *private_data =
446 i2c_get_clientdata(client);
447
448 dev_dbg(&client->adapter->dev, "%s\n", __func__);
449 inv_mpu_unregister_slave(client, private_data->pdata,
450 ak8975_get_slave_descr);
451
452 kfree(private_data);
453 return 0;
454}
455
456static const struct i2c_device_id ak8975_mod_id[] = {
457 { "ak8975", COMPASS_ID_AK8975 },
458 {}
459};
460
461MODULE_DEVICE_TABLE(i2c, ak8975_mod_id);
462
463static struct i2c_driver ak8975_mod_driver = {
464 .class = I2C_CLASS_HWMON,
465 .probe = ak8975_mod_probe,
466 .remove = ak8975_mod_remove,
467 .id_table = ak8975_mod_id,
468 .driver = {
469 .owner = THIS_MODULE,
470 .name = "ak8975_mod",
471 },
472 .address_list = normal_i2c,
473};
474
475static int __init ak8975_mod_init(void)
476{
477 int res = i2c_add_driver(&ak8975_mod_driver);
478 pr_info("%s: Probe name %s\n", __func__, "ak8975_mod");
479 if (res)
480 pr_err("%s failed\n", __func__);
481 return res;
482}
483
484static void __exit ak8975_mod_exit(void)
485{
486 pr_info("%s\n", __func__);
487 i2c_del_driver(&ak8975_mod_driver);
488}
489
490module_init(ak8975_mod_init);
491module_exit(ak8975_mod_exit);
492
493MODULE_AUTHOR("Invensense Corporation");
494MODULE_DESCRIPTION("Driver to integrate AK8975 sensor with the MPU");
495MODULE_LICENSE("GPL");
496MODULE_ALIAS("ak8975_mod");
497
498/**
499 * @}
500 */
diff --git a/drivers/misc/inv_mpu/compass/ak89xx.c b/drivers/misc/inv_mpu/compass/ak89xx.c
new file mode 100644
index 00000000000..d15b0b8dbe6
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ak89xx.c
@@ -0,0 +1,522 @@
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 COMPASSDL
22 *
23 * @{
24 * @file ak89xx.c
25 * @brief Magnetometer setup and handling methods for the AKM
26 * compass devices.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48#define AK89XX_REG_ST1 (0x02)
49#define AK89XX_REG_HXL (0x03)
50#define AK89XX_REG_ST2 (0x09)
51
52#define AK89XX_REG_CNTL (0x0A)
53#define AK89XX_REG_ASAX (0x10)
54#define AK89XX_REG_ASAY (0x11)
55#define AK89XX_REG_ASAZ (0x12)
56
57#define AK89XX_CNTL_MODE_POWER_DOWN (0x00)
58#define AK89XX_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
59#define AK89XX_CNTL_MODE_FUSE_ROM_ACCESS (0x0f)
60
61/* -------------------------------------------------------------------------- */
62struct ak89xx_config {
63 char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */
64};
65
66struct ak89xx_private_data {
67 struct ak89xx_config init;
68};
69
70/* -------------------------------------------------------------------------- */
71static int ak89xx_init(void *mlsl_handle,
72 struct ext_slave_descr *slave,
73 struct ext_slave_platform_data *pdata)
74{
75 int result;
76 unsigned char serial_data[COMPASS_NUM_AXES];
77
78 struct ak89xx_private_data *private_data;
79 private_data = (struct ak89xx_private_data *)
80 kzalloc(sizeof(struct ak89xx_private_data), GFP_KERNEL);
81
82 if (!private_data)
83 return INV_ERROR_MEMORY_EXAUSTED;
84
85 result = inv_serial_single_write(mlsl_handle, pdata->address,
86 AK89XX_REG_CNTL,
87 AK89XX_CNTL_MODE_POWER_DOWN);
88 if (result) {
89 LOG_RESULT_LOCATION(result);
90 return result;
91 }
92 /* Wait at least 100us */
93 udelay(100);
94
95 result = inv_serial_single_write(mlsl_handle, pdata->address,
96 AK89XX_REG_CNTL,
97 AK89XX_CNTL_MODE_FUSE_ROM_ACCESS);
98 if (result) {
99 LOG_RESULT_LOCATION(result);
100 return result;
101 }
102
103 /* Wait at least 200us */
104 udelay(200);
105
106 result = inv_serial_read(mlsl_handle, pdata->address,
107 AK89XX_REG_ASAX,
108 COMPASS_NUM_AXES, serial_data);
109 if (result) {
110 LOG_RESULT_LOCATION(result);
111 return result;
112 }
113
114 pdata->private_data = private_data;
115
116 private_data->init.asa[0] = serial_data[0];
117 private_data->init.asa[1] = serial_data[1];
118 private_data->init.asa[2] = serial_data[2];
119
120 result = inv_serial_single_write(mlsl_handle, pdata->address,
121 AK89XX_REG_CNTL,
122 AK89XX_CNTL_MODE_POWER_DOWN);
123 if (result) {
124 LOG_RESULT_LOCATION(result);
125 return result;
126 }
127
128 udelay(100);
129 return INV_SUCCESS;
130}
131
132static int ak89xx_exit(void *mlsl_handle,
133 struct ext_slave_descr *slave,
134 struct ext_slave_platform_data *pdata)
135{
136 kfree(pdata->private_data);
137 return INV_SUCCESS;
138}
139
140static int ak89xx_suspend(void *mlsl_handle,
141 struct ext_slave_descr *slave,
142 struct ext_slave_platform_data *pdata)
143{
144 int result = INV_SUCCESS;
145 result =
146 inv_serial_single_write(mlsl_handle, pdata->address,
147 AK89XX_REG_CNTL,
148 AK89XX_CNTL_MODE_POWER_DOWN);
149 msleep(1); /* wait at least 100us */
150 if (result) {
151 LOG_RESULT_LOCATION(result);
152 return result;
153 }
154 return result;
155}
156
157static int ak89xx_resume(void *mlsl_handle,
158 struct ext_slave_descr *slave,
159 struct ext_slave_platform_data *pdata)
160{
161 int result = INV_SUCCESS;
162 result =
163 inv_serial_single_write(mlsl_handle, pdata->address,
164 AK89XX_REG_CNTL,
165 AK89XX_CNTL_MODE_SINGLE_MEASUREMENT);
166 if (result) {
167 LOG_RESULT_LOCATION(result);
168 return result;
169 }
170 return result;
171}
172
173static int ak89xx_read(void *mlsl_handle,
174 struct ext_slave_descr *slave,
175 struct ext_slave_platform_data *pdata, unsigned char *data)
176{
177 unsigned char regs[8];
178 unsigned char *stat = &regs[0];
179 unsigned char *stat2 = &regs[7];
180 int result = INV_SUCCESS;
181 int status = INV_SUCCESS;
182
183 result =
184 inv_serial_read(mlsl_handle, pdata->address, AK89XX_REG_ST1,
185 8, regs);
186 if (result) {
187 LOG_RESULT_LOCATION(result);
188 return result;
189 }
190
191 /* Always return the data and the status registers */
192 memcpy(data, &regs[1], 6);
193 data[6] = regs[0];
194 data[7] = regs[7];
195
196 /*
197 * ST : data ready -
198 * Measurement has been completed and data is ready to be read.
199 */
200 if (*stat & 0x01)
201 status = INV_SUCCESS;
202
203 /*
204 * ST2 : data error -
205 * occurs when data read is started outside of a readable period;
206 * data read would not be correct.
207 * Valid in continuous measurement mode only.
208 * In single measurement mode this error should not occour but we
209 * stil account for it and return an error, since the data would be
210 * corrupted.
211 * DERR bit is self-clearing when ST2 register is read.
212 *
213 * This bit is always zero on the AK8963.
214 */
215 if (*stat2 & 0x04)
216 status = INV_ERROR_COMPASS_DATA_ERROR;
217 /*
218 * ST2 : overflow -
219 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
220 * This is likely to happen in presence of an external magnetic
221 * disturbance; it indicates, the sensor data is incorrect and should
222 * be ignored.
223 * An error is returned.
224 * HOFL bit clears when a new measurement starts.
225 */
226 if (*stat2 & 0x08)
227 status = INV_ERROR_COMPASS_DATA_OVERFLOW;
228 /*
229 * ST : overrun -
230 * the previous sample was not fetched and lost.
231 * Valid in continuous measurement mode only.
232 * In single measurement mode this error should not occour and we
233 * don't consider this condition an error.
234 * DOR bit is self-clearing when ST2 or any meas. data register is
235 * read.
236 */
237 if (*stat & 0x02) {
238 /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
239 status = INV_SUCCESS;
240 }
241
242 /*
243 * trigger next measurement if:
244 * - stat is non zero;
245 * - if stat is zero and stat2 is non zero.
246 * Won't trigger if data is not ready and there was no error.
247 */
248 if (*stat != 0x00 || *stat2 != 0x00) {
249 result = inv_serial_single_write(
250 mlsl_handle, pdata->address,
251 AK89XX_REG_CNTL, AK89XX_CNTL_MODE_SINGLE_MEASUREMENT);
252 if (result) {
253 LOG_RESULT_LOCATION(result);
254 return result;
255 }
256 }
257
258 return status;
259}
260
261static int ak89xx_config(void *mlsl_handle,
262 struct ext_slave_descr *slave,
263 struct ext_slave_platform_data *pdata,
264 struct ext_slave_config *data)
265{
266 int result;
267 if (!data->data)
268 return INV_ERROR_INVALID_PARAMETER;
269
270 switch (data->key) {
271 case MPU_SLAVE_WRITE_REGISTERS:
272 result = inv_serial_write(mlsl_handle, pdata->address,
273 data->len,
274 (unsigned char *)data->data);
275 if (result) {
276 LOG_RESULT_LOCATION(result);
277 return result;
278 }
279 break;
280 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
281 case MPU_SLAVE_CONFIG_ODR_RESUME:
282 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
283 case MPU_SLAVE_CONFIG_FSR_RESUME:
284 case MPU_SLAVE_CONFIG_MOT_THS:
285 case MPU_SLAVE_CONFIG_NMOT_THS:
286 case MPU_SLAVE_CONFIG_MOT_DUR:
287 case MPU_SLAVE_CONFIG_NMOT_DUR:
288 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
289 case MPU_SLAVE_CONFIG_IRQ_RESUME:
290 default:
291 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
292 };
293
294 return INV_SUCCESS;
295}
296
297static int ak89xx_get_config(void *mlsl_handle,
298 struct ext_slave_descr *slave,
299 struct ext_slave_platform_data *pdata,
300 struct ext_slave_config *data)
301{
302 struct ak89xx_private_data *private_data = pdata->private_data;
303 int result;
304 if (!data->data)
305 return INV_ERROR_INVALID_PARAMETER;
306
307 switch (data->key) {
308 case MPU_SLAVE_READ_REGISTERS:
309 {
310 unsigned char *serial_data =
311 (unsigned char *)data->data;
312 result =
313 inv_serial_read(mlsl_handle, pdata->address,
314 serial_data[0], data->len - 1,
315 &serial_data[1]);
316 if (result) {
317 LOG_RESULT_LOCATION(result);
318 return result;
319 }
320 break;
321 }
322 case MPU_SLAVE_READ_SCALE:
323 {
324 unsigned char *serial_data =
325 (unsigned char *)data->data;
326 serial_data[0] = private_data->init.asa[0];
327 serial_data[1] = private_data->init.asa[1];
328 serial_data[2] = private_data->init.asa[2];
329 result = INV_SUCCESS;
330 if (result) {
331 LOG_RESULT_LOCATION(result);
332 return result;
333 }
334 break;
335 }
336 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
337 (*(unsigned long *)data->data) = 0;
338 break;
339 case MPU_SLAVE_CONFIG_ODR_RESUME:
340 (*(unsigned long *)data->data) = 8000;
341 break;
342 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
343 case MPU_SLAVE_CONFIG_FSR_RESUME:
344 case MPU_SLAVE_CONFIG_MOT_THS:
345 case MPU_SLAVE_CONFIG_NMOT_THS:
346 case MPU_SLAVE_CONFIG_MOT_DUR:
347 case MPU_SLAVE_CONFIG_NMOT_DUR:
348 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
349 case MPU_SLAVE_CONFIG_IRQ_RESUME:
350 default:
351 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
352 };
353
354 return INV_SUCCESS;
355}
356
357static struct ext_slave_read_trigger ak89xx_read_trigger = {
358 /*.reg = */ 0x0A,
359 /*.value = */ 0x01
360};
361
362static struct ext_slave_descr ak89xx_descr = {
363 .init = ak89xx_init,
364 .exit = ak89xx_exit,
365 .suspend = ak89xx_suspend,
366 .resume = ak89xx_resume,
367 .read = ak89xx_read,
368 .config = ak89xx_config,
369 .get_config = ak89xx_get_config,
370 .name = "ak89xx",
371 .type = EXT_SLAVE_TYPE_COMPASS,
372 .id = COMPASS_ID_AK8963,
373 .read_reg = 0x01,
374 .read_len = 10,
375 .endian = EXT_SLAVE_LITTLE_ENDIAN,
376 .range = {9830, 4000},
377 .trigger = &ak89xx_read_trigger,
378};
379
380static
381struct ext_slave_descr *ak89xx_get_slave_descr(void)
382{
383 return &ak89xx_descr;
384}
385
386/* -------------------------------------------------------------------------- */
387struct ak89xx_mod_private_data {
388 struct i2c_client *client;
389 struct ext_slave_platform_data *pdata;
390};
391
392static unsigned short normal_i2c[] = { I2C_CLIENT_END };
393
394static int ak89xx_mod_probe(struct i2c_client *client,
395 const struct i2c_device_id *devid)
396{
397 struct ext_slave_platform_data *pdata;
398 struct ak89xx_mod_private_data *private_data;
399 int result = 0;
400
401 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
402
403 /* Override default values based on model. */
404 ak89xx_descr.id = devid->driver_data;
405 /* ak89xx_descr.name = devid->name; */
406 switch (ak89xx_descr.id) {
407 case COMPASS_ID_AK8963:
408 break;
409 case COMPASS_ID_AK8972:
410 ak89xx_descr.read_len = 9;
411 ak89xx_descr.range.mantissa = 39321;
412 ak89xx_descr.range.fraction = 6000;
413 break;
414 case COMPASS_ID_AK8975:
415 break;
416 default:
417 result = -EFAULT;
418 goto out_no_free;
419 }
420
421 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
422 result = -ENODEV;
423 goto out_no_free;
424 }
425
426 pdata = client->dev.platform_data;
427 if (!pdata) {
428 dev_err(&client->adapter->dev,
429 "Missing platform data for slave %s\n", devid->name);
430 result = -EFAULT;
431 goto out_no_free;
432 }
433
434 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
435 if (!private_data) {
436 result = -ENOMEM;
437 goto out_no_free;
438 }
439
440 i2c_set_clientdata(client, private_data);
441 private_data->client = client;
442 private_data->pdata = pdata;
443
444 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
445 ak89xx_get_slave_descr);
446 if (result) {
447 dev_err(&client->adapter->dev,
448 "Slave registration failed: %s, %d\n",
449 devid->name, result);
450 goto out_free_memory;
451 }
452
453 return result;
454
455out_free_memory:
456 kfree(private_data);
457out_no_free:
458 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
459 return result;
460
461}
462
463static int ak89xx_mod_remove(struct i2c_client *client)
464{
465 struct ak89xx_mod_private_data *private_data =
466 i2c_get_clientdata(client);
467
468 dev_dbg(&client->adapter->dev, "%s\n", __func__);
469 inv_mpu_unregister_slave(client, private_data->pdata,
470 ak89xx_get_slave_descr);
471
472 kfree(private_data);
473 return 0;
474}
475
476static const struct i2c_device_id ak89xx_mod_id[] = {
477 { "ak8963", COMPASS_ID_AK8963 },
478 { "ak8972", COMPASS_ID_AK8972 },
479 { "ak8975", COMPASS_ID_AK8975 },
480 {}
481};
482
483MODULE_DEVICE_TABLE(i2c, ak89xx_mod_id);
484
485static struct i2c_driver ak89xx_mod_driver = {
486 .class = I2C_CLASS_HWMON,
487 .probe = ak89xx_mod_probe,
488 .remove = ak89xx_mod_remove,
489 .id_table = ak89xx_mod_id,
490 .driver = {
491 .owner = THIS_MODULE,
492 .name = "ak89xx_mod",
493 },
494 .address_list = normal_i2c,
495};
496
497static int __init ak89xx_mod_init(void)
498{
499 int res = i2c_add_driver(&ak89xx_mod_driver);
500 pr_info("%s: Probe name %s\n", __func__, "ak89xx_mod");
501 if (res)
502 pr_err("%s failed\n", __func__);
503 return res;
504}
505
506static void __exit ak89xx_mod_exit(void)
507{
508 pr_info("%s\n", __func__);
509 i2c_del_driver(&ak89xx_mod_driver);
510}
511
512module_init(ak89xx_mod_init);
513module_exit(ak89xx_mod_exit);
514
515MODULE_AUTHOR("Invensense Corporation");
516MODULE_DESCRIPTION("Driver to integrate AKM AK89XX sensor with the MPU");
517MODULE_LICENSE("GPL");
518MODULE_ALIAS("ak89xx_mod");
519
520/**
521 * @}
522 */
diff --git a/drivers/misc/inv_mpu/compass/ami306.c b/drivers/misc/inv_mpu/compass/ami306.c
new file mode 100644
index 00000000000..f645457d161
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami306.c
@@ -0,0 +1,1020 @@
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 COMPASSDL
22 *
23 * @{
24 * @file ami306.c
25 * @brief Magnetometer setup and handling methods for Aichi AMI306
26 * compass.
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 "ami_hw.h"
41#include "ami_sensor_def.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-compass"
49
50/* -------------------------------------------------------------------------- */
51#define AMI306_REG_DATAX (0x10)
52#define AMI306_REG_STAT1 (0x18)
53#define AMI306_REG_CNTL1 (0x1B)
54#define AMI306_REG_CNTL2 (0x1C)
55#define AMI306_REG_CNTL3 (0x1D)
56#define AMI306_REG_CNTL4_1 (0x5C)
57#define AMI306_REG_CNTL4_2 (0x5D)
58
59#define AMI306_BIT_CNTL1_PC1 (0x80)
60#define AMI306_BIT_CNTL1_ODR1 (0x10)
61#define AMI306_BIT_CNTL1_FS1 (0x02)
62
63#define AMI306_BIT_CNTL2_IEN (0x10)
64#define AMI306_BIT_CNTL2_DREN (0x08)
65#define AMI306_BIT_CNTL2_DRP (0x04)
66#define AMI306_BIT_CNTL3_F0RCE (0x40)
67
68#define AMI_FINE_MAX (96)
69#define AMI_STANDARD_OFFSET (0x800)
70#define AMI_GAIN_COR_DEFAULT (1000)
71
72/* -------------------------------------------------------------------------- */
73struct ami306_private_data {
74 int isstandby;
75 unsigned char fine[3];
76 struct ami_sensor_parametor param;
77 struct ami_win_parameter win;
78};
79
80/* -------------------------------------------------------------------------- */
81static inline unsigned short little_u8_to_u16(unsigned char *p_u8)
82{
83 return p_u8[0] | (p_u8[1] << 8);
84}
85
86static int ami306_set_bits8(void *mlsl_handle,
87 struct ext_slave_platform_data *pdata,
88 unsigned char reg, unsigned char bits)
89{
90 int result;
91 unsigned char buf;
92
93 result = inv_serial_read(mlsl_handle, pdata->address, reg, 1, &buf);
94 if (result) {
95 LOG_RESULT_LOCATION(result);
96 return result;
97 }
98
99 buf |= bits;
100 result = inv_serial_single_write(mlsl_handle, pdata->address, reg, buf);
101 if (result) {
102 LOG_RESULT_LOCATION(result);
103 return result;
104 }
105 return result;
106}
107
108static int ami306_wait_data_ready(void *mlsl_handle,
109 struct ext_slave_platform_data *pdata,
110 unsigned long usecs, unsigned long times)
111{
112 int result = 0;
113 unsigned char buf;
114
115 for (; 0 < times; --times) {
116 udelay(usecs);
117 result = inv_serial_read(mlsl_handle, pdata->address,
118 AMI_REG_STA1, 1, &buf);
119 if (buf & AMI_STA1_DRDY_BIT)
120 return 0;
121 else if (buf & AMI_STA1_DOR_BIT)
122 return INV_ERROR_COMPASS_DATA_OVERFLOW;
123 }
124 return INV_ERROR_COMPASS_DATA_NOT_READY;
125}
126
127static int ami306_read_raw_data(void *mlsl_handle,
128 struct ext_slave_platform_data *pdata,
129 short dat[3])
130{
131 int result;
132 unsigned char buf[6];
133 result = inv_serial_read(mlsl_handle, pdata->address,
134 AMI_REG_DATAX, sizeof(buf), buf);
135 if (result) {
136 LOG_RESULT_LOCATION(result);
137 return result;
138 }
139 dat[0] = little_u8_to_u16(&buf[0]);
140 dat[1] = little_u8_to_u16(&buf[2]);
141 dat[2] = little_u8_to_u16(&buf[4]);
142 return result;
143}
144
145#define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */
146#define AMI_DRDYWAIT 800 /* u(micro) sec */
147static int ami306_force_mesurement(void *mlsl_handle,
148 struct ext_slave_platform_data *pdata,
149 short ver[3])
150{
151 int result;
152 int status;
153 result = ami306_set_bits8(mlsl_handle, pdata,
154 AMI_REG_CTRL3, AMI_CTRL3_FORCE_BIT);
155 if (result) {
156 LOG_RESULT_LOCATION(result);
157 return result;
158 }
159
160 result = ami306_wait_data_ready(mlsl_handle, pdata,
161 AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY);
162 if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) {
163 LOG_RESULT_LOCATION(result);
164 return result;
165 }
166 /* READ DATA X,Y,Z */
167 status = ami306_read_raw_data(mlsl_handle, pdata, ver);
168 if (status) {
169 LOG_RESULT_LOCATION(status);
170 return status;
171 }
172
173 return result;
174}
175
176static int ami306_mea(void *mlsl_handle,
177 struct ext_slave_platform_data *pdata, short val[3])
178{
179 int result = ami306_force_mesurement(mlsl_handle, pdata, val);
180 if (result) {
181 LOG_RESULT_LOCATION(result);
182 return result;
183 }
184 val[0] += AMI_STANDARD_OFFSET;
185 val[1] += AMI_STANDARD_OFFSET;
186 val[2] += AMI_STANDARD_OFFSET;
187 return result;
188}
189
190static int ami306_write_offset(void *mlsl_handle,
191 struct ext_slave_platform_data *pdata,
192 unsigned char *fine)
193{
194 int result = 0;
195 unsigned char dat[3];
196 dat[0] = AMI_REG_OFFX;
197 dat[1] = 0x7f & fine[0];
198 dat[2] = 0;
199 result = inv_serial_write(mlsl_handle, pdata->address,
200 sizeof(dat), dat);
201 dat[0] = AMI_REG_OFFY;
202 dat[1] = 0x7f & fine[1];
203 dat[2] = 0;
204 result = inv_serial_write(mlsl_handle, pdata->address,
205 sizeof(dat), dat);
206 dat[0] = AMI_REG_OFFZ;
207 dat[1] = 0x7f & fine[2];
208 dat[2] = 0;
209 result = inv_serial_write(mlsl_handle, pdata->address,
210 sizeof(dat), dat);
211 return result;
212}
213
214static int ami306_start_sensor(void *mlsl_handle,
215 struct ext_slave_platform_data *pdata)
216{
217 int result = 0;
218 unsigned char buf[3];
219 struct ami306_private_data *private_data = pdata->private_data;
220
221 /* Step 1 */
222 result = ami306_set_bits8(mlsl_handle, pdata,
223 AMI_REG_CTRL1,
224 AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
225 if (result) {
226 LOG_RESULT_LOCATION(result);
227 return result;
228 }
229 /* Step 2 */
230 result = ami306_set_bits8(mlsl_handle, pdata,
231 AMI_REG_CTRL2, AMI_CTRL2_DREN);
232 if (result) {
233 LOG_RESULT_LOCATION(result);
234 return result;
235 }
236 /* Step 3 */
237 buf[0] = AMI_REG_CTRL4;
238 buf[1] = AMI_CTRL4_HS & 0xFF;
239 buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
240 result = inv_serial_write(mlsl_handle, pdata->address,
241 sizeof(buf), buf);
242 if (result) {
243 LOG_RESULT_LOCATION(result);
244 return result;
245 }
246 /* Step 4 */
247 result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
248 if (result) {
249 LOG_RESULT_LOCATION(result);
250 return result;
251 }
252 return result;
253}
254
255/**
256 * This function does this.
257 *
258 * @param mlsl_handle this param is this.
259 * @param slave
260 * @param pdata
261 *
262 * @return INV_SUCCESS or non-zero error code.
263 */
264static int ami306_read_param(void *mlsl_handle,
265 struct ext_slave_descr *slave,
266 struct ext_slave_platform_data *pdata)
267{
268 int result = 0;
269 unsigned char regs[12];
270 struct ami306_private_data *private_data = pdata->private_data;
271 struct ami_sensor_parametor *param = &private_data->param;
272
273 result = inv_serial_read(mlsl_handle, pdata->address,
274 AMI_REG_SENX, sizeof(regs), regs);
275 if (result) {
276 LOG_RESULT_LOCATION(result);
277 return result;
278 }
279
280 /* Little endian 16 bit registers */
281 param->m_gain.x = little_u8_to_u16(&regs[0]);
282 param->m_gain.y = little_u8_to_u16(&regs[2]);
283 param->m_gain.z = little_u8_to_u16(&regs[4]);
284
285 param->m_interference.xy = regs[7];
286 param->m_interference.xz = regs[6];
287 param->m_interference.yx = regs[9];
288 param->m_interference.yz = regs[8];
289 param->m_interference.zx = regs[11];
290 param->m_interference.zy = regs[10];
291
292 param->m_offset.x = AMI_STANDARD_OFFSET;
293 param->m_offset.y = AMI_STANDARD_OFFSET;
294 param->m_offset.z = AMI_STANDARD_OFFSET;
295
296 param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT;
297 param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT;
298 param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT;
299
300 return result;
301}
302
303static int ami306_initial_b0_adjust(void *mlsl_handle,
304 struct ext_slave_descr *slave,
305 struct ext_slave_platform_data *pdata)
306{
307 int result;
308 unsigned char fine[3] = { 0 };
309 short data[3];
310 int diff[3] = { 0x7fff, 0x7fff, 0x7fff };
311 int fn = 0;
312 int ax = 0;
313 unsigned char buf[3];
314 struct ami306_private_data *private_data = pdata->private_data;
315
316 result = ami306_set_bits8(mlsl_handle, pdata,
317 AMI_REG_CTRL2, AMI_CTRL2_DREN);
318 if (result) {
319 LOG_RESULT_LOCATION(result);
320 return result;
321 }
322
323 buf[0] = AMI_REG_CTRL4;
324 buf[1] = AMI_CTRL4_HS & 0xFF;
325 buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
326 result = inv_serial_write(mlsl_handle, pdata->address,
327 sizeof(buf), buf);
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332
333 for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */
334 fine[0] = fine[1] = fine[2] = fn;
335 result = ami306_write_offset(mlsl_handle, pdata, fine);
336 if (result) {
337 LOG_RESULT_LOCATION(result);
338 return result;
339 }
340
341 result = ami306_force_mesurement(mlsl_handle, pdata, data);
342 if (result) {
343 LOG_RESULT_LOCATION(result);
344 return result;
345 }
346 MPL_LOGV("[%d] x:%-5d y:%-5d z:%-5d\n",
347 fn, data[0], data[1], data[2]);
348
349 for (ax = 0; ax < 3; ax++) {
350 /* search point most close to zero. */
351 if (diff[ax] > abs(data[ax])) {
352 private_data->fine[ax] = fn;
353 diff[ax] = abs(data[ax]);
354 }
355 }
356 }
357 MPL_LOGV("fine x:%-5d y:%-5d z:%-5d\n",
358 private_data->fine[0], private_data->fine[1],
359 private_data->fine[2]);
360
361 result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
362 if (result) {
363 LOG_RESULT_LOCATION(result);
364 return result;
365 }
366
367 /* Software Reset */
368 result = ami306_set_bits8(mlsl_handle, pdata,
369 AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
370 if (result) {
371 LOG_RESULT_LOCATION(result);
372 return result;
373 }
374 return result;
375}
376
377#define SEH_RANGE_MIN 100
378#define SEH_RANGE_MAX 3950
379static int ami306_search_offset(void *mlsl_handle,
380 struct ext_slave_descr *slave,
381 struct ext_slave_platform_data *pdata)
382{
383 int result;
384 int axis;
385 unsigned char regs[6];
386 unsigned char run_flg[3] = { 1, 1, 1 };
387 unsigned char fine[3];
388 unsigned char win_range_fine[3];
389 unsigned short fine_output[3];
390 short val[3];
391 unsigned short cnt[3] = { 0 };
392 struct ami306_private_data *private_data = pdata->private_data;
393
394 result = inv_serial_read(mlsl_handle, pdata->address,
395 AMI_FINEOUTPUT_X, sizeof(regs), regs);
396 if (result) {
397 LOG_RESULT_LOCATION(result);
398 return result;
399 }
400 fine_output[0] = little_u8_to_u16(&regs[0]);
401 fine_output[1] = little_u8_to_u16(&regs[2]);
402 fine_output[2] = little_u8_to_u16(&regs[4]);
403
404 for (axis = 0; axis < 3; ++axis) {
405 if (fine_output[axis] == 0) {
406 MPL_LOGV("error fine_output %d axis:%d\n",
407 __LINE__, axis);
408 return -1;
409 }
410 /* fines per a window */
411 win_range_fine[axis] = (SEH_RANGE_MAX - SEH_RANGE_MIN)
412 / fine_output[axis];
413 }
414
415 /* get current fine */
416 result = inv_serial_read(mlsl_handle, pdata->address,
417 AMI_REG_OFFX, 2, &regs[0]);
418 if (result) {
419 LOG_RESULT_LOCATION(result);
420 return result;
421 }
422 result = inv_serial_read(mlsl_handle, pdata->address,
423 AMI_REG_OFFY, 2, &regs[2]);
424 if (result) {
425 LOG_RESULT_LOCATION(result);
426 return result;
427 }
428 result = inv_serial_read(mlsl_handle, pdata->address,
429 AMI_REG_OFFZ, 2, &regs[4]);
430 if (result) {
431 LOG_RESULT_LOCATION(result);
432 return result;
433 }
434
435 fine[0] = (unsigned char)(regs[0] & 0x7f);
436 fine[1] = (unsigned char)(regs[2] & 0x7f);
437 fine[2] = (unsigned char)(regs[4] & 0x7f);
438
439 while (run_flg[0] == 1 || run_flg[1] == 1 || run_flg[2] == 1) {
440
441 result = ami306_mea(mlsl_handle, pdata, val);
442 if (result) {
443 LOG_RESULT_LOCATION(result);
444 return result;
445 }
446 MPL_LOGV("val x:%-5d y:%-5d z:%-5d\n", val[0], val[1], val[2]);
447 MPL_LOGV("now fine x:%-5d y:%-5d z:%-5d\n",
448 fine[0], fine[1], fine[2]);
449
450 for (axis = 0; axis < 3; ++axis) {
451 if (axis == 0) { /* X-axis is reversed */
452 val[axis] = 0x0FFF & ~val[axis];
453 }
454 if (val[axis] < SEH_RANGE_MIN) {
455 /* At the case of less low limmit. */
456 fine[axis] -= win_range_fine[axis];
457 MPL_LOGV("min : fine=%d diff=%d\n",
458 fine[axis], win_range_fine[axis]);
459 }
460 if (val[axis] > SEH_RANGE_MAX) {
461 /* At the case of over high limmit. */
462 fine[axis] += win_range_fine[axis];
463 MPL_LOGV("max : fine=%d diff=%d\n",
464 fine[axis], win_range_fine[axis]);
465 }
466 if (SEH_RANGE_MIN <= val[axis] &&
467 val[axis] <= SEH_RANGE_MAX) {
468 /* In the current window. */
469 int diff_fine =
470 (val[axis] - AMI_STANDARD_OFFSET) /
471 fine_output[axis];
472 fine[axis] += diff_fine;
473 run_flg[axis] = 0;
474 MPL_LOGV("mid : fine=%d diff=%d\n",
475 fine[axis], diff_fine);
476 }
477
478 if (!(0 <= fine[axis] && fine[axis] < AMI_FINE_MAX)) {
479 MPL_LOGE("fine err :%d\n", cnt[axis]);
480 goto out;
481 }
482 if (cnt[axis] > 3) {
483 MPL_LOGE("cnt err :%d\n", cnt[axis]);
484 goto out;
485 }
486 cnt[axis]++;
487 }
488 MPL_LOGV("new fine x:%-5d y:%-5d z:%-5d\n",
489 fine[0], fine[1], fine[2]);
490
491 /* set current fine */
492 result = ami306_write_offset(mlsl_handle, pdata, fine);
493 if (result) {
494 LOG_RESULT_LOCATION(result);
495 return result;
496 }
497 }
498 memcpy(private_data->fine, fine, sizeof(fine));
499out:
500 result = ami306_set_bits8(mlsl_handle, pdata,
501 AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
502 if (result) {
503 LOG_RESULT_LOCATION(result);
504 return result;
505 }
506 udelay(250 + 50);
507 return 0;
508}
509
510static int ami306_read_win(void *mlsl_handle,
511 struct ext_slave_descr *slave,
512 struct ext_slave_platform_data *pdata)
513{
514 int result = 0;
515 unsigned char regs[6];
516 struct ami306_private_data *private_data = pdata->private_data;
517 struct ami_win_parameter *win = &private_data->win;
518
519 result = inv_serial_read(mlsl_handle, pdata->address,
520 AMI_REG_OFFOTPX, sizeof(regs), regs);
521 if (result) {
522 LOG_RESULT_LOCATION(result);
523 return result;
524 }
525
526 win->m_0Gauss_fine.x = (unsigned char)(regs[0] & 0x7f);
527 win->m_0Gauss_fine.y = (unsigned char)(regs[2] & 0x7f);
528 win->m_0Gauss_fine.z = (unsigned char)(regs[4] & 0x7f);
529
530 result = inv_serial_read(mlsl_handle, pdata->address,
531 AMI_REG_OFFX, 2, &regs[0]);
532 if (result) {
533 LOG_RESULT_LOCATION(result);
534 return result;
535 }
536 result = inv_serial_read(mlsl_handle, pdata->address,
537 AMI_REG_OFFY, 2, &regs[2]);
538 if (result) {
539 LOG_RESULT_LOCATION(result);
540 return result;
541 }
542 result = inv_serial_read(mlsl_handle, pdata->address,
543 AMI_REG_OFFZ, 2, &regs[4]);
544 if (result) {
545 LOG_RESULT_LOCATION(result);
546 return result;
547 }
548
549 win->m_fine.x = (unsigned char)(regs[0] & 0x7f);
550 win->m_fine.y = (unsigned char)(regs[2] & 0x7f);
551 win->m_fine.z = (unsigned char)(regs[4] & 0x7f);
552
553 result = inv_serial_read(mlsl_handle, pdata->address,
554 AMI_FINEOUTPUT_X, sizeof(regs), regs);
555 if (result) {
556 LOG_RESULT_LOCATION(result);
557 return result;
558 }
559 win->m_fine_output.x = little_u8_to_u16(&regs[0]);
560 win->m_fine_output.y = little_u8_to_u16(&regs[2]);
561 win->m_fine_output.z = little_u8_to_u16(&regs[4]);
562
563 return result;
564}
565
566static int ami306_suspend(void *mlsl_handle,
567 struct ext_slave_descr *slave,
568 struct ext_slave_platform_data *pdata)
569{
570 int result;
571 unsigned char reg;
572 result = inv_serial_read(mlsl_handle, pdata->address,
573 AMI306_REG_CNTL1, 1, &reg);
574 if (result) {
575 LOG_RESULT_LOCATION(result);
576 return result;
577 }
578
579 reg &= ~(AMI306_BIT_CNTL1_PC1 | AMI306_BIT_CNTL1_FS1);
580 result = inv_serial_single_write(mlsl_handle, pdata->address,
581 AMI306_REG_CNTL1, reg);
582 if (result) {
583 LOG_RESULT_LOCATION(result);
584 return result;
585 }
586
587 return result;
588}
589
590static int ami306_resume(void *mlsl_handle,
591 struct ext_slave_descr *slave,
592 struct ext_slave_platform_data *pdata)
593{
594 int result = INV_SUCCESS;
595 unsigned char regs[] = {
596 AMI306_REG_CNTL4_1,
597 0x7E,
598 0xA0
599 };
600 /* Step1. Set CNTL1 reg to power model active (Write CNTL1:PC1=1) */
601 result = inv_serial_single_write(mlsl_handle, pdata->address,
602 AMI306_REG_CNTL1,
603 AMI306_BIT_CNTL1_PC1 |
604 AMI306_BIT_CNTL1_FS1);
605 if (result) {
606 LOG_RESULT_LOCATION(result);
607 return result;
608 }
609
610 /* Step2. Set CNTL2 reg to DRDY active high and enabled
611 (Write CNTL2:DREN=1) */
612 result = inv_serial_single_write(mlsl_handle, pdata->address,
613 AMI306_REG_CNTL2,
614 AMI306_BIT_CNTL2_DREN |
615 AMI306_BIT_CNTL2_DRP);
616 if (result) {
617 LOG_RESULT_LOCATION(result);
618 return result;
619 }
620
621 /* Step3. Set CNTL4 reg to for measurement speed: Write CNTL4, 0xA07E */
622 result = inv_serial_write(mlsl_handle, pdata->address,
623 ARRAY_SIZE(regs), regs);
624 if (result) {
625 LOG_RESULT_LOCATION(result);
626 return result;
627 }
628
629 /* Step4. skipped */
630
631 /* Step5. Set CNTL3 reg to forced measurement period
632 (Write CNTL3:FORCE=1) */
633 result = inv_serial_single_write(mlsl_handle, pdata->address,
634 AMI306_REG_CNTL3,
635 AMI306_BIT_CNTL3_F0RCE);
636
637 return result;
638}
639
640static int ami306_read(void *mlsl_handle,
641 struct ext_slave_descr *slave,
642 struct ext_slave_platform_data *pdata,
643 unsigned char *data)
644{
645 int result = INV_SUCCESS;
646 int ii;
647 short val[COMPASS_NUM_AXES];
648
649 result = ami306_mea(mlsl_handle, pdata, val);
650 if (result) {
651 LOG_RESULT_LOCATION(result);
652 return result;
653 }
654 for (ii = 0; ii < COMPASS_NUM_AXES; ii++) {
655 val[ii] -= AMI_STANDARD_OFFSET;
656 data[2 * ii] = val[ii] & 0xFF;
657 data[(2 * ii) + 1] = (val[ii] >> 8) & 0xFF;
658 }
659 return result;
660}
661
662static int ami306_init(void *mlsl_handle,
663 struct ext_slave_descr *slave,
664 struct ext_slave_platform_data *pdata)
665{
666 int result;
667 struct ami306_private_data *private_data;
668 private_data = (struct ami306_private_data *)
669 kzalloc(sizeof(struct ami306_private_data), GFP_KERNEL);
670
671 if (!private_data)
672 return INV_ERROR_MEMORY_EXAUSTED;
673
674 pdata->private_data = private_data;
675 result = ami306_set_bits8(mlsl_handle, pdata,
676 AMI_REG_CTRL1,
677 AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
678 if (result) {
679 LOG_RESULT_LOCATION(result);
680 return result;
681 }
682 /* Read Parameters */
683 result = ami306_read_param(mlsl_handle, slave, pdata);
684 if (result) {
685 LOG_RESULT_LOCATION(result);
686 return result;
687 }
688 /* Read Window */
689 result = ami306_initial_b0_adjust(mlsl_handle, slave, pdata);
690 if (result) {
691 LOG_RESULT_LOCATION(result);
692 return result;
693 }
694 result = ami306_start_sensor(mlsl_handle, pdata);
695 if (result) {
696 LOG_RESULT_LOCATION(result);
697 return result;
698 }
699 result = ami306_read_win(mlsl_handle, slave, pdata);
700 if (result) {
701 LOG_RESULT_LOCATION(result);
702 return result;
703 }
704
705 result = inv_serial_single_write(mlsl_handle, pdata->address,
706 AMI306_REG_CNTL1, 0);
707 if (result) {
708 LOG_RESULT_LOCATION(result);
709 return result;
710 }
711
712 return INV_SUCCESS;
713}
714
715static int ami306_exit(void *mlsl_handle,
716 struct ext_slave_descr *slave,
717 struct ext_slave_platform_data *pdata)
718{
719 kfree(pdata->private_data);
720 return INV_SUCCESS;
721}
722
723static int ami306_config(void *mlsl_handle,
724 struct ext_slave_descr *slave,
725 struct ext_slave_platform_data *pdata,
726 struct ext_slave_config *data)
727{
728 if (!data->data) {
729 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
730 return INV_ERROR_INVALID_PARAMETER;
731 }
732
733 switch (data->key) {
734 case MPU_SLAVE_PARAM:
735 case MPU_SLAVE_WINDOW:
736 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
737 case MPU_SLAVE_CONFIG_ODR_RESUME:
738 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
739 case MPU_SLAVE_CONFIG_FSR_RESUME:
740 case MPU_SLAVE_CONFIG_MOT_THS:
741 case MPU_SLAVE_CONFIG_NMOT_THS:
742 case MPU_SLAVE_CONFIG_MOT_DUR:
743 case MPU_SLAVE_CONFIG_NMOT_DUR:
744 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
745 case MPU_SLAVE_CONFIG_IRQ_RESUME:
746 default:
747 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
748 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
749 };
750
751 return INV_SUCCESS;
752}
753
754static int ami306_get_config(void *mlsl_handle,
755 struct ext_slave_descr *slave,
756 struct ext_slave_platform_data *pdata,
757 struct ext_slave_config *data)
758{
759 int result;
760 struct ami306_private_data *private_data = pdata->private_data;
761 if (!data->data) {
762 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
763 return INV_ERROR_INVALID_PARAMETER;
764 }
765
766 switch (data->key) {
767 case MPU_SLAVE_PARAM:
768 if (sizeof(struct ami_sensor_parametor) > data->len) {
769 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
770 return INV_ERROR_INVALID_PARAMETER;
771 }
772 if (data->apply) {
773 result = ami306_read_param(mlsl_handle, slave, pdata);
774 if (result) {
775 LOG_RESULT_LOCATION(result);
776 return result;
777 }
778 }
779 memcpy(data->data, &private_data->param,
780 sizeof(struct ami_sensor_parametor));
781 break;
782 case MPU_SLAVE_WINDOW:
783 if (sizeof(struct ami_win_parameter) > data->len) {
784 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
785 return INV_ERROR_INVALID_PARAMETER;
786 }
787 if (data->apply) {
788 result = ami306_read_win(mlsl_handle, slave, pdata);
789 if (result) {
790 LOG_RESULT_LOCATION(result);
791 return result;
792 }
793 }
794 memcpy(data->data, &private_data->win,
795 sizeof(struct ami_win_parameter));
796 break;
797 case MPU_SLAVE_SEARCHOFFSET:
798 if (sizeof(struct ami_win_parameter) > data->len) {
799 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
800 return INV_ERROR_INVALID_PARAMETER;
801 }
802 if (data->apply) {
803 result = ami306_search_offset(mlsl_handle,
804 slave, pdata);
805 if (result) {
806 LOG_RESULT_LOCATION(result);
807 return result;
808 }
809 /* Start sensor */
810 result = ami306_start_sensor(mlsl_handle, pdata);
811 if (result) {
812 LOG_RESULT_LOCATION(result);
813 return result;
814 }
815 result = ami306_read_win(mlsl_handle, slave, pdata);
816 if (result) {
817 LOG_RESULT_LOCATION(result);
818 return result;
819 }
820 }
821 memcpy(data->data, &private_data->win,
822 sizeof(struct ami_win_parameter));
823 break;
824 case MPU_SLAVE_READWINPARAMS:
825 if (sizeof(struct ami_win_parameter) > data->len) {
826 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
827 return INV_ERROR_INVALID_PARAMETER;
828 }
829 if (data->apply) {
830 result = ami306_initial_b0_adjust(mlsl_handle,
831 slave, pdata);
832 if (result) {
833 LOG_RESULT_LOCATION(result);
834 return result;
835 }
836 /* Start sensor */
837 result = ami306_start_sensor(mlsl_handle, pdata);
838 if (result) {
839 LOG_RESULT_LOCATION(result);
840 return result;
841 }
842 result = ami306_read_win(mlsl_handle, slave, pdata);
843 if (result) {
844 LOG_RESULT_LOCATION(result);
845 return result;
846 }
847 }
848 memcpy(data->data, &private_data->win,
849 sizeof(struct ami_win_parameter));
850 break;
851 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
852 (*(unsigned long *)data->data) = 0;
853 break;
854 case MPU_SLAVE_CONFIG_ODR_RESUME:
855 (*(unsigned long *)data->data) = 50000;
856 break;
857 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
858 case MPU_SLAVE_CONFIG_FSR_RESUME:
859 case MPU_SLAVE_CONFIG_MOT_THS:
860 case MPU_SLAVE_CONFIG_NMOT_THS:
861 case MPU_SLAVE_CONFIG_MOT_DUR:
862 case MPU_SLAVE_CONFIG_NMOT_DUR:
863 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
864 case MPU_SLAVE_CONFIG_IRQ_RESUME:
865 case MPU_SLAVE_READ_SCALE:
866 default:
867 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
868 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
869 };
870
871 return INV_SUCCESS;
872}
873
874static struct ext_slave_read_trigger ami306_read_trigger = {
875 /*.reg = */ AMI_REG_CTRL3,
876 /*.value = */ AMI_CTRL3_FORCE_BIT
877};
878
879static struct ext_slave_descr ami306_descr = {
880 .init = ami306_init,
881 .exit = ami306_exit,
882 .suspend = ami306_suspend,
883 .resume = ami306_resume,
884 .read = ami306_read,
885 .config = ami306_config,
886 .get_config = ami306_get_config,
887 .name = "ami306",
888 .type = EXT_SLAVE_TYPE_COMPASS,
889 .id = COMPASS_ID_AMI306,
890 .read_reg = 0x0E,
891 .read_len = 13,
892 .endian = EXT_SLAVE_LITTLE_ENDIAN,
893 .range = {5461, 3333},
894 .trigger = &ami306_read_trigger,
895};
896
897static
898struct ext_slave_descr *ami306_get_slave_descr(void)
899{
900 return &ami306_descr;
901}
902
903/* -------------------------------------------------------------------------- */
904struct ami306_mod_private_data {
905 struct i2c_client *client;
906 struct ext_slave_platform_data *pdata;
907};
908
909static unsigned short normal_i2c[] = { I2C_CLIENT_END };
910
911static int ami306_mod_probe(struct i2c_client *client,
912 const struct i2c_device_id *devid)
913{
914 struct ext_slave_platform_data *pdata;
915 struct ami306_mod_private_data *private_data;
916 int result = 0;
917
918 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
919
920 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
921 result = -ENODEV;
922 goto out_no_free;
923 }
924
925 pdata = client->dev.platform_data;
926 if (!pdata) {
927 dev_err(&client->adapter->dev,
928 "Missing platform data for slave %s\n", devid->name);
929 result = -EFAULT;
930 goto out_no_free;
931 }
932
933 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
934 if (!private_data) {
935 result = -ENOMEM;
936 goto out_no_free;
937 }
938
939 i2c_set_clientdata(client, private_data);
940 private_data->client = client;
941 private_data->pdata = pdata;
942
943 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
944 ami306_get_slave_descr);
945 if (result) {
946 dev_err(&client->adapter->dev,
947 "Slave registration failed: %s, %d\n",
948 devid->name, result);
949 goto out_free_memory;
950 }
951
952 return result;
953
954out_free_memory:
955 kfree(private_data);
956out_no_free:
957 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
958 return result;
959
960}
961
962static int ami306_mod_remove(struct i2c_client *client)
963{
964 struct ami306_mod_private_data *private_data =
965 i2c_get_clientdata(client);
966
967 dev_dbg(&client->adapter->dev, "%s\n", __func__);
968
969 inv_mpu_unregister_slave(client, private_data->pdata,
970 ami306_get_slave_descr);
971
972 kfree(private_data);
973 return 0;
974}
975
976static const struct i2c_device_id ami306_mod_id[] = {
977 { "ami306", COMPASS_ID_AMI306 },
978 {}
979};
980
981MODULE_DEVICE_TABLE(i2c, ami306_mod_id);
982
983static struct i2c_driver ami306_mod_driver = {
984 .class = I2C_CLASS_HWMON,
985 .probe = ami306_mod_probe,
986 .remove = ami306_mod_remove,
987 .id_table = ami306_mod_id,
988 .driver = {
989 .owner = THIS_MODULE,
990 .name = "ami306_mod",
991 },
992 .address_list = normal_i2c,
993};
994
995static int __init ami306_mod_init(void)
996{
997 int res = i2c_add_driver(&ami306_mod_driver);
998 pr_info("%s: Probe name %s\n", __func__, "ami306_mod");
999 if (res)
1000 pr_err("%s failed\n", __func__);
1001 return res;
1002}
1003
1004static void __exit ami306_mod_exit(void)
1005{
1006 pr_info("%s\n", __func__);
1007 i2c_del_driver(&ami306_mod_driver);
1008}
1009
1010module_init(ami306_mod_init);
1011module_exit(ami306_mod_exit);
1012
1013MODULE_AUTHOR("Invensense Corporation");
1014MODULE_DESCRIPTION("Driver to integrate AMI306 sensor with the MPU");
1015MODULE_LICENSE("GPL");
1016MODULE_ALIAS("ami306_mod");
1017
1018/**
1019 * @}
1020 */
diff --git a/drivers/misc/inv_mpu/compass/ami30x.c b/drivers/misc/inv_mpu/compass/ami30x.c
new file mode 100644
index 00000000000..0c4937c4426
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami30x.c
@@ -0,0 +1,308 @@
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 COMPASSDL
22 *
23 * @{
24 * @file ami30x.c
25 * @brief Magnetometer setup and handling methods for Aichi AMI304
26 * and AMI305 compass devices.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48#define AMI30X_REG_DATAX (0x10)
49#define AMI30X_REG_STAT1 (0x18)
50#define AMI30X_REG_CNTL1 (0x1B)
51#define AMI30X_REG_CNTL2 (0x1C)
52#define AMI30X_REG_CNTL3 (0x1D)
53
54#define AMI30X_BIT_CNTL1_PC1 (0x80)
55#define AMI30X_BIT_CNTL1_ODR1 (0x10)
56#define AMI30X_BIT_CNTL1_FS1 (0x02)
57
58#define AMI30X_BIT_CNTL2_IEN (0x10)
59#define AMI30X_BIT_CNTL2_DREN (0x08)
60#define AMI30X_BIT_CNTL2_DRP (0x04)
61#define AMI30X_BIT_CNTL3_F0RCE (0x40)
62
63/* -------------------------------------------------------------------------- */
64static int ami30x_suspend(void *mlsl_handle,
65 struct ext_slave_descr *slave,
66 struct ext_slave_platform_data *pdata)
67{
68 int result;
69 unsigned char reg;
70 result =
71 inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_CNTL1,
72 1, &reg);
73 if (result) {
74 LOG_RESULT_LOCATION(result);
75 return result;
76 }
77
78 reg &= ~(AMI30X_BIT_CNTL1_PC1 | AMI30X_BIT_CNTL1_FS1);
79 result =
80 inv_serial_single_write(mlsl_handle, pdata->address,
81 AMI30X_REG_CNTL1, reg);
82 if (result) {
83 LOG_RESULT_LOCATION(result);
84 return result;
85 }
86
87 return result;
88}
89
90static int ami30x_resume(void *mlsl_handle,
91 struct ext_slave_descr *slave,
92 struct ext_slave_platform_data *pdata)
93{
94 int result = INV_SUCCESS;
95
96 /* Set CNTL1 reg to power model active */
97 result =
98 inv_serial_single_write(mlsl_handle, pdata->address,
99 AMI30X_REG_CNTL1,
100 AMI30X_BIT_CNTL1_PC1 |
101 AMI30X_BIT_CNTL1_FS1);
102 if (result) {
103 LOG_RESULT_LOCATION(result);
104 return result;
105 }
106 /* Set CNTL2 reg to DRDY active high and enabled */
107 result =
108 inv_serial_single_write(mlsl_handle, pdata->address,
109 AMI30X_REG_CNTL2,
110 AMI30X_BIT_CNTL2_DREN |
111 AMI30X_BIT_CNTL2_DRP);
112 if (result) {
113 LOG_RESULT_LOCATION(result);
114 return result;
115 }
116 /* Set CNTL3 reg to forced measurement period */
117 result =
118 inv_serial_single_write(mlsl_handle, pdata->address,
119 AMI30X_REG_CNTL3, AMI30X_BIT_CNTL3_F0RCE);
120
121 return result;
122}
123
124static int ami30x_read(void *mlsl_handle,
125 struct ext_slave_descr *slave,
126 struct ext_slave_platform_data *pdata,
127 unsigned char *data)
128{
129 unsigned char stat;
130 int result = INV_SUCCESS;
131
132 /* Read status reg and check if data ready (DRDY) */
133 result =
134 inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_STAT1,
135 1, &stat);
136 if (result) {
137 LOG_RESULT_LOCATION(result);
138 return result;
139 }
140
141 if (stat & 0x40) {
142 result =
143 inv_serial_read(mlsl_handle, pdata->address,
144 AMI30X_REG_DATAX, 6, (unsigned char *)data);
145 if (result) {
146 LOG_RESULT_LOCATION(result);
147 return result;
148 }
149 /* start another measurement */
150 result =
151 inv_serial_single_write(mlsl_handle, pdata->address,
152 AMI30X_REG_CNTL3,
153 AMI30X_BIT_CNTL3_F0RCE);
154 if (result) {
155 LOG_RESULT_LOCATION(result);
156 return result;
157 }
158
159 return INV_SUCCESS;
160 }
161
162 return INV_ERROR_COMPASS_DATA_NOT_READY;
163}
164
165
166/* For AMI305,the range field needs to be modified to {9830.4f} */
167static struct ext_slave_descr ami30x_descr = {
168 .init = NULL,
169 .exit = NULL,
170 .suspend = ami30x_suspend,
171 .resume = ami30x_resume,
172 .read = ami30x_read,
173 .config = NULL,
174 .get_config = NULL,
175 .name = "ami30x",
176 .type = EXT_SLAVE_TYPE_COMPASS,
177 .id = COMPASS_ID_AMI30X,
178 .read_reg = 0x06,
179 .read_len = 6,
180 .endian = EXT_SLAVE_LITTLE_ENDIAN,
181 .range = {5461, 3333},
182 .trigger = NULL,
183};
184
185static
186struct ext_slave_descr *ami30x_get_slave_descr(void)
187{
188 return &ami30x_descr;
189}
190
191/* -------------------------------------------------------------------------- */
192struct ami30x_mod_private_data {
193 struct i2c_client *client;
194 struct ext_slave_platform_data *pdata;
195};
196
197static unsigned short normal_i2c[] = { I2C_CLIENT_END };
198
199static int ami30x_mod_probe(struct i2c_client *client,
200 const struct i2c_device_id *devid)
201{
202 struct ext_slave_platform_data *pdata;
203 struct ami30x_mod_private_data *private_data;
204 int result = 0;
205
206 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
207
208 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
209 result = -ENODEV;
210 goto out_no_free;
211 }
212
213 pdata = client->dev.platform_data;
214 if (!pdata) {
215 dev_err(&client->adapter->dev,
216 "Missing platform data for slave %s\n", devid->name);
217 result = -EFAULT;
218 goto out_no_free;
219 }
220
221 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
222 if (!private_data) {
223 result = -ENOMEM;
224 goto out_no_free;
225 }
226
227 i2c_set_clientdata(client, private_data);
228 private_data->client = client;
229 private_data->pdata = pdata;
230
231 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
232 ami30x_get_slave_descr);
233 if (result) {
234 dev_err(&client->adapter->dev,
235 "Slave registration failed: %s, %d\n",
236 devid->name, result);
237 goto out_free_memory;
238 }
239
240 return result;
241
242out_free_memory:
243 kfree(private_data);
244out_no_free:
245 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
246 return result;
247
248}
249
250static int ami30x_mod_remove(struct i2c_client *client)
251{
252 struct ami30x_mod_private_data *private_data =
253 i2c_get_clientdata(client);
254
255 dev_dbg(&client->adapter->dev, "%s\n", __func__);
256
257 inv_mpu_unregister_slave(client, private_data->pdata,
258 ami30x_get_slave_descr);
259
260 kfree(private_data);
261 return 0;
262}
263
264static const struct i2c_device_id ami30x_mod_id[] = {
265 { "ami30x", COMPASS_ID_AMI30X },
266 {}
267};
268
269MODULE_DEVICE_TABLE(i2c, ami30x_mod_id);
270
271static struct i2c_driver ami30x_mod_driver = {
272 .class = I2C_CLASS_HWMON,
273 .probe = ami30x_mod_probe,
274 .remove = ami30x_mod_remove,
275 .id_table = ami30x_mod_id,
276 .driver = {
277 .owner = THIS_MODULE,
278 .name = "ami30x_mod",
279 },
280 .address_list = normal_i2c,
281};
282
283static int __init ami30x_mod_init(void)
284{
285 int res = i2c_add_driver(&ami30x_mod_driver);
286 pr_info("%s: Probe name %s\n", __func__, "ami30x_mod");
287 if (res)
288 pr_err("%s failed\n", __func__);
289 return res;
290}
291
292static void __exit ami30x_mod_exit(void)
293{
294 pr_info("%s\n", __func__);
295 i2c_del_driver(&ami30x_mod_driver);
296}
297
298module_init(ami30x_mod_init);
299module_exit(ami30x_mod_exit);
300
301MODULE_AUTHOR("Invensense Corporation");
302MODULE_DESCRIPTION("Driver to integrate AMI30X sensor with the MPU");
303MODULE_LICENSE("GPL");
304MODULE_ALIAS("ami30x_mod");
305
306/**
307 * @}
308 */
diff --git a/drivers/misc/inv_mpu/compass/ami_hw.h b/drivers/misc/inv_mpu/compass/ami_hw.h
new file mode 100644
index 00000000000..32a04e91cdc
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami_hw.h
@@ -0,0 +1,87 @@
1/*
2 * Copyright (C) 2010 Information System Products Co.,Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef AMI_HW_H
18#define AMI_HW_H
19
20#define AMI_I2C_BUS_NUM 2
21
22#ifdef AMI304_MODEL
23#define AMI_I2C_ADDRESS 0x0F
24#else
25#define AMI_I2C_ADDRESS 0x0E
26#endif
27
28#define AMI_GPIO_INT 152
29#define AMI_GPIO_DRDY 153
30
31/* AMI-Sensor Internal Register Address
32 *(Please refer to AMI-Sensor Specifications)
33 */
34#define AMI_MOREINFO_CMDCODE 0x0d
35#define AMI_WHOIAM_CMDCODE 0x0f
36#define AMI_REG_DATAX 0x10
37#define AMI_REG_DATAY 0x12
38#define AMI_REG_DATAZ 0x14
39#define AMI_REG_STA1 0x18
40#define AMI_REG_CTRL1 0x1b
41#define AMI_REG_CTRL2 0x1c
42#define AMI_REG_CTRL3 0x1d
43#define AMI_REG_B0X 0x20
44#define AMI_REG_B0Y 0x22
45#define AMI_REG_B0Z 0x24
46#define AMI_REG_CTRL5 0x40
47#define AMI_REG_CTRL4 0x5c
48#define AMI_REG_TEMP 0x60
49#define AMI_REG_DELAYX 0x68
50#define AMI_REG_DELAYY 0x6e
51#define AMI_REG_DELAYZ 0x74
52#define AMI_REG_OFFX 0x6c
53#define AMI_REG_OFFY 0x72
54#define AMI_REG_OFFZ 0x78
55#define AMI_FINEOUTPUT_X 0x90
56#define AMI_FINEOUTPUT_Y 0x92
57#define AMI_FINEOUTPUT_Z 0x94
58#define AMI_REG_SENX 0x96
59#define AMI_REG_SENY 0x98
60#define AMI_REG_SENZ 0x9a
61#define AMI_REG_GAINX 0x9c
62#define AMI_REG_GAINY 0x9e
63#define AMI_REG_GAINZ 0xa0
64#define AMI_GETVERSION_CMDCODE 0xe8
65#define AMI_SERIALNUMBER_CMDCODE 0xea
66#define AMI_REG_B0OTPX 0xa2
67#define AMI_REG_B0OTPY 0xb8
68#define AMI_REG_B0OTPZ 0xce
69#define AMI_REG_OFFOTPX 0xf8
70#define AMI_REG_OFFOTPY 0xfa
71#define AMI_REG_OFFOTPZ 0xfc
72
73/* AMI-Sensor Control Bit (Please refer to AMI-Sensor Specifications) */
74#define AMI_CTRL1_PC1 0x80
75#define AMI_CTRL1_FS1_FORCE 0x02
76#define AMI_CTRL1_ODR1 0x10
77#define AMI_CTRL2_DREN 0x08
78#define AMI_CTRL2_DRP 0x04
79#define AMI_CTRL3_FORCE_BIT 0x40
80#define AMI_CTRL3_B0_LO_BIT 0x10
81#define AMI_CTRL3_SRST_BIT 0x80
82#define AMI_CTRL4_HS 0xa07e
83#define AMI_CTRL4_AB 0x0001
84#define AMI_STA1_DRDY_BIT 0x40
85#define AMI_STA1_DOR_BIT 0x20
86
87#endif
diff --git a/drivers/misc/inv_mpu/compass/ami_sensor_def.h b/drivers/misc/inv_mpu/compass/ami_sensor_def.h
new file mode 100644
index 00000000000..64032e2bf1f
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami_sensor_def.h
@@ -0,0 +1,144 @@
1/*
2 * Copyright (C) 2010 Information System Products Co.,Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Definitions for ami306 compass chip.
19 */
20#ifndef AMI_SENSOR_DEF_H
21#define AMI_SENSOR_DEF_H
22
23/*********************************************************************
24 Constant
25 *********************************************************************/
26#define AMI_OK 0x00 /**< Normal */
27#define AMI_PARAM_ERR 0x01 /**< Parameter Error */
28#define AMI_SEQ_ERR 0x02 /**< Squence Error */
29#define AMI_SYSTEM_ERR 0x10 /**< System Error */
30#define AMI_BLOCK_ERR 0x20 /**< Block Error */
31#define AMI_ERROR 0x99 /**< other Error */
32
33/*********************************************************************
34 Struct definition
35 *********************************************************************/
36/** axis sensitivity(gain) calibration parameter information */
37struct ami_vector3d {
38 signed short x; /**< X-axis */
39 signed short y; /**< Y-axis */
40 signed short z; /**< Z-axis */
41};
42
43/** axis interference information */
44struct ami_interference {
45 /**< Y-axis magnetic field for X-axis correction value */
46 signed short xy;
47 /**< Z-axis magnetic field for X-axis correction value */
48 signed short xz;
49 /**< X-axis magnetic field for Y-axis correction value */
50 signed short yx;
51 /**< Z-axis magnetic field for Y-axis correction value */
52 signed short yz;
53 /**< X-axis magnetic field for Z-axis correction value */
54 signed short zx;
55 /**< Y-axis magnetic field for Z-axis correction value */
56 signed short zy;
57};
58
59/** sensor calibration Parameter information */
60struct ami_sensor_parametor {
61 /**< geomagnetic field sensor gain */
62 struct ami_vector3d m_gain;
63 /**< geomagnetic field sensor gain correction parameter */
64 struct ami_vector3d m_gain_cor;
65 /**< geomagnetic field sensor offset */
66 struct ami_vector3d m_offset;
67 /**< geomagnetic field sensor axis interference parameter */
68 struct ami_interference m_interference;
69#ifdef AMI_6AXIS
70 /**< acceleration sensor gain */
71 struct ami_vector3d a_gain;
72 /**< acceleration sensor offset */
73 struct ami_vector3d a_offset;
74 /**< acceleration sensor deviation */
75 signed short a_deviation;
76#endif
77};
78
79/** G2-Sensor measurement value (voltage ADC value ) */
80struct ami_sensor_rawvalue {
81 /**< geomagnetic field sensor measurement X-axis value
82 (mounted position/direction reference) */
83 unsigned short mx;
84 /**< geomagnetic field sensor measurement Y-axis value
85 (mounted position/direction reference) */
86 unsigned short my;
87 /**< geomagnetic field sensor measurement Z-axis value
88 (mounted position/direction reference) */
89 unsigned short mz;
90#ifdef AMI_6AXIS
91 /**< acceleration sensor measurement X-axis value
92 (mounted position/direction reference) */
93 unsigned short ax;
94 /**< acceleration sensor measurement Y-axis value
95 (mounted position/direction reference) */
96 unsigned short ay;
97 /**< acceleration sensor measurement Z-axis value
98 (mounted position/direction reference) */
99 unsigned short az;
100#endif
101 /**< temperature sensor measurement value */
102 unsigned short temperature;
103};
104
105/** Window function Parameter information */
106struct ami_win_parameter {
107 /**< current fine value */
108 struct ami_vector3d m_fine;
109 /**< change per 1coarse */
110 struct ami_vector3d m_fine_output;
111 /**< fine value at zero gauss */
112 struct ami_vector3d m_0Gauss_fine;
113#ifdef AMI304
114 /**< current b0 value */
115 struct ami_vector3d m_b0;
116 /**< current coarse value */
117 struct ami_vector3d m_coar;
118 /**< change per 1fine */
119 struct ami_vector3d m_coar_output;
120 /**< coarse value at zero gauss */
121 struct ami_vector3d m_0Gauss_coar;
122 /**< delay value */
123 struct ami_vector3d m_delay;
124#endif
125};
126
127/** AMI chip information ex) 1)model 2)s/n 3)ver 4)more info in the chip */
128struct ami_chipinfo {
129 unsigned short info; /* INFO 0x0d/0x0e reg. */
130 unsigned short ver; /* VER 0xe8/0xe9 reg. */
131 unsigned short sn; /* SN 0xea/0xeb reg. */
132 unsigned char wia; /* WIA 0x0f reg. */
133};
134
135/** AMI Driver Information */
136struct ami_driverinfo {
137 unsigned char remarks[40]; /* Some Information */
138 unsigned char datetime[30]; /* compiled date&time */
139 unsigned char ver_major; /* major version */
140 unsigned char ver_middle; /* middle.. */
141 unsigned char ver_minor; /* minor .. */
142};
143
144#endif
diff --git a/drivers/misc/inv_mpu/compass/hmc5883.c b/drivers/misc/inv_mpu/compass/hmc5883.c
new file mode 100644
index 00000000000..fdf2ac00565
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hmc5883.c
@@ -0,0 +1,391 @@
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 COMPASSDL
22 *
23 * @{
24 * @file hmc5883.c
25 * @brief Magnetometer setup and handling methods for Honeywell
26 * HMC5883 compass.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48enum HMC_REG {
49 HMC_REG_CONF_A = 0x0,
50 HMC_REG_CONF_B = 0x1,
51 HMC_REG_MODE = 0x2,
52 HMC_REG_X_M = 0x3,
53 HMC_REG_X_L = 0x4,
54 HMC_REG_Z_M = 0x5,
55 HMC_REG_Z_L = 0x6,
56 HMC_REG_Y_M = 0x7,
57 HMC_REG_Y_L = 0x8,
58 HMC_REG_STATUS = 0x9,
59 HMC_REG_ID_A = 0xA,
60 HMC_REG_ID_B = 0xB,
61 HMC_REG_ID_C = 0xC
62};
63
64enum HMC_CONF_A {
65 HMC_CONF_A_DRATE_MASK = 0x1C,
66 HMC_CONF_A_DRATE_0_75 = 0x00,
67 HMC_CONF_A_DRATE_1_5 = 0x04,
68 HMC_CONF_A_DRATE_3 = 0x08,
69 HMC_CONF_A_DRATE_7_5 = 0x0C,
70 HMC_CONF_A_DRATE_15 = 0x10,
71 HMC_CONF_A_DRATE_30 = 0x14,
72 HMC_CONF_A_DRATE_75 = 0x18,
73 HMC_CONF_A_MEAS_MASK = 0x3,
74 HMC_CONF_A_MEAS_NORM = 0x0,
75 HMC_CONF_A_MEAS_POS = 0x1,
76 HMC_CONF_A_MEAS_NEG = 0x2
77};
78
79enum HMC_CONF_B {
80 HMC_CONF_B_GAIN_MASK = 0xE0,
81 HMC_CONF_B_GAIN_0_9 = 0x00,
82 HMC_CONF_B_GAIN_1_2 = 0x20,
83 HMC_CONF_B_GAIN_1_9 = 0x40,
84 HMC_CONF_B_GAIN_2_5 = 0x60,
85 HMC_CONF_B_GAIN_4_0 = 0x80,
86 HMC_CONF_B_GAIN_4_6 = 0xA0,
87 HMC_CONF_B_GAIN_5_5 = 0xC0,
88 HMC_CONF_B_GAIN_7_9 = 0xE0
89};
90
91enum HMC_MODE {
92 HMC_MODE_MASK = 0x3,
93 HMC_MODE_CONT = 0x0,
94 HMC_MODE_SINGLE = 0x1,
95 HMC_MODE_IDLE = 0x2,
96 HMC_MODE_SLEEP = 0x3
97};
98
99/* -------------------------------------------------------------------------- */
100static int hmc5883_suspend(void *mlsl_handle,
101 struct ext_slave_descr *slave,
102 struct ext_slave_platform_data *pdata)
103{
104 int result = INV_SUCCESS;
105
106 result =
107 inv_serial_single_write(mlsl_handle, pdata->address,
108 HMC_REG_MODE, HMC_MODE_SLEEP);
109 if (result) {
110 LOG_RESULT_LOCATION(result);
111 return result;
112 }
113 msleep(3);
114
115 return result;
116}
117
118static int hmc5883_resume(void *mlsl_handle,
119 struct ext_slave_descr *slave,
120 struct ext_slave_platform_data *pdata)
121{
122 int result = INV_SUCCESS;
123
124 /* Use single measurement mode. Start at sleep state. */
125 result =
126 inv_serial_single_write(mlsl_handle, pdata->address,
127 HMC_REG_MODE, HMC_MODE_SLEEP);
128 if (result) {
129 LOG_RESULT_LOCATION(result);
130 return result;
131 }
132 /* Config normal measurement */
133 result =
134 inv_serial_single_write(mlsl_handle, pdata->address,
135 HMC_REG_CONF_A, 0);
136 if (result) {
137 LOG_RESULT_LOCATION(result);
138 return result;
139 }
140 /* Adjust gain to 307 LSB/Gauss */
141 result =
142 inv_serial_single_write(mlsl_handle, pdata->address,
143 HMC_REG_CONF_B, HMC_CONF_B_GAIN_5_5);
144 if (result) {
145 LOG_RESULT_LOCATION(result);
146 return result;
147 }
148
149 return result;
150}
151
152static int hmc5883_read(void *mlsl_handle,
153 struct ext_slave_descr *slave,
154 struct ext_slave_platform_data *pdata,
155 unsigned char *data)
156{
157 unsigned char stat;
158 int result = INV_SUCCESS;
159 unsigned char tmp;
160 short axisFixed;
161
162 /* Read status reg. to check if data is ready */
163 result =
164 inv_serial_read(mlsl_handle, pdata->address, HMC_REG_STATUS, 1,
165 &stat);
166 if (result) {
167 LOG_RESULT_LOCATION(result);
168 return result;
169 }
170 if (stat & 0x01) {
171 result =
172 inv_serial_read(mlsl_handle, pdata->address,
173 HMC_REG_X_M, 6, (unsigned char *)data);
174 if (result) {
175 LOG_RESULT_LOCATION(result);
176 return result;
177 }
178
179 /* switch YZ axis to proper position */
180 tmp = data[2];
181 data[2] = data[4];
182 data[4] = tmp;
183 tmp = data[3];
184 data[3] = data[5];
185 data[5] = tmp;
186
187 /*drop data if overflows */
188 if ((data[0] == 0xf0) || (data[2] == 0xf0)
189 || (data[4] == 0xf0)) {
190 /* trigger next measurement read */
191 result =
192 inv_serial_single_write(mlsl_handle,
193 pdata->address,
194 HMC_REG_MODE,
195 HMC_MODE_SINGLE);
196 if (result) {
197 LOG_RESULT_LOCATION(result);
198 return result;
199 }
200 return INV_ERROR_COMPASS_DATA_OVERFLOW;
201 }
202 /* convert to fixed point and apply sensitivity correction for
203 Z-axis */
204 axisFixed =
205 (short)((unsigned short)data[5] +
206 (unsigned short)data[4] * 256);
207 /* scale up by 1.125 (36/32) */
208 axisFixed = (short)(axisFixed * 36);
209 data[4] = axisFixed >> 8;
210 data[5] = axisFixed & 0xFF;
211
212 axisFixed =
213 (short)((unsigned short)data[3] +
214 (unsigned short)data[2] * 256);
215 axisFixed = (short)(axisFixed * 32);
216 data[2] = axisFixed >> 8;
217 data[3] = axisFixed & 0xFF;
218
219 axisFixed =
220 (short)((unsigned short)data[1] +
221 (unsigned short)data[0] * 256);
222 axisFixed = (short)(axisFixed * 32);
223 data[0] = axisFixed >> 8;
224 data[1] = axisFixed & 0xFF;
225
226 /* trigger next measurement read */
227 result =
228 inv_serial_single_write(mlsl_handle, pdata->address,
229 HMC_REG_MODE, HMC_MODE_SINGLE);
230 if (result) {
231 LOG_RESULT_LOCATION(result);
232 return result;
233 }
234
235 return INV_SUCCESS;
236 } else {
237 /* trigger next measurement read */
238 result =
239 inv_serial_single_write(mlsl_handle, pdata->address,
240 HMC_REG_MODE, HMC_MODE_SINGLE);
241 if (result) {
242 LOG_RESULT_LOCATION(result);
243 return result;
244 }
245
246 return INV_ERROR_COMPASS_DATA_NOT_READY;
247 }
248}
249
250static struct ext_slave_descr hmc5883_descr = {
251 .init = NULL,
252 .exit = NULL,
253 .suspend = hmc5883_suspend,
254 .resume = hmc5883_resume,
255 .read = hmc5883_read,
256 .config = NULL,
257 .get_config = NULL,
258 .name = "hmc5883",
259 .type = EXT_SLAVE_TYPE_COMPASS,
260 .id = COMPASS_ID_HMC5883,
261 .read_reg = 0x06,
262 .read_len = 6,
263 .endian = EXT_SLAVE_BIG_ENDIAN,
264 .range = {10673, 6156},
265 .trigger = NULL,
266};
267
268static
269struct ext_slave_descr *hmc5883_get_slave_descr(void)
270{
271 return &hmc5883_descr;
272}
273
274/* -------------------------------------------------------------------------- */
275struct hmc5883_mod_private_data {
276 struct i2c_client *client;
277 struct ext_slave_platform_data *pdata;
278};
279
280static unsigned short normal_i2c[] = { I2C_CLIENT_END };
281
282static int hmc5883_mod_probe(struct i2c_client *client,
283 const struct i2c_device_id *devid)
284{
285 struct ext_slave_platform_data *pdata;
286 struct hmc5883_mod_private_data *private_data;
287 int result = 0;
288
289 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
290
291 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
292 result = -ENODEV;
293 goto out_no_free;
294 }
295
296 pdata = client->dev.platform_data;
297 if (!pdata) {
298 dev_err(&client->adapter->dev,
299 "Missing platform data for slave %s\n", devid->name);
300 result = -EFAULT;
301 goto out_no_free;
302 }
303
304 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
305 if (!private_data) {
306 result = -ENOMEM;
307 goto out_no_free;
308 }
309
310 i2c_set_clientdata(client, private_data);
311 private_data->client = client;
312 private_data->pdata = pdata;
313
314 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
315 hmc5883_get_slave_descr);
316 if (result) {
317 dev_err(&client->adapter->dev,
318 "Slave registration failed: %s, %d\n",
319 devid->name, result);
320 goto out_free_memory;
321 }
322
323 return result;
324
325out_free_memory:
326 kfree(private_data);
327out_no_free:
328 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
329 return result;
330
331}
332
333static int hmc5883_mod_remove(struct i2c_client *client)
334{
335 struct hmc5883_mod_private_data *private_data =
336 i2c_get_clientdata(client);
337
338 dev_dbg(&client->adapter->dev, "%s\n", __func__);
339
340 inv_mpu_unregister_slave(client, private_data->pdata,
341 hmc5883_get_slave_descr);
342
343 kfree(private_data);
344 return 0;
345}
346
347static const struct i2c_device_id hmc5883_mod_id[] = {
348 { "hmc5883", COMPASS_ID_HMC5883 },
349 {}
350};
351
352MODULE_DEVICE_TABLE(i2c, hmc5883_mod_id);
353
354static struct i2c_driver hmc5883_mod_driver = {
355 .class = I2C_CLASS_HWMON,
356 .probe = hmc5883_mod_probe,
357 .remove = hmc5883_mod_remove,
358 .id_table = hmc5883_mod_id,
359 .driver = {
360 .owner = THIS_MODULE,
361 .name = "hmc5883_mod",
362 },
363 .address_list = normal_i2c,
364};
365
366static int __init hmc5883_mod_init(void)
367{
368 int res = i2c_add_driver(&hmc5883_mod_driver);
369 pr_info("%s: Probe name %s\n", __func__, "hmc5883_mod");
370 if (res)
371 pr_err("%s failed\n", __func__);
372 return res;
373}
374
375static void __exit hmc5883_mod_exit(void)
376{
377 pr_info("%s\n", __func__);
378 i2c_del_driver(&hmc5883_mod_driver);
379}
380
381module_init(hmc5883_mod_init);
382module_exit(hmc5883_mod_exit);
383
384MODULE_AUTHOR("Invensense Corporation");
385MODULE_DESCRIPTION("Driver to integrate HMC5883 sensor with the MPU");
386MODULE_LICENSE("GPL");
387MODULE_ALIAS("hmc5883_mod");
388
389/**
390 * @}
391 */
diff --git a/drivers/misc/inv_mpu/compass/hscdtd002b.c b/drivers/misc/inv_mpu/compass/hscdtd002b.c
new file mode 100644
index 00000000000..4f6013cbe3d
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hscdtd002b.c
@@ -0,0 +1,294 @@
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 COMPASSDL
22 *
23 * @{
24 * @file hscdtd002b.c
25 * @brief Magnetometer setup and handling methods for Alps HSCDTD002B
26 * compass.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48#define COMPASS_HSCDTD002B_STAT (0x18)
49#define COMPASS_HSCDTD002B_CTRL1 (0x1B)
50#define COMPASS_HSCDTD002B_CTRL2 (0x1C)
51#define COMPASS_HSCDTD002B_CTRL3 (0x1D)
52#define COMPASS_HSCDTD002B_DATAX (0x10)
53
54/* -------------------------------------------------------------------------- */
55static int hscdtd002b_suspend(void *mlsl_handle,
56 struct ext_slave_descr *slave,
57 struct ext_slave_platform_data *pdata)
58{
59 int result = INV_SUCCESS;
60
61 /* Power mode: stand-by */
62 result =
63 inv_serial_single_write(mlsl_handle, pdata->address,
64 COMPASS_HSCDTD002B_CTRL1, 0x00);
65 if (result) {
66 LOG_RESULT_LOCATION(result);
67 return result;
68 }
69 msleep(1); /* turn-off time */
70
71 return result;
72}
73
74static int hscdtd002b_resume(void *mlsl_handle,
75 struct ext_slave_descr *slave,
76 struct ext_slave_platform_data *pdata)
77{
78 int result = INV_SUCCESS;
79
80 /* Soft reset */
81 result =
82 inv_serial_single_write(mlsl_handle, pdata->address,
83 COMPASS_HSCDTD002B_CTRL3, 0x80);
84 if (result) {
85 LOG_RESULT_LOCATION(result);
86 return result;
87 }
88 /* Force state; Power mode: active */
89 result =
90 inv_serial_single_write(mlsl_handle, pdata->address,
91 COMPASS_HSCDTD002B_CTRL1, 0x82);
92 if (result) {
93 LOG_RESULT_LOCATION(result);
94 return result;
95 }
96 /* Data ready enable */
97 result =
98 inv_serial_single_write(mlsl_handle, pdata->address,
99 COMPASS_HSCDTD002B_CTRL2, 0x08);
100 if (result) {
101 LOG_RESULT_LOCATION(result);
102 return result;
103 }
104 msleep(1); /* turn-on time */
105
106 return result;
107}
108
109static int hscdtd002b_read(void *mlsl_handle,
110 struct ext_slave_descr *slave,
111 struct ext_slave_platform_data *pdata,
112 unsigned char *data)
113{
114 unsigned char stat;
115 int result = INV_SUCCESS;
116 int status = INV_SUCCESS;
117
118 /* Read status reg. to check if data is ready */
119 result =
120 inv_serial_read(mlsl_handle, pdata->address,
121 COMPASS_HSCDTD002B_STAT, 1, &stat);
122 if (result) {
123 LOG_RESULT_LOCATION(result);
124 return result;
125 }
126 if (stat & 0x40) {
127 result =
128 inv_serial_read(mlsl_handle, pdata->address,
129 COMPASS_HSCDTD002B_DATAX, 6,
130 (unsigned char *)data);
131 if (result) {
132 LOG_RESULT_LOCATION(result);
133 return result;
134 }
135 status = INV_SUCCESS;
136 } else if (stat & 0x20) {
137 status = INV_ERROR_COMPASS_DATA_OVERFLOW;
138 } else {
139 status = INV_ERROR_COMPASS_DATA_NOT_READY;
140 }
141 /* trigger next measurement read */
142 result =
143 inv_serial_single_write(mlsl_handle, pdata->address,
144 COMPASS_HSCDTD002B_CTRL3, 0x40);
145 if (result) {
146 LOG_RESULT_LOCATION(result);
147 return result;
148 }
149
150 return status;
151}
152
153static struct ext_slave_descr hscdtd002b_descr = {
154 .init = NULL,
155 .exit = NULL,
156 .suspend = hscdtd002b_suspend,
157 .resume = hscdtd002b_resume,
158 .read = hscdtd002b_read,
159 .config = NULL,
160 .get_config = NULL,
161 .name = "hscdtd002b",
162 .type = EXT_SLAVE_TYPE_COMPASS,
163 .id = COMPASS_ID_HSCDTD002B,
164 .read_reg = 0x10,
165 .read_len = 6,
166 .endian = EXT_SLAVE_LITTLE_ENDIAN,
167 .range = {9830, 4000},
168 .trigger = NULL,
169};
170
171static
172struct ext_slave_descr *hscdtd002b_get_slave_descr(void)
173{
174 return &hscdtd002b_descr;
175}
176
177/* -------------------------------------------------------------------------- */
178struct hscdtd002b_mod_private_data {
179 struct i2c_client *client;
180 struct ext_slave_platform_data *pdata;
181};
182
183static unsigned short normal_i2c[] = { I2C_CLIENT_END };
184
185static int hscdtd002b_mod_probe(struct i2c_client *client,
186 const struct i2c_device_id *devid)
187{
188 struct ext_slave_platform_data *pdata;
189 struct hscdtd002b_mod_private_data *private_data;
190 int result = 0;
191
192 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
193
194 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
195 result = -ENODEV;
196 goto out_no_free;
197 }
198
199 pdata = client->dev.platform_data;
200 if (!pdata) {
201 dev_err(&client->adapter->dev,
202 "Missing platform data for slave %s\n", devid->name);
203 result = -EFAULT;
204 goto out_no_free;
205 }
206
207 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
208 if (!private_data) {
209 result = -ENOMEM;
210 goto out_no_free;
211 }
212
213 i2c_set_clientdata(client, private_data);
214 private_data->client = client;
215 private_data->pdata = pdata;
216
217 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
218 hscdtd002b_get_slave_descr);
219 if (result) {
220 dev_err(&client->adapter->dev,
221 "Slave registration failed: %s, %d\n",
222 devid->name, result);
223 goto out_free_memory;
224 }
225
226 return result;
227
228out_free_memory:
229 kfree(private_data);
230out_no_free:
231 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
232 return result;
233
234}
235
236static int hscdtd002b_mod_remove(struct i2c_client *client)
237{
238 struct hscdtd002b_mod_private_data *private_data =
239 i2c_get_clientdata(client);
240
241 dev_dbg(&client->adapter->dev, "%s\n", __func__);
242
243 inv_mpu_unregister_slave(client, private_data->pdata,
244 hscdtd002b_get_slave_descr);
245
246 kfree(private_data);
247 return 0;
248}
249
250static const struct i2c_device_id hscdtd002b_mod_id[] = {
251 { "hscdtd002b", COMPASS_ID_HSCDTD002B },
252 {}
253};
254
255MODULE_DEVICE_TABLE(i2c, hscdtd002b_mod_id);
256
257static struct i2c_driver hscdtd002b_mod_driver = {
258 .class = I2C_CLASS_HWMON,
259 .probe = hscdtd002b_mod_probe,
260 .remove = hscdtd002b_mod_remove,
261 .id_table = hscdtd002b_mod_id,
262 .driver = {
263 .owner = THIS_MODULE,
264 .name = "hscdtd002b_mod",
265 },
266 .address_list = normal_i2c,
267};
268
269static int __init hscdtd002b_mod_init(void)
270{
271 int res = i2c_add_driver(&hscdtd002b_mod_driver);
272 pr_info("%s: Probe name %s\n", __func__, "hscdtd002b_mod");
273 if (res)
274 pr_err("%s failed\n", __func__);
275 return res;
276}
277
278static void __exit hscdtd002b_mod_exit(void)
279{
280 pr_info("%s\n", __func__);
281 i2c_del_driver(&hscdtd002b_mod_driver);
282}
283
284module_init(hscdtd002b_mod_init);
285module_exit(hscdtd002b_mod_exit);
286
287MODULE_AUTHOR("Invensense Corporation");
288MODULE_DESCRIPTION("Driver to integrate HSCDTD002B sensor with the MPU");
289MODULE_LICENSE("GPL");
290MODULE_ALIAS("hscdtd002b_mod");
291
292/**
293 * @}
294 */
diff --git a/drivers/misc/inv_mpu/compass/hscdtd004a.c b/drivers/misc/inv_mpu/compass/hscdtd004a.c
new file mode 100644
index 00000000000..f0915599bd2
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hscdtd004a.c
@@ -0,0 +1,318 @@
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 COMPASSDL
22 *
23 * @{
24 * @file hscdtd004a.c
25 * @brief Magnetometer setup and handling methods for Alps HSCDTD004A
26 * compass.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48#define COMPASS_HSCDTD004A_STAT (0x18)
49#define COMPASS_HSCDTD004A_CTRL1 (0x1B)
50#define COMPASS_HSCDTD004A_CTRL2 (0x1C)
51#define COMPASS_HSCDTD004A_CTRL3 (0x1D)
52#define COMPASS_HSCDTD004A_DATAX (0x10)
53
54/* -------------------------------------------------------------------------- */
55
56static int hscdtd004a_suspend(void *mlsl_handle,
57 struct ext_slave_descr *slave,
58 struct ext_slave_platform_data *pdata)
59{
60 int result = INV_SUCCESS;
61
62 /* Power mode: stand-by */
63 result =
64 inv_serial_single_write(mlsl_handle, pdata->address,
65 COMPASS_HSCDTD004A_CTRL1, 0x00);
66 if (result) {
67 LOG_RESULT_LOCATION(result);
68 return result;
69 }
70 msleep(1); /* turn-off time */
71
72 return result;
73}
74
75static int hscdtd004a_init(void *mlsl_handle,
76 struct ext_slave_descr *slave,
77 struct ext_slave_platform_data *pdata)
78{
79 int result;
80 unsigned char data1, data2[2];
81
82 result = inv_serial_read(mlsl_handle, pdata->address, 0xf, 1, &data1);
83 if (result) {
84 LOG_RESULT_LOCATION(result);
85 return result;
86 }
87 result = inv_serial_read(mlsl_handle, pdata->address, 0xd, 2, data2);
88 if (result) {
89 LOG_RESULT_LOCATION(result);
90 return result;
91 }
92 if (data1 != 0x49 || data2[0] != 0x45 || data2[1] != 0x54) {
93 LOG_RESULT_LOCATION(INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED);
94 return INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED;
95 }
96 return result;
97}
98
99static int hscdtd004a_resume(void *mlsl_handle,
100 struct ext_slave_descr *slave,
101 struct ext_slave_platform_data *pdata)
102{
103 int result = INV_SUCCESS;
104
105 /* Soft reset */
106 result =
107 inv_serial_single_write(mlsl_handle, pdata->address,
108 COMPASS_HSCDTD004A_CTRL3, 0x80);
109 if (result) {
110 LOG_RESULT_LOCATION(result);
111 return result;
112 }
113 /* Normal state; Power mode: active */
114 result =
115 inv_serial_single_write(mlsl_handle, pdata->address,
116 COMPASS_HSCDTD004A_CTRL1, 0x82);
117 if (result) {
118 LOG_RESULT_LOCATION(result);
119 return result;
120 }
121 /* Data ready enable */
122 result =
123 inv_serial_single_write(mlsl_handle, pdata->address,
124 COMPASS_HSCDTD004A_CTRL2, 0x7C);
125 if (result) {
126 LOG_RESULT_LOCATION(result);
127 return result;
128 }
129 msleep(1); /* turn-on time */
130 return result;
131}
132
133static int hscdtd004a_read(void *mlsl_handle,
134 struct ext_slave_descr *slave,
135 struct ext_slave_platform_data *pdata,
136 unsigned char *data)
137{
138 unsigned char stat;
139 int result = INV_SUCCESS;
140 int status = INV_SUCCESS;
141
142 /* Read status reg. to check if data is ready */
143 result =
144 inv_serial_read(mlsl_handle, pdata->address,
145 COMPASS_HSCDTD004A_STAT, 1, &stat);
146 if (result) {
147 LOG_RESULT_LOCATION(result);
148 return result;
149 }
150 if (stat & 0x48) {
151 result =
152 inv_serial_read(mlsl_handle, pdata->address,
153 COMPASS_HSCDTD004A_DATAX, 6,
154 (unsigned char *)data);
155 if (result) {
156 LOG_RESULT_LOCATION(result);
157 return result;
158 }
159 status = INV_SUCCESS;
160 } else if (stat & 0x68) {
161 status = INV_ERROR_COMPASS_DATA_OVERFLOW;
162 } else {
163 status = INV_ERROR_COMPASS_DATA_NOT_READY;
164 }
165 /* trigger next measurement read */
166 result =
167 inv_serial_single_write(mlsl_handle, pdata->address,
168 COMPASS_HSCDTD004A_CTRL3, 0x40);
169 if (result) {
170 LOG_RESULT_LOCATION(result);
171 return result;
172 }
173 return status;
174
175}
176
177static struct ext_slave_descr hscdtd004a_descr = {
178 .init = hscdtd004a_init,
179 .exit = NULL,
180 .suspend = hscdtd004a_suspend,
181 .resume = hscdtd004a_resume,
182 .read = hscdtd004a_read,
183 .config = NULL,
184 .get_config = NULL,
185 .name = "hscdtd004a",
186 .type = EXT_SLAVE_TYPE_COMPASS,
187 .id = COMPASS_ID_HSCDTD004A,
188 .read_reg = 0x10,
189 .read_len = 6,
190 .endian = EXT_SLAVE_LITTLE_ENDIAN,
191 .range = {9830, 4000},
192 .trigger = NULL,
193};
194
195static
196struct ext_slave_descr *hscdtd004a_get_slave_descr(void)
197{
198 return &hscdtd004a_descr;
199}
200
201/* -------------------------------------------------------------------------- */
202struct hscdtd004a_mod_private_data {
203 struct i2c_client *client;
204 struct ext_slave_platform_data *pdata;
205};
206
207static unsigned short normal_i2c[] = { I2C_CLIENT_END };
208
209static int hscdtd004a_mod_probe(struct i2c_client *client,
210 const struct i2c_device_id *devid)
211{
212 struct ext_slave_platform_data *pdata;
213 struct hscdtd004a_mod_private_data *private_data;
214 int result = 0;
215
216 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
217
218 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
219 result = -ENODEV;
220 goto out_no_free;
221 }
222
223 pdata = client->dev.platform_data;
224 if (!pdata) {
225 dev_err(&client->adapter->dev,
226 "Missing platform data for slave %s\n", devid->name);
227 result = -EFAULT;
228 goto out_no_free;
229 }
230
231 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
232 if (!private_data) {
233 result = -ENOMEM;
234 goto out_no_free;
235 }
236
237 i2c_set_clientdata(client, private_data);
238 private_data->client = client;
239 private_data->pdata = pdata;
240
241 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
242 hscdtd004a_get_slave_descr);
243 if (result) {
244 dev_err(&client->adapter->dev,
245 "Slave registration failed: %s, %d\n",
246 devid->name, result);
247 goto out_free_memory;
248 }
249
250 return result;
251
252out_free_memory:
253 kfree(private_data);
254out_no_free:
255 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
256 return result;
257
258}
259
260static int hscdtd004a_mod_remove(struct i2c_client *client)
261{
262 struct hscdtd004a_mod_private_data *private_data =
263 i2c_get_clientdata(client);
264
265 dev_dbg(&client->adapter->dev, "%s\n", __func__);
266
267 inv_mpu_unregister_slave(client, private_data->pdata,
268 hscdtd004a_get_slave_descr);
269
270 kfree(private_data);
271 return 0;
272}
273
274static const struct i2c_device_id hscdtd004a_mod_id[] = {
275 { "hscdtd004a", COMPASS_ID_HSCDTD004A },
276 {}
277};
278
279MODULE_DEVICE_TABLE(i2c, hscdtd004a_mod_id);
280
281static struct i2c_driver hscdtd004a_mod_driver = {
282 .class = I2C_CLASS_HWMON,
283 .probe = hscdtd004a_mod_probe,
284 .remove = hscdtd004a_mod_remove,
285 .id_table = hscdtd004a_mod_id,
286 .driver = {
287 .owner = THIS_MODULE,
288 .name = "hscdtd004a_mod",
289 },
290 .address_list = normal_i2c,
291};
292
293static int __init hscdtd004a_mod_init(void)
294{
295 int res = i2c_add_driver(&hscdtd004a_mod_driver);
296 pr_info("%s: Probe name %s\n", __func__, "hscdtd004a_mod");
297 if (res)
298 pr_err("%s failed\n", __func__);
299 return res;
300}
301
302static void __exit hscdtd004a_mod_exit(void)
303{
304 pr_info("%s\n", __func__);
305 i2c_del_driver(&hscdtd004a_mod_driver);
306}
307
308module_init(hscdtd004a_mod_init);
309module_exit(hscdtd004a_mod_exit);
310
311MODULE_AUTHOR("Invensense Corporation");
312MODULE_DESCRIPTION("Driver to integrate HSCDTD004A sensor with the MPU");
313MODULE_LICENSE("GPL");
314MODULE_ALIAS("hscdtd004a_mod");
315
316/**
317 * @}
318 */
diff --git a/drivers/misc/inv_mpu/compass/lsm303dlx_m.c b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c
new file mode 100644
index 00000000000..32f8cdddb00
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/lsm303dlx_m.c
@@ -0,0 +1,395 @@
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 COMPASSDL
22 *
23 * @{
24 * @file lsm303dlx_m.c
25 * @brief Magnetometer setup and handling methods for ST LSM303
26 * compass.
27 * This magnetometer device is part of a combo chip with the
28 * ST LIS331DLH accelerometer and the logic in entirely based
29 * on the Honeywell HMC5883 magnetometer.
30 */
31
32/* -------------------------------------------------------------------------- */
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-compass"
49
50/* -------------------------------------------------------------------------- */
51enum LSM_REG {
52 LSM_REG_CONF_A = 0x0,
53 LSM_REG_CONF_B = 0x1,
54 LSM_REG_MODE = 0x2,
55 LSM_REG_X_M = 0x3,
56 LSM_REG_X_L = 0x4,
57 LSM_REG_Z_M = 0x5,
58 LSM_REG_Z_L = 0x6,
59 LSM_REG_Y_M = 0x7,
60 LSM_REG_Y_L = 0x8,
61 LSM_REG_STATUS = 0x9,
62 LSM_REG_ID_A = 0xA,
63 LSM_REG_ID_B = 0xB,
64 LSM_REG_ID_C = 0xC
65};
66
67enum LSM_CONF_A {
68 LSM_CONF_A_DRATE_MASK = 0x1C,
69 LSM_CONF_A_DRATE_0_75 = 0x00,
70 LSM_CONF_A_DRATE_1_5 = 0x04,
71 LSM_CONF_A_DRATE_3 = 0x08,
72 LSM_CONF_A_DRATE_7_5 = 0x0C,
73 LSM_CONF_A_DRATE_15 = 0x10,
74 LSM_CONF_A_DRATE_30 = 0x14,
75 LSM_CONF_A_DRATE_75 = 0x18,
76 LSM_CONF_A_MEAS_MASK = 0x3,
77 LSM_CONF_A_MEAS_NORM = 0x0,
78 LSM_CONF_A_MEAS_POS = 0x1,
79 LSM_CONF_A_MEAS_NEG = 0x2
80};
81
82enum LSM_CONF_B {
83 LSM_CONF_B_GAIN_MASK = 0xE0,
84 LSM_CONF_B_GAIN_0_9 = 0x00,
85 LSM_CONF_B_GAIN_1_2 = 0x20,
86 LSM_CONF_B_GAIN_1_9 = 0x40,
87 LSM_CONF_B_GAIN_2_5 = 0x60,
88 LSM_CONF_B_GAIN_4_0 = 0x80,
89 LSM_CONF_B_GAIN_4_6 = 0xA0,
90 LSM_CONF_B_GAIN_5_5 = 0xC0,
91 LSM_CONF_B_GAIN_7_9 = 0xE0
92};
93
94enum LSM_MODE {
95 LSM_MODE_MASK = 0x3,
96 LSM_MODE_CONT = 0x0,
97 LSM_MODE_SINGLE = 0x1,
98 LSM_MODE_IDLE = 0x2,
99 LSM_MODE_SLEEP = 0x3
100};
101
102/* -------------------------------------------------------------------------- */
103
104static int lsm303dlx_m_suspend(void *mlsl_handle,
105 struct ext_slave_descr *slave,
106 struct ext_slave_platform_data *pdata)
107{
108 int result = INV_SUCCESS;
109
110 result =
111 inv_serial_single_write(mlsl_handle, pdata->address,
112 LSM_REG_MODE, LSM_MODE_SLEEP);
113 if (result) {
114 LOG_RESULT_LOCATION(result);
115 return result;
116 }
117 msleep(3);
118
119 return result;
120}
121
122static int lsm303dlx_m_resume(void *mlsl_handle,
123 struct ext_slave_descr *slave,
124 struct ext_slave_platform_data *pdata)
125{
126 int result = INV_SUCCESS;
127
128 /* Use single measurement mode. Start at sleep state. */
129 result =
130 inv_serial_single_write(mlsl_handle, pdata->address,
131 LSM_REG_MODE, LSM_MODE_SLEEP);
132 if (result) {
133 LOG_RESULT_LOCATION(result);
134 return result;
135 }
136 /* Config normal measurement */
137 result =
138 inv_serial_single_write(mlsl_handle, pdata->address,
139 LSM_REG_CONF_A, 0);
140 if (result) {
141 LOG_RESULT_LOCATION(result);
142 return result;
143 }
144 /* Adjust gain to 320 LSB/Gauss */
145 result =
146 inv_serial_single_write(mlsl_handle, pdata->address,
147 LSM_REG_CONF_B, LSM_CONF_B_GAIN_5_5);
148 if (result) {
149 LOG_RESULT_LOCATION(result);
150 return result;
151 }
152
153 return result;
154}
155
156static int lsm303dlx_m_read(void *mlsl_handle,
157 struct ext_slave_descr *slave,
158 struct ext_slave_platform_data *pdata,
159 unsigned char *data)
160{
161 unsigned char stat;
162 int result = INV_SUCCESS;
163 short axis_fixed;
164
165 /* Read status reg. to check if data is ready */
166 result =
167 inv_serial_read(mlsl_handle, pdata->address, LSM_REG_STATUS, 1,
168 &stat);
169 if (result) {
170 LOG_RESULT_LOCATION(result);
171 return result;
172 }
173 if (stat & 0x01) {
174 result =
175 inv_serial_read(mlsl_handle, pdata->address,
176 LSM_REG_X_M, 6, (unsigned char *)data);
177 if (result) {
178 LOG_RESULT_LOCATION(result);
179 return result;
180 }
181
182 /*drop data if overflows */
183 if ((data[0] == 0xf0) || (data[2] == 0xf0)
184 || (data[4] == 0xf0)) {
185 /* trigger next measurement read */
186 result =
187 inv_serial_single_write(mlsl_handle,
188 pdata->address,
189 LSM_REG_MODE,
190 LSM_MODE_SINGLE);
191 if (result) {
192 LOG_RESULT_LOCATION(result);
193 return result;
194 }
195 return INV_ERROR_COMPASS_DATA_OVERFLOW;
196 }
197 /* convert to fixed point and apply sensitivity correction for
198 Z-axis */
199 axis_fixed =
200 (short)((unsigned short)data[5] +
201 (unsigned short)data[4] * 256);
202 /* scale up by 1.125 (36/32) approximate of 1.122 (320/285) */
203 if (slave->id == COMPASS_ID_LSM303DLM) {
204 /* NOTE/IMPORTANT:
205 lsm303dlm compass axis definition doesn't
206 respect the right hand rule. We invert
207 the sign of the Z axis to fix that. */
208 axis_fixed = (short)(-1 * axis_fixed * 36);
209 } else {
210 axis_fixed = (short)(axis_fixed * 36);
211 }
212 data[4] = axis_fixed >> 8;
213 data[5] = axis_fixed & 0xFF;
214
215 axis_fixed =
216 (short)((unsigned short)data[3] +
217 (unsigned short)data[2] * 256);
218 axis_fixed = (short)(axis_fixed * 32);
219 data[2] = axis_fixed >> 8;
220 data[3] = axis_fixed & 0xFF;
221
222 axis_fixed =
223 (short)((unsigned short)data[1] +
224 (unsigned short)data[0] * 256);
225 axis_fixed = (short)(axis_fixed * 32);
226 data[0] = axis_fixed >> 8;
227 data[1] = axis_fixed & 0xFF;
228
229 /* trigger next measurement read */
230 result =
231 inv_serial_single_write(mlsl_handle, pdata->address,
232 LSM_REG_MODE, LSM_MODE_SINGLE);
233 if (result) {
234 LOG_RESULT_LOCATION(result);
235 return result;
236 }
237
238 return INV_SUCCESS;
239 } else {
240 /* trigger next measurement read */
241 result =
242 inv_serial_single_write(mlsl_handle, pdata->address,
243 LSM_REG_MODE, LSM_MODE_SINGLE);
244 if (result) {
245 LOG_RESULT_LOCATION(result);
246 return result;
247 }
248
249 return INV_ERROR_COMPASS_DATA_NOT_READY;
250 }
251}
252
253static struct ext_slave_descr lsm303dlx_m_descr = {
254 .init = NULL,
255 .exit = NULL,
256 .suspend = lsm303dlx_m_suspend,
257 .resume = lsm303dlx_m_resume,
258 .read = lsm303dlx_m_read,
259 .config = NULL,
260 .get_config = NULL,
261 .name = "lsm303dlx_m",
262 .type = EXT_SLAVE_TYPE_COMPASS,
263 .id = ID_INVALID,
264 .read_reg = 0x06,
265 .read_len = 6,
266 .endian = EXT_SLAVE_BIG_ENDIAN,
267 .range = {10240, 0},
268 .trigger = NULL,
269};
270
271static
272struct ext_slave_descr *lsm303dlx_m_get_slave_descr(void)
273{
274 return &lsm303dlx_m_descr;
275}
276
277/* -------------------------------------------------------------------------- */
278struct lsm303dlx_m_mod_private_data {
279 struct i2c_client *client;
280 struct ext_slave_platform_data *pdata;
281};
282
283static const struct i2c_device_id lsm303dlx_m_mod_id[] = {
284 { "lsm303dlh", COMPASS_ID_LSM303DLH },
285 { "lsm303dlm", COMPASS_ID_LSM303DLM },
286 {}
287};
288MODULE_DEVICE_TABLE(i2c, lsm303dlx_m_mod_id);
289
290static unsigned short normal_i2c[] = { I2C_CLIENT_END };
291
292static int lsm303dlx_m_mod_probe(struct i2c_client *client,
293 const struct i2c_device_id *devid)
294{
295 struct ext_slave_platform_data *pdata;
296 struct lsm303dlx_m_mod_private_data *private_data;
297 int result = 0;
298
299 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
300 lsm303dlx_m_descr.id = devid->driver_data;
301
302 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
303 result = -ENODEV;
304 goto out_no_free;
305 }
306
307 pdata = client->dev.platform_data;
308 if (!pdata) {
309 dev_err(&client->adapter->dev,
310 "Missing platform data for slave %s\n", devid->name);
311 result = -EFAULT;
312 goto out_no_free;
313 }
314
315 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
316 if (!private_data) {
317 result = -ENOMEM;
318 goto out_no_free;
319 }
320
321 i2c_set_clientdata(client, private_data);
322 private_data->client = client;
323 private_data->pdata = pdata;
324
325 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
326 lsm303dlx_m_get_slave_descr);
327 if (result) {
328 dev_err(&client->adapter->dev,
329 "Slave registration failed: %s, %d\n",
330 devid->name, result);
331 goto out_free_memory;
332 }
333
334 return result;
335
336out_free_memory:
337 kfree(private_data);
338out_no_free:
339 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
340 return result;
341
342}
343
344static int lsm303dlx_m_mod_remove(struct i2c_client *client)
345{
346 struct lsm303dlx_m_mod_private_data *private_data =
347 i2c_get_clientdata(client);
348
349 dev_dbg(&client->adapter->dev, "%s\n", __func__);
350
351 inv_mpu_unregister_slave(client, private_data->pdata,
352 lsm303dlx_m_get_slave_descr);
353
354 kfree(private_data);
355 return 0;
356}
357
358static struct i2c_driver lsm303dlx_m_mod_driver = {
359 .class = I2C_CLASS_HWMON,
360 .probe = lsm303dlx_m_mod_probe,
361 .remove = lsm303dlx_m_mod_remove,
362 .id_table = lsm303dlx_m_mod_id,
363 .driver = {
364 .owner = THIS_MODULE,
365 .name = "lsm303dlx_m_mod",
366 },
367 .address_list = normal_i2c,
368};
369
370static int __init lsm303dlx_m_mod_init(void)
371{
372 int res = i2c_add_driver(&lsm303dlx_m_mod_driver);
373 pr_info("%s: Probe name %s\n", __func__, "lsm303dlx_m_mod");
374 if (res)
375 pr_err("%s failed\n", __func__);
376 return res;
377}
378
379static void __exit lsm303dlx_m_mod_exit(void)
380{
381 pr_info("%s\n", __func__);
382 i2c_del_driver(&lsm303dlx_m_mod_driver);
383}
384
385module_init(lsm303dlx_m_mod_init);
386module_exit(lsm303dlx_m_mod_exit);
387
388MODULE_AUTHOR("Invensense Corporation");
389MODULE_DESCRIPTION("Driver to integrate lsm303dlx_m sensor with the MPU");
390MODULE_LICENSE("GPL");
391MODULE_ALIAS("lsm303dlx_m_mod");
392
393/**
394 * @}
395 */
diff --git a/drivers/misc/inv_mpu/compass/mmc314x.c b/drivers/misc/inv_mpu/compass/mmc314x.c
new file mode 100644
index 00000000000..786fadcc3e4
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/mmc314x.c
@@ -0,0 +1,313 @@
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 COMPASSDL
22 *
23 * @{
24 * @file mmc314x.c
25 * @brief Magnetometer setup and handling methods for the
26 * MEMSIC MMC314x compass.
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-compass"
46
47/* -------------------------------------------------------------------------- */
48
49static int reset_int = 1000;
50static int read_count = 1;
51static char reset_mode; /* in Z-init section */
52
53/* -------------------------------------------------------------------------- */
54#define MMC314X_REG_ST (0x00)
55#define MMC314X_REG_X_MSB (0x01)
56
57#define MMC314X_CNTL_MODE_WAKE_UP (0x01)
58#define MMC314X_CNTL_MODE_SET (0x02)
59#define MMC314X_CNTL_MODE_RESET (0x04)
60
61/* -------------------------------------------------------------------------- */
62
63static int mmc314x_suspend(void *mlsl_handle,
64 struct ext_slave_descr *slave,
65 struct ext_slave_platform_data *pdata)
66{
67 int result = INV_SUCCESS;
68
69 return result;
70}
71
72static int mmc314x_resume(void *mlsl_handle,
73 struct ext_slave_descr *slave,
74 struct ext_slave_platform_data *pdata)
75{
76
77 int result;
78 result =
79 inv_serial_single_write(mlsl_handle, pdata->address,
80 MMC314X_REG_ST, MMC314X_CNTL_MODE_RESET);
81 if (result) {
82 LOG_RESULT_LOCATION(result);
83 return result;
84 }
85 msleep(10);
86 result =
87 inv_serial_single_write(mlsl_handle, pdata->address,
88 MMC314X_REG_ST, MMC314X_CNTL_MODE_SET);
89 if (result) {
90 LOG_RESULT_LOCATION(result);
91 return result;
92 }
93 msleep(10);
94 read_count = 1;
95 return INV_SUCCESS;
96}
97
98static int mmc314x_read(void *mlsl_handle,
99 struct ext_slave_descr *slave,
100 struct ext_slave_platform_data *pdata,
101 unsigned char *data)
102{
103 int result, ii;
104 short tmp[3];
105 unsigned char tmpdata[6];
106
107 if (read_count > 1000)
108 read_count = 1;
109
110 result =
111 inv_serial_read(mlsl_handle, pdata->address, MMC314X_REG_X_MSB,
112 6, (unsigned char *)data);
113 if (result) {
114 LOG_RESULT_LOCATION(result);
115 return result;
116 }
117
118 for (ii = 0; ii < 6; ii++)
119 tmpdata[ii] = data[ii];
120
121 for (ii = 0; ii < 3; ii++) {
122 tmp[ii] = (short)((tmpdata[2 * ii] << 8) + tmpdata[2 * ii + 1]);
123 tmp[ii] = tmp[ii] - 4096;
124 tmp[ii] = tmp[ii] * 16;
125 }
126
127 for (ii = 0; ii < 3; ii++) {
128 data[2 * ii] = (unsigned char)(tmp[ii] >> 8);
129 data[2 * ii + 1] = (unsigned char)(tmp[ii]);
130 }
131
132 if (read_count % reset_int == 0) {
133 if (reset_mode) {
134 result =
135 inv_serial_single_write(mlsl_handle,
136 pdata->address,
137 MMC314X_REG_ST,
138 MMC314X_CNTL_MODE_RESET);
139 if (result) {
140 LOG_RESULT_LOCATION(result);
141 return result;
142 }
143 reset_mode = 0;
144 return INV_ERROR_COMPASS_DATA_NOT_READY;
145 } else {
146 result =
147 inv_serial_single_write(mlsl_handle,
148 pdata->address,
149 MMC314X_REG_ST,
150 MMC314X_CNTL_MODE_SET);
151 if (result) {
152 LOG_RESULT_LOCATION(result);
153 return result;
154 }
155 reset_mode = 1;
156 read_count++;
157 return INV_ERROR_COMPASS_DATA_NOT_READY;
158 }
159 }
160 result =
161 inv_serial_single_write(mlsl_handle, pdata->address,
162 MMC314X_REG_ST, MMC314X_CNTL_MODE_WAKE_UP);
163 if (result) {
164 LOG_RESULT_LOCATION(result);
165 return result;
166 }
167 read_count++;
168
169 return INV_SUCCESS;
170}
171
172static struct ext_slave_descr mmc314x_descr = {
173 .init = NULL,
174 .exit = NULL,
175 .suspend = mmc314x_suspend,
176 .resume = mmc314x_resume,
177 .read = mmc314x_read,
178 .config = NULL,
179 .get_config = NULL,
180 .name = "mmc314x",
181 .type = EXT_SLAVE_TYPE_COMPASS,
182 .id = COMPASS_ID_MMC314X,
183 .read_reg = 0x01,
184 .read_len = 6,
185 .endian = EXT_SLAVE_BIG_ENDIAN,
186 .range = {400, 0},
187 .trigger = NULL,
188};
189
190static
191struct ext_slave_descr *mmc314x_get_slave_descr(void)
192{
193 return &mmc314x_descr;
194}
195
196/* -------------------------------------------------------------------------- */
197struct mmc314x_mod_private_data {
198 struct i2c_client *client;
199 struct ext_slave_platform_data *pdata;
200};
201
202static unsigned short normal_i2c[] = { I2C_CLIENT_END };
203
204static int mmc314x_mod_probe(struct i2c_client *client,
205 const struct i2c_device_id *devid)
206{
207 struct ext_slave_platform_data *pdata;
208 struct mmc314x_mod_private_data *private_data;
209 int result = 0;
210
211 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
212
213 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
214 result = -ENODEV;
215 goto out_no_free;
216 }
217
218 pdata = client->dev.platform_data;
219 if (!pdata) {
220 dev_err(&client->adapter->dev,
221 "Missing platform data for slave %s\n", devid->name);
222 result = -EFAULT;
223 goto out_no_free;
224 }
225
226 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
227 if (!private_data) {
228 result = -ENOMEM;
229 goto out_no_free;
230 }
231
232 i2c_set_clientdata(client, private_data);
233 private_data->client = client;
234 private_data->pdata = pdata;
235
236 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
237 mmc314x_get_slave_descr);
238 if (result) {
239 dev_err(&client->adapter->dev,
240 "Slave registration failed: %s, %d\n",
241 devid->name, result);
242 goto out_free_memory;
243 }
244
245 return result;
246
247out_free_memory:
248 kfree(private_data);
249out_no_free:
250 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
251 return result;
252
253}
254
255static int mmc314x_mod_remove(struct i2c_client *client)
256{
257 struct mmc314x_mod_private_data *private_data =
258 i2c_get_clientdata(client);
259
260 dev_dbg(&client->adapter->dev, "%s\n", __func__);
261
262 inv_mpu_unregister_slave(client, private_data->pdata,
263 mmc314x_get_slave_descr);
264
265 kfree(private_data);
266 return 0;
267}
268
269static const struct i2c_device_id mmc314x_mod_id[] = {
270 { "mmc314x", COMPASS_ID_MMC314X },
271 {}
272};
273
274MODULE_DEVICE_TABLE(i2c, mmc314x_mod_id);
275
276static struct i2c_driver mmc314x_mod_driver = {
277 .class = I2C_CLASS_HWMON,
278 .probe = mmc314x_mod_probe,
279 .remove = mmc314x_mod_remove,
280 .id_table = mmc314x_mod_id,
281 .driver = {
282 .owner = THIS_MODULE,
283 .name = "mmc314x_mod",
284 },
285 .address_list = normal_i2c,
286};
287
288static int __init mmc314x_mod_init(void)
289{
290 int res = i2c_add_driver(&mmc314x_mod_driver);
291 pr_info("%s: Probe name %s\n", __func__, "mmc314x_mod");
292 if (res)
293 pr_err("%s failed\n", __func__);
294 return res;
295}
296
297static void __exit mmc314x_mod_exit(void)
298{
299 pr_info("%s\n", __func__);
300 i2c_del_driver(&mmc314x_mod_driver);
301}
302
303module_init(mmc314x_mod_init);
304module_exit(mmc314x_mod_exit);
305
306MODULE_AUTHOR("Invensense Corporation");
307MODULE_DESCRIPTION("Driver to integrate MMC314X sensor with the MPU");
308MODULE_LICENSE("GPL");
309MODULE_ALIAS("mmc314x_mod");
310
311/**
312 * @}
313 */
diff --git a/drivers/misc/inv_mpu/compass/yas529-kernel.c b/drivers/misc/inv_mpu/compass/yas529-kernel.c
new file mode 100644
index 00000000000..f53223fba64
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/yas529-kernel.c
@@ -0,0 +1,611 @@
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
22#include <linux/i2c.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/kernel.h>
26#include <linux/errno.h>
27#include <linux/slab.h>
28#include <linux/delay.h>
29#include "mpu-dev.h"
30
31#include <log.h>
32#include <linux/mpu.h>
33#include "mlsl.h"
34#include "mldl_cfg.h"
35#undef MPL_LOG_TAG
36#define MPL_LOG_TAG "MPL-acc"
37
38/*----- YAMAHA YAS529 Registers ------*/
39enum YAS_REG {
40 YAS_REG_CMDR = 0x00, /* 000 < 5 */
41 YAS_REG_XOFFSETR = 0x20, /* 001 < 5 */
42 YAS_REG_Y1OFFSETR = 0x40, /* 010 < 5 */
43 YAS_REG_Y2OFFSETR = 0x60, /* 011 < 5 */
44 YAS_REG_ICOILR = 0x80, /* 100 < 5 */
45 YAS_REG_CAL = 0xA0, /* 101 < 5 */
46 YAS_REG_CONFR = 0xC0, /* 110 < 5 */
47 YAS_REG_DOUTR = 0xE0 /* 111 < 5 */
48};
49
50/* -------------------------------------------------------------------------- */
51
52static long a1;
53static long a2;
54static long a3;
55static long a4;
56static long a5;
57static long a6;
58static long a7;
59static long a8;
60static long a9;
61
62/* -------------------------------------------------------------------------- */
63static int yas529_sensor_i2c_write(struct i2c_adapter *i2c_adap,
64 unsigned char address,
65 unsigned int len, unsigned char *data)
66{
67 struct i2c_msg msgs[1];
68 int res;
69
70 if (NULL == data || NULL == i2c_adap)
71 return -EINVAL;
72
73 msgs[0].addr = address;
74 msgs[0].flags = 0; /* write */
75 msgs[0].buf = (unsigned char *)data;
76 msgs[0].len = len;
77
78 res = i2c_transfer(i2c_adap, msgs, 1);
79 if (res < 1)
80 return res;
81 else
82 return 0;
83}
84
85static int yas529_sensor_i2c_read(struct i2c_adapter *i2c_adap,
86 unsigned char address,
87 unsigned char reg,
88 unsigned int len, unsigned char *data)
89{
90 struct i2c_msg msgs[2];
91 int res;
92
93 if (NULL == data || NULL == i2c_adap)
94 return -EINVAL;
95
96 msgs[0].addr = address;
97 msgs[0].flags = I2C_M_RD;
98 msgs[0].buf = data;
99 msgs[0].len = len;
100
101 res = i2c_transfer(i2c_adap, msgs, 1);
102 if (res < 1)
103 return res;
104 else
105 return 0;
106}
107
108static int yas529_suspend(void *mlsl_handle,
109 struct ext_slave_descr *slave,
110 struct ext_slave_platform_data *pdata)
111{
112 int result = INV_SUCCESS;
113
114 return result;
115}
116
117static int yas529_resume(void *mlsl_handle,
118 struct ext_slave_descr *slave,
119 struct ext_slave_platform_data *pdata)
120{
121 int result = INV_SUCCESS;
122
123 unsigned char dummyData[1] = { 0 };
124 unsigned char dummyRegister = 0;
125 unsigned char rawData[6];
126 unsigned char calData[9];
127
128 short xoffset, y1offset, y2offset;
129 short d2, d3, d4, d5, d6, d7, d8, d9;
130
131 /* YAS529 Application Manual MS-3C - Section 4.4.5 */
132 /* =============================================== */
133 /* Step 1 - register initialization */
134 /* zero initialization coil register - "100 00 000" */
135 dummyData[0] = YAS_REG_ICOILR | 0x00;
136 result =
137 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
138 if (result) {
139 LOG_RESULT_LOCATION(result);
140 return result;
141 }
142 /* zero config register - "110 00 000" */
143 dummyData[0] = YAS_REG_CONFR | 0x00;
144 result =
145 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
146 if (result) {
147 LOG_RESULT_LOCATION(result);
148 return result;
149 }
150
151 /* Step 2 - initialization coil operation */
152 dummyData[0] = YAS_REG_ICOILR | 0x11;
153 result =
154 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
155 if (result) {
156 LOG_RESULT_LOCATION(result);
157 return result;
158 }
159 dummyData[0] = YAS_REG_ICOILR | 0x01;
160 result =
161 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
162 if (result) {
163 LOG_RESULT_LOCATION(result);
164 return result;
165 }
166 dummyData[0] = YAS_REG_ICOILR | 0x12;
167 result =
168 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
169 if (result) {
170 LOG_RESULT_LOCATION(result);
171 return result;
172 }
173 dummyData[0] = YAS_REG_ICOILR | 0x02;
174 result =
175 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
176 if (result) {
177 LOG_RESULT_LOCATION(result);
178 return result;
179 }
180 dummyData[0] = YAS_REG_ICOILR | 0x13;
181 result =
182 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
183 if (result) {
184 LOG_RESULT_LOCATION(result);
185 return result;
186 }
187 dummyData[0] = YAS_REG_ICOILR | 0x03;
188 result =
189 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
190 if (result) {
191 LOG_RESULT_LOCATION(result);
192 return result;
193 }
194 dummyData[0] = YAS_REG_ICOILR | 0x14;
195 result =
196 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
197 if (result) {
198 LOG_RESULT_LOCATION(result);
199 return result;
200 }
201 dummyData[0] = YAS_REG_ICOILR | 0x04;
202 result =
203 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
204 if (result) {
205 LOG_RESULT_LOCATION(result);
206 return result;
207 }
208 dummyData[0] = YAS_REG_ICOILR | 0x15;
209 result =
210 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
211 if (result) {
212 LOG_RESULT_LOCATION(result);
213 return result;
214 }
215 dummyData[0] = YAS_REG_ICOILR | 0x05;
216 result =
217 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
218 if (result) {
219 LOG_RESULT_LOCATION(result);
220 return result;
221 }
222 dummyData[0] = YAS_REG_ICOILR | 0x16;
223 result =
224 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
225 if (result) {
226 LOG_RESULT_LOCATION(result);
227 return result;
228 }
229 dummyData[0] = YAS_REG_ICOILR | 0x06;
230 result =
231 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
232 if (result) {
233 LOG_RESULT_LOCATION(result);
234 return result;
235 }
236 dummyData[0] = YAS_REG_ICOILR | 0x17;
237 result =
238 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
239 if (result) {
240 LOG_RESULT_LOCATION(result);
241 return result;
242 }
243 dummyData[0] = YAS_REG_ICOILR | 0x07;
244 result =
245 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
246 if (result) {
247 LOG_RESULT_LOCATION(result);
248 return result;
249 }
250 dummyData[0] = YAS_REG_ICOILR | 0x10;
251 result =
252 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
253 if (result) {
254 LOG_RESULT_LOCATION(result);
255 return result;
256 }
257 dummyData[0] = YAS_REG_ICOILR | 0x00;
258 result =
259 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
260 if (result) {
261 LOG_RESULT_LOCATION(result);
262 return result;
263 }
264
265 /* Step 3 - rough offset measurement */
266 /* Config register - Measurements results - "110 00 000" */
267 dummyData[0] = YAS_REG_CONFR | 0x00;
268 result =
269 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
270 if (result) {
271 LOG_RESULT_LOCATION(result);
272 return result;
273 }
274 /* Measurements command register - Rough offset measurement -
275 "000 00001" */
276 dummyData[0] = YAS_REG_CMDR | 0x01;
277 result =
278 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
279 if (result) {
280 LOG_RESULT_LOCATION(result);
281 return result;
282 }
283 msleep(2); /* wait at least 1.5ms */
284
285 /* Measurement data read */
286 result =
287 yas529_sensor_i2c_read(mlsl_handle, pdata->address,
288 dummyRegister, 6, rawData);
289 if (result) {
290 LOG_RESULT_LOCATION(result);
291 return result;
292 }
293 xoffset =
294 (short)((unsigned short)rawData[5] +
295 ((unsigned short)rawData[4] & 0x7) * 256) - 5;
296 if (xoffset < 0)
297 xoffset = 0;
298 y1offset =
299 (short)((unsigned short)rawData[3] +
300 ((unsigned short)rawData[2] & 0x7) * 256) - 5;
301 if (y1offset < 0)
302 y1offset = 0;
303 y2offset =
304 (short)((unsigned short)rawData[1] +
305 ((unsigned short)rawData[0] & 0x7) * 256) - 5;
306 if (y2offset < 0)
307 y2offset = 0;
308
309 /* Step 4 - rough offset setting */
310 /* Set rough offset register values */
311 dummyData[0] = YAS_REG_XOFFSETR | xoffset;
312 result =
313 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
314 if (result) {
315 LOG_RESULT_LOCATION(result);
316 return result;
317 }
318 dummyData[0] = YAS_REG_Y1OFFSETR | y1offset;
319 result =
320 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
321 if (result) {
322 LOG_RESULT_LOCATION(result);
323 return result;
324 }
325 dummyData[0] = YAS_REG_Y2OFFSETR | y2offset;
326 result =
327 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
328 if (result) {
329 LOG_RESULT_LOCATION(result);
330 return result;
331 }
332
333 /* CAL matrix read (first read is invalid) */
334 /* Config register - CAL register read - "110 01 000" */
335 dummyData[0] = YAS_REG_CONFR | 0x08;
336 result =
337 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
338 if (result) {
339 LOG_RESULT_LOCATION(result);
340 return result;
341 }
342 /* CAL data read */
343 result =
344 yas529_sensor_i2c_read(mlsl_handle, pdata->address,
345 dummyRegister, 9, calData);
346 if (result) {
347 LOG_RESULT_LOCATION(result);
348 return result;
349 }
350 /* Config register - CAL register read - "110 01 000" */
351 dummyData[0] = YAS_REG_CONFR | 0x08;
352 result =
353 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
354 if (result) {
355 LOG_RESULT_LOCATION(result);
356 return result;
357 }
358 /* CAL data read */
359 result =
360 yas529_sensor_i2c_read(mlsl_handle, pdata->address,
361 dummyRegister, 9, calData);
362 if (result) {
363 LOG_RESULT_LOCATION(result);
364 return result;
365 }
366
367 /* Calculate coefficients of the sensitivity correction matrix */
368 a1 = 100;
369 d2 = (calData[0] & 0xFC) >> 2; /* [71..66] 6bit */
370 a2 = (short)(d2 - 32);
371 /* [65..62] 4bit */
372 d3 = ((calData[0] & 0x03) << 2) | ((calData[1] & 0xC0) >> 6);
373 a3 = (short)(d3 - 8);
374 d4 = (calData[1] & 0x3F); /* [61..56] 6bit */
375 a4 = (short)(d4 - 32);
376 d5 = (calData[2] & 0xFC) >> 2; /* [55..50] 6bit */
377 a5 = (short)(d5 - 32) + 70;
378 /* [49..44] 6bit */
379 d6 = ((calData[2] & 0x03) << 4) | ((calData[3] & 0xF0) >> 4);
380 a6 = (short)(d6 - 32);
381 /* [43..38] 6bit */
382 d7 = ((calData[3] & 0x0F) << 2) | ((calData[4] & 0xC0) >> 6);
383 a7 = (short)(d7 - 32);
384 d8 = (calData[4] & 0x3F); /* [37..32] 6bit */
385 a8 = (short)(d8 - 32);
386 d9 = (calData[5] & 0xFE) >> 1; /* [31..25] 7bit */
387 a9 = (short)(d9 - 64) + 130;
388
389 return result;
390}
391
392static int yas529_read(void *mlsl_handle,
393 struct ext_slave_descr *slave,
394 struct ext_slave_platform_data *pdata,
395 unsigned char *data)
396{
397 unsigned char stat;
398 unsigned char rawData[6];
399 unsigned char dummyData[1] = { 0 };
400 unsigned char dummyRegister = 0;
401 int result = INV_SUCCESS;
402 short SX, SY1, SY2, SY, SZ;
403 short row1fixed, row2fixed, row3fixed;
404
405 /* Config register - Measurements results - "110 00 000" */
406 dummyData[0] = YAS_REG_CONFR | 0x00;
407 result =
408 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
409 if (result) {
410 LOG_RESULT_LOCATION(result);
411 return result;
412 }
413 /* Measurements command register - Normal magnetic field measurement -
414 "000 00000" */
415 dummyData[0] = YAS_REG_CMDR | 0x00;
416 result =
417 yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
418 if (result) {
419 LOG_RESULT_LOCATION(result);
420 return result;
421 }
422 msleep(10);
423 /* Measurement data read */
424 result =
425 yas529_sensor_i2c_read(mlsl_handle, pdata->address,
426 dummyRegister, 6, (unsigned char *)&rawData);
427 if (result) {
428 LOG_RESULT_LOCATION(result);
429 return result;
430 }
431
432 stat = rawData[0] & 0x80;
433 if (stat == 0x00) {
434 /* Extract raw data */
435 SX = (short)((unsigned short)rawData[5] +
436 ((unsigned short)rawData[4] & 0x7) * 256);
437 SY1 =
438 (short)((unsigned short)rawData[3] +
439 ((unsigned short)rawData[2] & 0x7) * 256);
440 SY2 =
441 (short)((unsigned short)rawData[1] +
442 ((unsigned short)rawData[0] & 0x7) * 256);
443 if ((SX <= 1) || (SY1 <= 1) || (SY2 <= 1))
444 return INV_ERROR_COMPASS_DATA_UNDERFLOW;
445 if ((SX >= 1024) || (SY1 >= 1024) || (SY2 >= 1024))
446 return INV_ERROR_COMPASS_DATA_OVERFLOW;
447 /* Convert to XYZ axis */
448 SX = -1 * SX;
449 SY = SY2 - SY1;
450 SZ = SY1 + SY2;
451
452 /* Apply sensitivity correction matrix */
453 row1fixed = (short)((a1 * SX + a2 * SY + a3 * SZ) >> 7) * 41;
454 row2fixed = (short)((a4 * SX + a5 * SY + a6 * SZ) >> 7) * 41;
455 row3fixed = (short)((a7 * SX + a8 * SY + a9 * SZ) >> 7) * 41;
456
457 data[0] = row1fixed >> 8;
458 data[1] = row1fixed & 0xFF;
459 data[2] = row2fixed >> 8;
460 data[3] = row2fixed & 0xFF;
461 data[4] = row3fixed >> 8;
462 data[5] = row3fixed & 0xFF;
463
464 return INV_SUCCESS;
465 } else {
466 return INV_ERROR_COMPASS_DATA_NOT_READY;
467 }
468}
469
470static struct ext_slave_descr yas529_descr = {
471 .init = NULL,
472 .exit = NULL,
473 .suspend = yas529_suspend,
474 .resume = yas529_resume,
475 .read = yas529_read,
476 .config = NULL,
477 .get_config = NULL,
478 .name = "yas529",
479 .type = EXT_SLAVE_TYPE_COMPASS,
480 .id = COMPASS_ID_YAS529,
481 .read_reg = 0x06,
482 .read_len = 6,
483 .endian = EXT_SLAVE_BIG_ENDIAN,
484 .range = {19660, 8000},
485 .trigger = NULL,
486};
487
488static
489struct ext_slave_descr *yas529_get_slave_descr(void)
490{
491 return &yas529_descr;
492}
493
494/* -------------------------------------------------------------------------- */
495struct yas529_mod_private_data {
496 struct i2c_client *client;
497 struct ext_slave_platform_data *pdata;
498};
499
500static unsigned short normal_i2c[] = { I2C_CLIENT_END };
501
502static int yas529_mod_probe(struct i2c_client *client,
503 const struct i2c_device_id *devid)
504{
505 struct ext_slave_platform_data *pdata;
506 struct yas529_mod_private_data *private_data;
507 int result = 0;
508
509 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
510
511 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
512 result = -ENODEV;
513 goto out_no_free;
514 }
515
516 pdata = client->dev.platform_data;
517 if (!pdata) {
518 dev_err(&client->adapter->dev,
519 "Missing platform data for slave %s\n", devid->name);
520 result = -EFAULT;
521 goto out_no_free;
522 }
523
524 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
525 if (!private_data) {
526 result = -ENOMEM;
527 goto out_no_free;
528 }
529
530 i2c_set_clientdata(client, private_data);
531 private_data->client = client;
532 private_data->pdata = pdata;
533
534 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
535 yas529_get_slave_descr);
536 if (result) {
537 dev_err(&client->adapter->dev,
538 "Slave registration failed: %s, %d\n",
539 devid->name, result);
540 goto out_free_memory;
541 }
542
543 return result;
544
545out_free_memory:
546 kfree(private_data);
547out_no_free:
548 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
549 return result;
550
551}
552
553static int yas529_mod_remove(struct i2c_client *client)
554{
555 struct yas529_mod_private_data *private_data =
556 i2c_get_clientdata(client);
557
558 dev_dbg(&client->adapter->dev, "%s\n", __func__);
559
560 inv_mpu_unregister_slave(client, private_data->pdata,
561 yas529_get_slave_descr);
562
563 kfree(private_data);
564 return 0;
565}
566
567static const struct i2c_device_id yas529_mod_id[] = {
568 { "yas529", COMPASS_ID_YAS529 },
569 {}
570};
571
572MODULE_DEVICE_TABLE(i2c, yas529_mod_id);
573
574static struct i2c_driver yas529_mod_driver = {
575 .class = I2C_CLASS_HWMON,
576 .probe = yas529_mod_probe,
577 .remove = yas529_mod_remove,
578 .id_table = yas529_mod_id,
579 .driver = {
580 .owner = THIS_MODULE,
581 .name = "yas529_mod",
582 },
583 .address_list = normal_i2c,
584};
585
586static int __init yas529_mod_init(void)
587{
588 int res = i2c_add_driver(&yas529_mod_driver);
589 pr_info("%s: Probe name %s\n", __func__, "yas529_mod");
590 if (res)
591 pr_err("%s failed\n", __func__);
592 return res;
593}
594
595static void __exit yas529_mod_exit(void)
596{
597 pr_info("%s\n", __func__);
598 i2c_del_driver(&yas529_mod_driver);
599}
600
601module_init(yas529_mod_init);
602module_exit(yas529_mod_exit);
603
604MODULE_AUTHOR("Invensense Corporation");
605MODULE_DESCRIPTION("Driver to integrate YAS529 sensor with the MPU");
606MODULE_LICENSE("GPL");
607MODULE_ALIAS("yas529_mod");
608
609/**
610 * @}
611 */
diff --git a/drivers/misc/inv_mpu/compass/yas530.c b/drivers/misc/inv_mpu/compass/yas530.c
new file mode 100644
index 00000000000..fdca05ba8e5
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/yas530.c
@@ -0,0 +1,580 @@
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 COMPASSDL
22 *
23 * @{
24 * @file yas530.c
25 * @brief Magnetometer setup and handling methods for Yamaha YAS530
26 * compass when used in a user-space solution (no kernel driver).
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
38#include <linux/module.h>
39#include <linux/delay.h>
40#include "mpu-dev.h"
41
42#include "log.h"
43#include <linux/mpu.h>
44#include "mlsl.h"
45#include "mldl_cfg.h"
46#undef MPL_LOG_TAG
47#define MPL_LOG_TAG "MPL-compass"
48
49/* -------------------------------------------------------------------------- */
50#define YAS530_REGADDR_DEVICE_ID (0x80)
51#define YAS530_REGADDR_ACTUATE_INIT_COIL (0x81)
52#define YAS530_REGADDR_MEASURE_COMMAND (0x82)
53#define YAS530_REGADDR_CONFIG (0x83)
54#define YAS530_REGADDR_MEASURE_INTERVAL (0x84)
55#define YAS530_REGADDR_OFFSET_X (0x85)
56#define YAS530_REGADDR_OFFSET_Y1 (0x86)
57#define YAS530_REGADDR_OFFSET_Y2 (0x87)
58#define YAS530_REGADDR_TEST1 (0x88)
59#define YAS530_REGADDR_TEST2 (0x89)
60#define YAS530_REGADDR_CAL (0x90)
61#define YAS530_REGADDR_MEASURE_DATA (0xb0)
62
63/* -------------------------------------------------------------------------- */
64static int Cx, Cy1, Cy2;
65static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9;
66static int k;
67
68static unsigned char dx, dy1, dy2;
69static unsigned char d2, d3, d4, d5, d6, d7, d8, d9, d0;
70static unsigned char dck;
71
72/* -------------------------------------------------------------------------- */
73
74static int set_hardware_offset(void *mlsl_handle,
75 struct ext_slave_descr *slave,
76 struct ext_slave_platform_data *pdata,
77 char offset_x, char offset_y1, char offset_y2)
78{
79 char data;
80 int result = INV_SUCCESS;
81
82 data = offset_x & 0x3f;
83 result = inv_serial_single_write(mlsl_handle, pdata->address,
84 YAS530_REGADDR_OFFSET_X, data);
85 if (result) {
86 LOG_RESULT_LOCATION(result);
87 return result;
88 }
89
90 data = offset_y1 & 0x3f;
91 result = inv_serial_single_write(mlsl_handle, pdata->address,
92 YAS530_REGADDR_OFFSET_Y1, data);
93 if (result) {
94 LOG_RESULT_LOCATION(result);
95 return result;
96 }
97
98 data = offset_y2 & 0x3f;
99 result = inv_serial_single_write(mlsl_handle, pdata->address,
100 YAS530_REGADDR_OFFSET_Y2, data);
101 if (result) {
102 LOG_RESULT_LOCATION(result);
103 return result;
104 }
105
106 return result;
107}
108
109static int set_measure_command(void *mlsl_handle,
110 struct ext_slave_descr *slave,
111 struct ext_slave_platform_data *pdata,
112 int ldtc, int fors, int dlymes)
113{
114 int result = INV_SUCCESS;
115
116 result = inv_serial_single_write(mlsl_handle, pdata->address,
117 YAS530_REGADDR_MEASURE_COMMAND, 0x01);
118 if (result) {
119 LOG_RESULT_LOCATION(result);
120 return result;
121 }
122
123 return result;
124}
125
126static int measure_normal(void *mlsl_handle,
127 struct ext_slave_descr *slave,
128 struct ext_slave_platform_data *pdata,
129 int *busy, unsigned short *t,
130 unsigned short *x, unsigned short *y1,
131 unsigned short *y2)
132{
133 unsigned char data[8];
134 unsigned short b, to, xo, y1o, y2o;
135 int result;
136 ktime_t sleeptime;
137 result = set_measure_command(mlsl_handle, slave, pdata, 0, 0, 0);
138 sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC);
139 set_current_state(TASK_UNINTERRUPTIBLE);
140 schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL);
141
142 result = inv_serial_read(mlsl_handle, pdata->address,
143 YAS530_REGADDR_MEASURE_DATA, 8, data);
144 if (result) {
145 LOG_RESULT_LOCATION(result);
146 return result;
147 }
148
149 b = (data[0] >> 7) & 0x01;
150 to = ((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03);
151 xo = ((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f);
152 y1o = ((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f);
153 y2o = ((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f);
154
155 *busy = b;
156 *t = to;
157 *x = xo;
158 *y1 = y1o;
159 *y2 = y2o;
160
161 return result;
162}
163
164static int check_offset(void *mlsl_handle,
165 struct ext_slave_descr *slave,
166 struct ext_slave_platform_data *pdata,
167 char offset_x, char offset_y1, char offset_y2,
168 int *flag_x, int *flag_y1, int *flag_y2)
169{
170 int result;
171 int busy;
172 short t, x, y1, y2;
173
174 result = set_hardware_offset(mlsl_handle, slave, pdata,
175 offset_x, offset_y1, offset_y2);
176 if (result) {
177 LOG_RESULT_LOCATION(result);
178 return result;
179 }
180
181 result = measure_normal(mlsl_handle, slave, pdata,
182 &busy, &t, &x, &y1, &y2);
183 if (result) {
184 LOG_RESULT_LOCATION(result);
185 return result;
186 }
187
188 *flag_x = 0;
189 *flag_y1 = 0;
190 *flag_y2 = 0;
191
192 if (x > 2048)
193 *flag_x = 1;
194 if (y1 > 2048)
195 *flag_y1 = 1;
196 if (y2 > 2048)
197 *flag_y2 = 1;
198 if (x < 2048)
199 *flag_x = -1;
200 if (y1 < 2048)
201 *flag_y1 = -1;
202 if (y2 < 2048)
203 *flag_y2 = -1;
204
205 return result;
206}
207
208static int measure_and_set_offset(void *mlsl_handle,
209 struct ext_slave_descr *slave,
210 struct ext_slave_platform_data *pdata,
211 char *offset)
212{
213 int i;
214 int result = INV_SUCCESS;
215 char offset_x = 0, offset_y1 = 0, offset_y2 = 0;
216 int flag_x = 0, flag_y1 = 0, flag_y2 = 0;
217 static const int correct[5] = { 16, 8, 4, 2, 1 };
218
219 for (i = 0; i < 5; i++) {
220 result = check_offset(mlsl_handle, slave, pdata,
221 offset_x, offset_y1, offset_y2,
222 &flag_x, &flag_y1, &flag_y2);
223 if (result) {
224 LOG_RESULT_LOCATION(result);
225 return result;
226 }
227
228 if (flag_x)
229 offset_x += flag_x * correct[i];
230 if (flag_y1)
231 offset_y1 += flag_y1 * correct[i];
232 if (flag_y2)
233 offset_y2 += flag_y2 * correct[i];
234 }
235
236 result = set_hardware_offset(mlsl_handle, slave, pdata,
237 offset_x, offset_y1, offset_y2);
238 if (result) {
239 LOG_RESULT_LOCATION(result);
240 return result;
241 }
242
243 offset[0] = offset_x;
244 offset[1] = offset_y1;
245 offset[2] = offset_y2;
246
247 return result;
248}
249
250static void coordinate_conversion(short x, short y1, short y2, short t,
251 int32_t *xo, int32_t *yo, int32_t *zo)
252{
253 int32_t sx, sy1, sy2, sy, sz;
254 int32_t hx, hy, hz;
255
256 sx = x - (Cx * t) / 100;
257 sy1 = y1 - (Cy1 * t) / 100;
258 sy2 = y2 - (Cy2 * t) / 100;
259
260 sy = sy1 - sy2;
261 sz = -sy1 - sy2;
262
263 hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10);
264 hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10);
265 hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10);
266
267 *xo = hx;
268 *yo = hy;
269 *zo = hz;
270}
271
272static int yas530_suspend(void *mlsl_handle,
273 struct ext_slave_descr *slave,
274 struct ext_slave_platform_data *pdata)
275{
276 int result = INV_SUCCESS;
277
278 return result;
279}
280
281static int yas530_resume(void *mlsl_handle,
282 struct ext_slave_descr *slave,
283 struct ext_slave_platform_data *pdata)
284{
285 int result = INV_SUCCESS;
286
287 unsigned char dummyData = 0x00;
288 char offset[3] = { 0, 0, 0 };
289 unsigned char data[16];
290 unsigned char read_reg[1];
291
292 /* =============================================== */
293
294 /* Step 1 - Test register initialization */
295 dummyData = 0x00;
296 result = inv_serial_single_write(mlsl_handle, pdata->address,
297 YAS530_REGADDR_TEST1, dummyData);
298 if (result) {
299 LOG_RESULT_LOCATION(result);
300 return result;
301 }
302
303 result =
304 inv_serial_single_write(mlsl_handle, pdata->address,
305 YAS530_REGADDR_TEST2, dummyData);
306 if (result) {
307 LOG_RESULT_LOCATION(result);
308 return result;
309 }
310
311 /* Device ID read */
312 result = inv_serial_read(mlsl_handle, pdata->address,
313 YAS530_REGADDR_DEVICE_ID, 1, read_reg);
314
315 /*Step 2 Read the CAL register */
316 /* CAL data read */
317 result = inv_serial_read(mlsl_handle, pdata->address,
318 YAS530_REGADDR_CAL, 16, data);
319 if (result) {
320 LOG_RESULT_LOCATION(result);
321 return result;
322 }
323 /* CAL data Second Read */
324 result = inv_serial_read(mlsl_handle, pdata->address,
325 YAS530_REGADDR_CAL, 16, data);
326 if (result) {
327 LOG_RESULT_LOCATION(result);
328 return result;
329 }
330
331 /*Cal data */
332 dx = data[0];
333 dy1 = data[1];
334 dy2 = data[2];
335 d2 = (data[3] >> 2) & 0x03f;
336 d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03);
337 d4 = data[4] & 0x3f;
338 d5 = (data[5] >> 2) & 0x3f;
339 d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f);
340 d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07);
341 d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01);
342 d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01);
343 d0 = (data[9] >> 2) & 0x1f;
344 dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01);
345
346 /*Correction Data */
347 Cx = (int)dx * 6 - 768;
348 Cy1 = (int)dy1 * 6 - 768;
349 Cy2 = (int)dy2 * 6 - 768;
350 a2 = (int)d2 - 32;
351 a3 = (int)d3 - 8;
352 a4 = (int)d4 - 32;
353 a5 = (int)d5 + 38;
354 a6 = (int)d6 - 32;
355 a7 = (int)d7 - 64;
356 a8 = (int)d8 - 32;
357 a9 = (int)d9;
358 k = (int)d0 + 10;
359
360 /*Obtain the [49:47] bits */
361 dck &= 0x07;
362
363 /*Step 3 : Storing the CONFIG with the CLK value */
364 dummyData = 0x00 | (dck << 2);
365 result = inv_serial_single_write(mlsl_handle, pdata->address,
366 YAS530_REGADDR_CONFIG, dummyData);
367 if (result) {
368 LOG_RESULT_LOCATION(result);
369 return result;
370 }
371
372 /*Step 4 : Set Acquisition Interval Register */
373 dummyData = 0x00;
374 result = inv_serial_single_write(mlsl_handle, pdata->address,
375 YAS530_REGADDR_MEASURE_INTERVAL,
376 dummyData);
377 if (result) {
378 LOG_RESULT_LOCATION(result);
379 return result;
380 }
381
382 /*Step 5 : Reset Coil */
383 dummyData = 0x00;
384 result = inv_serial_single_write(mlsl_handle, pdata->address,
385 YAS530_REGADDR_ACTUATE_INIT_COIL,
386 dummyData);
387 if (result) {
388 LOG_RESULT_LOCATION(result);
389 return result;
390 }
391
392 /* Offset Measurement and Set */
393 result = measure_and_set_offset(mlsl_handle, slave, pdata, offset);
394 if (result) {
395 LOG_RESULT_LOCATION(result);
396 return result;
397 }
398
399 return result;
400}
401
402static int yas530_read(void *mlsl_handle,
403 struct ext_slave_descr *slave,
404 struct ext_slave_platform_data *pdata,
405 unsigned char *data)
406{
407 int result = INV_SUCCESS;
408
409 int busy;
410 short t, x, y1, y2;
411 int32_t xyz[3];
412 short rawfixed[3];
413
414 result = measure_normal(mlsl_handle, slave, pdata,
415 &busy, &t, &x, &y1, &y2);
416 if (result) {
417 LOG_RESULT_LOCATION(result);
418 return result;
419 }
420
421 coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]);
422
423 rawfixed[0] = (short)(xyz[0] / 100);
424 rawfixed[1] = (short)(xyz[1] / 100);
425 rawfixed[2] = (short)(xyz[2] / 100);
426
427 data[0] = rawfixed[0] >> 8;
428 data[1] = rawfixed[0] & 0xFF;
429 data[2] = rawfixed[1] >> 8;
430 data[3] = rawfixed[1] & 0xFF;
431 data[4] = rawfixed[2] >> 8;
432 data[5] = rawfixed[2] & 0xFF;
433
434 if (busy)
435 return INV_ERROR_COMPASS_DATA_NOT_READY;
436 return result;
437}
438
439static struct ext_slave_descr yas530_descr = {
440 .init = NULL,
441 .exit = NULL,
442 .suspend = yas530_suspend,
443 .resume = yas530_resume,
444 .read = yas530_read,
445 .config = NULL,
446 .get_config = NULL,
447 .name = "yas530",
448 .type = EXT_SLAVE_TYPE_COMPASS,
449 .id = COMPASS_ID_YAS530,
450 .read_reg = 0x06,
451 .read_len = 6,
452 .endian = EXT_SLAVE_BIG_ENDIAN,
453 .range = {3276, 8001},
454 .trigger = NULL,
455};
456
457static
458struct ext_slave_descr *yas530_get_slave_descr(void)
459{
460 return &yas530_descr;
461}
462
463/* -------------------------------------------------------------------------- */
464struct yas530_mod_private_data {
465 struct i2c_client *client;
466 struct ext_slave_platform_data *pdata;
467};
468
469static unsigned short normal_i2c[] = { I2C_CLIENT_END };
470
471static int yas530_mod_probe(struct i2c_client *client,
472 const struct i2c_device_id *devid)
473{
474 struct ext_slave_platform_data *pdata;
475 struct yas530_mod_private_data *private_data;
476 int result = 0;
477
478 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
479
480 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
481 result = -ENODEV;
482 goto out_no_free;
483 }
484
485 pdata = client->dev.platform_data;
486 if (!pdata) {
487 dev_err(&client->adapter->dev,
488 "Missing platform data for slave %s\n", devid->name);
489 result = -EFAULT;
490 goto out_no_free;
491 }
492
493 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
494 if (!private_data) {
495 result = -ENOMEM;
496 goto out_no_free;
497 }
498
499 i2c_set_clientdata(client, private_data);
500 private_data->client = client;
501 private_data->pdata = pdata;
502
503 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
504 yas530_get_slave_descr);
505 if (result) {
506 dev_err(&client->adapter->dev,
507 "Slave registration failed: %s, %d\n",
508 devid->name, result);
509 goto out_free_memory;
510 }
511
512 return result;
513
514out_free_memory:
515 kfree(private_data);
516out_no_free:
517 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
518 return result;
519
520}
521
522static int yas530_mod_remove(struct i2c_client *client)
523{
524 struct yas530_mod_private_data *private_data =
525 i2c_get_clientdata(client);
526
527 dev_dbg(&client->adapter->dev, "%s\n", __func__);
528
529 inv_mpu_unregister_slave(client, private_data->pdata,
530 yas530_get_slave_descr);
531
532 kfree(private_data);
533 return 0;
534}
535
536static const struct i2c_device_id yas530_mod_id[] = {
537 { "yas530", COMPASS_ID_YAS530 },
538 {}
539};
540
541MODULE_DEVICE_TABLE(i2c, yas530_mod_id);
542
543static struct i2c_driver yas530_mod_driver = {
544 .class = I2C_CLASS_HWMON,
545 .probe = yas530_mod_probe,
546 .remove = yas530_mod_remove,
547 .id_table = yas530_mod_id,
548 .driver = {
549 .owner = THIS_MODULE,
550 .name = "yas530_mod",
551 },
552 .address_list = normal_i2c,
553};
554
555static int __init yas530_mod_init(void)
556{
557 int res = i2c_add_driver(&yas530_mod_driver);
558 pr_info("%s: Probe name %s\n", __func__, "yas530_mod");
559 if (res)
560 pr_err("%s failed\n", __func__);
561 return res;
562}
563
564static void __exit yas530_mod_exit(void)
565{
566 pr_info("%s\n", __func__);
567 i2c_del_driver(&yas530_mod_driver);
568}
569
570module_init(yas530_mod_init);
571module_exit(yas530_mod_exit);
572
573MODULE_AUTHOR("Invensense Corporation");
574MODULE_DESCRIPTION("Driver to integrate YAS530 sensor with the MPU");
575MODULE_LICENSE("GPL");
576MODULE_ALIAS("yas530_mod");
577
578/**
579 * @}
580 */
diff --git a/drivers/misc/inv_mpu/log.h b/drivers/misc/inv_mpu/log.h
new file mode 100644
index 00000000000..5630602e3ef
--- /dev/null
+++ b/drivers/misc/inv_mpu/log.h
@@ -0,0 +1,287 @@
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 * This file incorporates work covered by the following copyright and
22 * permission notice:
23 *
24 * Copyright (C) 2005 The Android Open Source Project
25 *
26 * Licensed under the Apache License, Version 2.0 (the "License");
27 * you may not use this file except in compliance with the License.
28 * You may obtain a copy of the License at
29 *
30 * http://www.apache.org/licenses/LICENSE-2.0
31 *
32 * Unless required by applicable law or agreed to in writing, software
33 * distributed under the License is distributed on an "AS IS" BASIS,
34 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35 * See the License for the specific language governing permissions and
36 * limitations under the License.
37 */
38
39/*
40 * C/C++ logging functions. See the logging documentation for API details.
41 *
42 * We'd like these to be available from C code (in case we import some from
43 * somewhere), so this has a C interface.
44 *
45 * The output will be correct when the log file is shared between multiple
46 * threads and/or multiple processes so long as the operating system
47 * supports O_APPEND. These calls have mutex-protected data structures
48 * and so are NOT reentrant. Do not use MPL_LOG in a signal handler.
49 */
50#ifndef _LIBS_CUTILS_MPL_LOG_H
51#define _LIBS_CUTILS_MPL_LOG_H
52
53#include "mltypes.h"
54#include <stdarg.h>
55
56
57#include <linux/kernel.h>
58
59
60/* --------------------------------------------------------------------- */
61
62/*
63 * Normally we strip MPL_LOGV (VERBOSE messages) from release builds.
64 * You can modify this (for example with "#define MPL_LOG_NDEBUG 0"
65 * at the top of your source file) to change that behavior.
66 */
67#ifndef MPL_LOG_NDEBUG
68#ifdef NDEBUG
69#define MPL_LOG_NDEBUG 1
70#else
71#define MPL_LOG_NDEBUG 0
72#endif
73#endif
74
75#define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE
76#define MPL_LOG_DEFAULT KERN_DEFAULT
77#define MPL_LOG_VERBOSE KERN_CONT
78#define MPL_LOG_DEBUG KERN_NOTICE
79#define MPL_LOG_INFO KERN_INFO
80#define MPL_LOG_WARN KERN_WARNING
81#define MPL_LOG_ERROR KERN_ERR
82#define MPL_LOG_SILENT MPL_LOG_VERBOSE
83
84
85
86/*
87 * This is the local tag used for the following simplified
88 * logging macros. You can change this preprocessor definition
89 * before using the other macros to change the tag.
90 */
91#ifndef MPL_LOG_TAG
92#define MPL_LOG_TAG
93#endif
94
95/* --------------------------------------------------------------------- */
96
97/*
98 * Simplified macro to send a verbose log message using the current MPL_LOG_TAG.
99 */
100#ifndef MPL_LOGV
101#if MPL_LOG_NDEBUG
102#define MPL_LOGV(fmt, ...) \
103 do { \
104 if (0) \
105 MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\
106 } while (0)
107#else
108#define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
109#endif
110#endif
111
112#ifndef CONDITION
113#define CONDITION(cond) ((cond) != 0)
114#endif
115
116#ifndef MPL_LOGV_IF
117#if MPL_LOG_NDEBUG
118#define MPL_LOGV_IF(cond, fmt, ...) \
119 do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0)
120#else
121#define MPL_LOGV_IF(cond, fmt, ...) \
122 ((CONDITION(cond)) \
123 ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
124 : (void)0)
125#endif
126#endif
127
128/*
129 * Simplified macro to send a debug log message using the current MPL_LOG_TAG.
130 */
131#ifndef MPL_LOGD
132#define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
133#endif
134
135#ifndef MPL_LOGD_IF
136#define MPL_LOGD_IF(cond, fmt, ...) \
137 ((CONDITION(cond)) \
138 ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
139 : (void)0)
140#endif
141
142/*
143 * Simplified macro to send an info log message using the current MPL_LOG_TAG.
144 */
145#ifndef MPL_LOGI
146#define MPL_LOGI(fmt, ...) pr_info(KERN_INFO MPL_LOG_TAG fmt, ##__VA_ARGS__)
147#endif
148
149#ifndef MPL_LOGI_IF
150#define MPL_LOGI_IF(cond, fmt, ...) \
151 ((CONDITION(cond)) \
152 ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
153 : (void)0)
154#endif
155
156/*
157 * Simplified macro to send a warning log message using the current MPL_LOG_TAG.
158 */
159#ifndef MPL_LOGW
160#define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__)
161#endif
162
163#ifndef MPL_LOGW_IF
164#define MPL_LOGW_IF(cond, fmt, ...) \
165 ((CONDITION(cond)) \
166 ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
167 : (void)0)
168#endif
169
170/*
171 * Simplified macro to send an error log message using the current MPL_LOG_TAG.
172 */
173#ifndef MPL_LOGE
174#define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__)
175#endif
176
177#ifndef MPL_LOGE_IF
178#define MPL_LOGE_IF(cond, fmt, ...) \
179 ((CONDITION(cond)) \
180 ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
181 : (void)0)
182#endif
183
184/* --------------------------------------------------------------------- */
185
186/*
187 * Log a fatal error. If the given condition fails, this stops program
188 * execution like a normal assertion, but also generating the given message.
189 * It is NOT stripped from release builds. Note that the condition test
190 * is -inverted- from the normal assert() semantics.
191 */
192#define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \
193 ((CONDITION(cond)) \
194 ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \
195 fmt, ##__VA_ARGS__)) \
196 : (void)0)
197
198#define MPL_LOG_ALWAYS_FATAL(fmt, ...) \
199 (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__)))
200
201/*
202 * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that
203 * are stripped out of release builds.
204 */
205#if MPL_LOG_NDEBUG
206#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
207 do { \
208 if (0) \
209 MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \
210 } while (0)
211#define MPL_LOG_FATAL(fmt, ...) \
212 do { \
213 if (0) \
214 MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \
215 } while (0)
216#else
217#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
218 MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__)
219#define MPL_LOG_FATAL(fmt, ...) \
220 MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__)
221#endif
222
223/*
224 * Assertion that generates a log message when the assertion fails.
225 * Stripped out of release builds. Uses the current MPL_LOG_TAG.
226 */
227#define MPL_LOG_ASSERT(cond, fmt, ...) \
228 MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__)
229
230/* --------------------------------------------------------------------- */
231
232/*
233 * Basic log message macro.
234 *
235 * Example:
236 * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno);
237 *
238 * The second argument may be NULL or "" to indicate the "global" tag.
239 */
240#ifndef MPL_LOG
241#define MPL_LOG(priority, tag, fmt, ...) \
242 MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__)
243#endif
244
245/*
246 * Log macro that allows you to specify a number for the priority.
247 */
248#ifndef MPL_LOG_PRI
249#define MPL_LOG_PRI(priority, tag, fmt, ...) \
250 pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__)
251#endif
252
253/*
254 * Log macro that allows you to pass in a varargs ("args" is a va_list).
255 */
256#ifndef MPL_LOG_PRI_VA
257/* not allowed in the Kernel because there is no dev_dbg that takes a va_list */
258#endif
259
260/* --------------------------------------------------------------------- */
261
262/*
263 * ===========================================================================
264 *
265 * The stuff in the rest of this file should not be used directly.
266 */
267
268int _MLPrintLog(int priority, const char *tag, const char *fmt, ...);
269int _MLPrintVaLog(int priority, const char *tag, const char *fmt, va_list args);
270/* Final implementation of actual writing to a character device */
271int _MLWriteLog(const char *buf, int buflen);
272
273static inline void __print_result_location(int result,
274 const char *file,
275 const char *func, int line)
276{
277 MPL_LOGE("%s|%s|%d returning %d\n", file, func, line, result);
278}
279
280#define LOG_RESULT_LOCATION(condition) \
281 do { \
282 __print_result_location((int)(condition), __FILE__, \
283 __func__, __LINE__); \
284 } while (0)
285
286
287#endif /* _LIBS_CUTILS_MPL_LOG_H */
diff --git a/drivers/misc/inv_mpu/mldl_cfg.h b/drivers/misc/inv_mpu/mldl_cfg.h
new file mode 100644
index 00000000000..2b81b89179d
--- /dev/null
+++ b/drivers/misc/inv_mpu/mldl_cfg.h
@@ -0,0 +1,384 @@
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 MLDL
22 *
23 * @{
24 * @file mldl_cfg.h
25 * @brief The Motion Library Driver Layer Configuration header file.
26 */
27
28#ifndef __MLDL_CFG_H__
29#define __MLDL_CFG_H__
30
31#include "mltypes.h"
32#include "mlsl.h"
33#include <linux/mpu.h>
34#ifdef MPU_CURRENT_BUILD_MPU6050B1
35#include "mpu6050b1.h"
36#elif defined(MPU_CURRENT_BUILD_MPU3050)
37#include "mpu3050.h"
38#endif
39
40#include "log.h"
41
42/*************************************************************************
43 * Sensors Bit definitions
44 *************************************************************************/
45
46#define INV_X_GYRO (0x0001)
47#define INV_Y_GYRO (0x0002)
48#define INV_Z_GYRO (0x0004)
49#define INV_DMP_PROCESSOR (0x0008)
50
51#define INV_X_ACCEL (0x0010)
52#define INV_Y_ACCEL (0x0020)
53#define INV_Z_ACCEL (0x0040)
54
55#define INV_X_COMPASS (0x0080)
56#define INV_Y_COMPASS (0x0100)
57#define INV_Z_COMPASS (0x0200)
58
59#define INV_X_PRESSURE (0x0300)
60#define INV_Y_PRESSURE (0x0800)
61#define INV_Z_PRESSURE (0x1000)
62
63#define INV_TEMPERATURE (0x2000)
64#define INV_TIME (0x4000)
65
66#define INV_THREE_AXIS_GYRO (0x000F)
67#define INV_THREE_AXIS_ACCEL (0x0070)
68#define INV_THREE_AXIS_COMPASS (0x0380)
69#define INV_THREE_AXIS_PRESSURE (0x1C00)
70
71#define INV_FIVE_AXIS (0x007B)
72#define INV_SIX_AXIS_GYRO_ACCEL (0x007F)
73#define INV_SIX_AXIS_ACCEL_COMPASS (0x03F0)
74#define INV_NINE_AXIS (0x03FF)
75#define INV_ALL_SENSORS (0x7FFF)
76
77#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
78
79/* -------------------------------------------------------------------------- */
80struct mpu_ram {
81 __u16 length;
82 __u8 *ram;
83};
84
85struct mpu_gyro_cfg {
86 __u8 int_config;
87 __u8 ext_sync;
88 __u8 full_scale;
89 __u8 lpf;
90 __u8 clk_src;
91 __u8 divider;
92 __u8 dmp_enable;
93 __u8 fifo_enable;
94 __u8 dmp_cfg1;
95 __u8 dmp_cfg2;
96};
97
98/* Offset registers that can be calibrated */
99struct mpu_offsets {
100 __u8 tc[GYRO_NUM_AXES];
101 __u16 gyro[GYRO_NUM_AXES];
102};
103
104/* Chip related information that can be read and verified */
105struct mpu_chip_info {
106 __u8 addr;
107 __u8 product_revision;
108 __u8 silicon_revision;
109 __u8 product_id;
110 __u16 gyro_sens_trim;
111 /* Only used for MPU6050 */
112 __u16 accel_sens_trim;
113};
114
115
116struct inv_mpu_cfg {
117 __u32 requested_sensors;
118 __u8 ignore_system_suspend;
119};
120
121/* Driver related state information */
122struct inv_mpu_state {
123#define MPU_GYRO_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_GYROSCOPE)
124#define MPU_ACCEL_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_ACCEL)
125#define MPU_COMPASS_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_COMPASS)
126#define MPU_PRESSURE_IS_SUSPENDED (0x01 << EXT_SLAVE_TYPE_PRESSURE)
127#define MPU_GYRO_IS_BYPASSED (0x10)
128#define MPU_DMP_IS_SUSPENDED (0x20)
129#define MPU_GYRO_NEEDS_CONFIG (0x40)
130#define MPU_DEVICE_IS_SUSPENDED (0x80)
131 __u8 status;
132 /* 0-1 for 3050, bitfield of BIT_SLVx_DLY_EN, x = [0..4] */
133 __u8 i2c_slaves_enabled;
134};
135
136/* Platform data for the MPU */
137struct mldl_cfg {
138 struct mpu_ram *mpu_ram;
139 struct mpu_gyro_cfg *mpu_gyro_cfg;
140 struct mpu_offsets *mpu_offsets;
141 struct mpu_chip_info *mpu_chip_info;
142
143 /* MPU Related stored status and info */
144 struct inv_mpu_cfg *inv_mpu_cfg;
145 struct inv_mpu_state *inv_mpu_state;
146
147 /* Slave related information */
148 struct ext_slave_descr *slave[EXT_SLAVE_NUM_TYPES];
149 /* Platform Data */
150 struct mpu_platform_data *pdata;
151 struct ext_slave_platform_data *pdata_slave[EXT_SLAVE_NUM_TYPES];
152};
153
154/* -------------------------------------------------------------------------- */
155
156int inv_mpu_open(struct mldl_cfg *mldl_cfg,
157 void *mlsl_handle,
158 void *accel_handle,
159 void *compass_handle,
160 void *pressure_handle);
161int inv_mpu_close(struct mldl_cfg *mldl_cfg,
162 void *mlsl_handle,
163 void *accel_handle,
164 void *compass_handle,
165 void *pressure_handle);
166int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
167 void *gyro_handle,
168 void *accel_handle,
169 void *compass_handle,
170 void *pressure_handle,
171 unsigned long sensors);
172int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
173 void *gyro_handle,
174 void *accel_handle,
175 void *compass_handle,
176 void *pressure_handle,
177 unsigned long sensors);
178int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg,
179 void *mlsl_handle,
180 const unsigned char *data,
181 int size);
182
183/* -------------------------------------------------------------------------- */
184/* Slave Read functions */
185int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
186 void *gyro_handle,
187 void *slave_handle,
188 struct ext_slave_descr *slave,
189 struct ext_slave_platform_data *pdata,
190 unsigned char *data);
191static inline int inv_mpu_read_accel(struct mldl_cfg *mldl_cfg,
192 void *gyro_handle,
193 void *accel_handle, unsigned char *data)
194{
195 if (!mldl_cfg) {
196 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
197 return INV_ERROR_INVALID_PARAMETER;
198 }
199
200 return inv_mpu_slave_read(
201 mldl_cfg, gyro_handle, accel_handle,
202 mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL],
203 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL],
204 data);
205}
206
207static inline int inv_mpu_read_compass(struct mldl_cfg *mldl_cfg,
208 void *gyro_handle,
209 void *compass_handle,
210 unsigned char *data)
211{
212 if (!mldl_cfg) {
213 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
214 return INV_ERROR_INVALID_PARAMETER;
215 }
216
217 return inv_mpu_slave_read(
218 mldl_cfg, gyro_handle, compass_handle,
219 mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS],
220 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS],
221 data);
222}
223
224static inline int inv_mpu_read_pressure(struct mldl_cfg *mldl_cfg,
225 void *gyro_handle,
226 void *pressure_handle,
227 unsigned char *data)
228{
229 if (!mldl_cfg) {
230 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
231 return INV_ERROR_INVALID_PARAMETER;
232 }
233
234 return inv_mpu_slave_read(
235 mldl_cfg, gyro_handle, pressure_handle,
236 mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE],
237 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
238 data);
239}
240
241int gyro_config(void *mlsl_handle,
242 struct mldl_cfg *mldl_cfg,
243 struct ext_slave_config *data);
244
245/* Slave Config functions */
246int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
247 void *gyro_handle,
248 void *slave_handle,
249 struct ext_slave_config *data,
250 struct ext_slave_descr *slave,
251 struct ext_slave_platform_data *pdata);
252static inline int inv_mpu_config_accel(struct mldl_cfg *mldl_cfg,
253 void *gyro_handle,
254 void *accel_handle,
255 struct ext_slave_config *data)
256{
257 if (!mldl_cfg) {
258 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
259 return INV_ERROR_INVALID_PARAMETER;
260 }
261
262 return inv_mpu_slave_config(
263 mldl_cfg, gyro_handle, accel_handle, data,
264 mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL],
265 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]);
266}
267
268static inline int inv_mpu_config_compass(struct mldl_cfg *mldl_cfg,
269 void *gyro_handle,
270 void *compass_handle,
271 struct ext_slave_config *data)
272{
273 if (!mldl_cfg) {
274 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
275 return INV_ERROR_INVALID_PARAMETER;
276 }
277
278 return inv_mpu_slave_config(
279 mldl_cfg, gyro_handle, compass_handle, data,
280 mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS],
281 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]);
282}
283
284static inline int inv_mpu_config_pressure(struct mldl_cfg *mldl_cfg,
285 void *gyro_handle,
286 void *pressure_handle,
287 struct ext_slave_config *data)
288{
289 if (!mldl_cfg) {
290 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
291 return INV_ERROR_INVALID_PARAMETER;
292 }
293
294 return inv_mpu_slave_config(
295 mldl_cfg, gyro_handle, pressure_handle, data,
296 mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE],
297 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]);
298}
299
300int gyro_get_config(void *mlsl_handle,
301 struct mldl_cfg *mldl_cfg,
302 struct ext_slave_config *data);
303
304/* Slave get config functions */
305int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
306 void *gyro_handle,
307 void *slave_handle,
308 struct ext_slave_config *data,
309 struct ext_slave_descr *slave,
310 struct ext_slave_platform_data *pdata);
311
312static inline int inv_mpu_get_accel_config(struct mldl_cfg *mldl_cfg,
313 void *gyro_handle,
314 void *accel_handle,
315 struct ext_slave_config *data)
316{
317 if (!mldl_cfg) {
318 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
319 return INV_ERROR_INVALID_PARAMETER;
320 }
321
322 return inv_mpu_get_slave_config(
323 mldl_cfg, gyro_handle, accel_handle, data,
324 mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL],
325 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]);
326}
327
328static inline int inv_mpu_get_compass_config(struct mldl_cfg *mldl_cfg,
329 void *gyro_handle,
330 void *compass_handle,
331 struct ext_slave_config *data)
332{
333 if (!mldl_cfg || !(mldl_cfg->pdata)) {
334 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
335 return INV_ERROR_INVALID_PARAMETER;
336 }
337
338 return inv_mpu_get_slave_config(
339 mldl_cfg, gyro_handle, compass_handle, data,
340 mldl_cfg->slave[EXT_SLAVE_TYPE_COMPASS],
341 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_COMPASS]);
342}
343
344static inline int inv_mpu_get_pressure_config(struct mldl_cfg *mldl_cfg,
345 void *gyro_handle,
346 void *pressure_handle,
347 struct ext_slave_config *data)
348{
349 if (!mldl_cfg || !(mldl_cfg->pdata)) {
350 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
351 return INV_ERROR_INVALID_PARAMETER;
352 }
353
354 return inv_mpu_get_slave_config(
355 mldl_cfg, gyro_handle, pressure_handle, data,
356 mldl_cfg->slave[EXT_SLAVE_TYPE_PRESSURE],
357 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_PRESSURE]);
358}
359
360/* -------------------------------------------------------------------------- */
361
362static inline
363long inv_mpu_get_sampling_rate_hz(struct mpu_gyro_cfg *gyro_cfg)
364{
365 if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7))
366 return 8000L / (gyro_cfg->divider + 1);
367 else
368 return 1000L / (gyro_cfg->divider + 1);
369}
370
371static inline
372long inv_mpu_get_sampling_period_us(struct mpu_gyro_cfg *gyro_cfg)
373{
374 if (((gyro_cfg->lpf) == 0) || ((gyro_cfg->lpf) == 7))
375 return (long) (1000000L * (gyro_cfg->divider + 1)) / 8000L;
376 else
377 return (long) (1000000L * (gyro_cfg->divider + 1)) / 1000L;
378}
379
380#endif /* __MLDL_CFG_H__ */
381
382/**
383 * @}
384 */
diff --git a/drivers/misc/inv_mpu/mldl_print_cfg.h b/drivers/misc/inv_mpu/mldl_print_cfg.h
new file mode 100644
index 00000000000..2e1911440cc
--- /dev/null
+++ b/drivers/misc/inv_mpu/mldl_print_cfg.h
@@ -0,0 +1,38 @@
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 * @defgroup
22 * @brief
23 *
24 * @{
25 * @file mldl_print_cfg.h
26 * @brief
27 *
28 *
29 */
30#ifndef __MLDL_PRINT_CFG__
31#define __MLDL_PRINT_CFG__
32
33#include "mldl_cfg.h"
34
35
36void mldl_print_cfg(struct mldl_cfg *mldl_cfg);
37
38#endif /* __MLDL_PRINT_CFG__ */
diff --git a/drivers/misc/inv_mpu/mlsl.h b/drivers/misc/inv_mpu/mlsl.h
new file mode 100644
index 00000000000..204baedc1e2
--- /dev/null
+++ b/drivers/misc/inv_mpu/mlsl.h
@@ -0,0 +1,186 @@
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#ifndef __MLSL_H__
21#define __MLSL_H__
22
23/**
24 * @defgroup MLSL
25 * @brief Motion Library - Serial Layer.
26 * The Motion Library System Layer provides the Motion Library
27 * with the communication interface to the hardware.
28 *
29 * The communication interface is assumed to support serial
30 * transfers in burst of variable length up to
31 * SERIAL_MAX_TRANSFER_SIZE.
32 * The default value for SERIAL_MAX_TRANSFER_SIZE is 128 bytes.
33 * Transfers of length greater than SERIAL_MAX_TRANSFER_SIZE, will
34 * be subdivided in smaller transfers of length <=
35 * SERIAL_MAX_TRANSFER_SIZE.
36 * The SERIAL_MAX_TRANSFER_SIZE definition can be modified to
37 * overcome any host processor transfer size limitation down to
38 * 1 B, the minimum.
39 * An higher value for SERIAL_MAX_TRANSFER_SIZE will favor
40 * performance and efficiency while requiring higher resource usage
41 * (mostly buffering). A smaller value will increase overhead and
42 * decrease efficiency but allows to operate with more resource
43 * constrained processor and master serial controllers.
44 * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the
45 * mlsl.h header file and master serial controllers.
46 * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the
47 * mlsl.h header file.
48 *
49 * @{
50 * @file mlsl.h
51 * @brief The Motion Library System Layer.
52 *
53 */
54
55#include "mltypes.h"
56#include <linux/mpu.h>
57
58
59/*
60 * NOTE : to properly support Yamaha compass reads,
61 * the max transfer size should be at least 9 B.
62 * Length in bytes, typically a power of 2 >= 2
63 */
64#define SERIAL_MAX_TRANSFER_SIZE 128
65
66
67/**
68 * inv_serial_single_write() - used to write a single byte of data.
69 * @sl_handle pointer to the serial device used for the communication.
70 * @slave_addr I2C slave address of device.
71 * @register_addr Register address to write.
72 * @data Single byte of data to write.
73 *
74 * It is called by the MPL to write a single byte of data to the MPU.
75 *
76 * returns INV_SUCCESS if successful, a non-zero error code otherwise.
77 */
78int inv_serial_single_write(
79 void *sl_handle,
80 unsigned char slave_addr,
81 unsigned char register_addr,
82 unsigned char data);
83
84/**
85 * inv_serial_write() - used to write multiple bytes of data to registers.
86 * @sl_handle a file handle to the serial device used for the communication.
87 * @slave_addr I2C slave address of device.
88 * @register_addr Register address to write.
89 * @length Length of burst of data.
90 * @data Pointer to block of data.
91 *
92 * returns INV_SUCCESS if successful, a non-zero error code otherwise.
93 */
94int inv_serial_write(
95 void *sl_handle,
96 unsigned char slave_addr,
97 unsigned short length,
98 unsigned char const *data);
99
100/**
101 * inv_serial_read() - used to read multiple bytes of data from registers.
102 * @sl_handle a file handle to the serial device used for the communication.
103 * @slave_addr I2C slave address of device.
104 * @register_addr Register address to read.
105 * @length Length of burst of data.
106 * @data Pointer to block of data.
107 *
108 * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
109 */
110int inv_serial_read(
111 void *sl_handle,
112 unsigned char slave_addr,
113 unsigned char register_addr,
114 unsigned short length,
115 unsigned char *data);
116
117/**
118 * inv_serial_read_mem() - used to read multiple bytes of data from the memory.
119 * This should be sent by I2C or SPI.
120 *
121 * @sl_handle a file handle to the serial device used for the communication.
122 * @slave_addr I2C slave address of device.
123 * @mem_addr The location in the memory to read from.
124 * @length Length of burst data.
125 * @data Pointer to block of data.
126 *
127 * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
128 */
129int inv_serial_read_mem(
130 void *sl_handle,
131 unsigned char slave_addr,
132 unsigned short mem_addr,
133 unsigned short length,
134 unsigned char *data);
135
136/**
137 * inv_serial_write_mem() - used to write multiple bytes of data to the memory.
138 * @sl_handle a file handle to the serial device used for the communication.
139 * @slave_addr I2C slave address of device.
140 * @mem_addr The location in the memory to write to.
141 * @length Length of burst data.
142 * @data Pointer to block of data.
143 *
144 * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
145 */
146int inv_serial_write_mem(
147 void *sl_handle,
148 unsigned char slave_addr,
149 unsigned short mem_addr,
150 unsigned short length,
151 unsigned char const *data);
152
153/**
154 * inv_serial_read_fifo() - used to read multiple bytes of data from the fifo.
155 * @sl_handle a file handle to the serial device used for the communication.
156 * @slave_addr I2C slave address of device.
157 * @length Length of burst of data.
158 * @data Pointer to block of data.
159 *
160 * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
161 */
162int inv_serial_read_fifo(
163 void *sl_handle,
164 unsigned char slave_addr,
165 unsigned short length,
166 unsigned char *data);
167
168/**
169 * inv_serial_write_fifo() - used to write multiple bytes of data to the fifo.
170 * @sl_handle a file handle to the serial device used for the communication.
171 * @slave_addr I2C slave address of device.
172 * @length Length of burst of data.
173 * @data Pointer to block of data.
174 *
175 * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
176 */
177int inv_serial_write_fifo(
178 void *sl_handle,
179 unsigned char slave_addr,
180 unsigned short length,
181 unsigned char const *data);
182
183/**
184 * @}
185 */
186#endif /* __MLSL_H__ */
diff --git a/drivers/misc/inv_mpu/mltypes.h b/drivers/misc/inv_mpu/mltypes.h
new file mode 100644
index 00000000000..a249f93be3e
--- /dev/null
+++ b/drivers/misc/inv_mpu/mltypes.h
@@ -0,0 +1,234 @@
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 * @defgroup MLERROR
22 * @brief Definition of the error codes used within the MPL and
23 * returned to the user.
24 * Every function tries to return a meaningful error code basing
25 * on the occuring error condition. The error code is numeric.
26 *
27 * The available error codes and their associated values are:
28 * - (0) INV_SUCCESS
29 * - (32) INV_ERROR
30 * - (22 / EINVAL) INV_ERROR_INVALID_PARAMETER
31 * - (1 / EPERM) INV_ERROR_FEATURE_NOT_ENABLED
32 * - (36) INV_ERROR_FEATURE_NOT_IMPLEMENTED
33 * - (38) INV_ERROR_DMP_NOT_STARTED
34 * - (39) INV_ERROR_DMP_STARTED
35 * - (40) INV_ERROR_NOT_OPENED
36 * - (41) INV_ERROR_OPENED
37 * - (19 / ENODEV) INV_ERROR_INVALID_MODULE
38 * - (12 / ENOMEM) INV_ERROR_MEMORY_EXAUSTED
39 * - (44) INV_ERROR_DIVIDE_BY_ZERO
40 * - (45) INV_ERROR_ASSERTION_FAILURE
41 * - (46) INV_ERROR_FILE_OPEN
42 * - (47) INV_ERROR_FILE_READ
43 * - (48) INV_ERROR_FILE_WRITE
44 * - (49) INV_ERROR_INVALID_CONFIGURATION
45 * - (52) INV_ERROR_SERIAL_CLOSED
46 * - (53) INV_ERROR_SERIAL_OPEN_ERROR
47 * - (54) INV_ERROR_SERIAL_READ
48 * - (55) INV_ERROR_SERIAL_WRITE
49 * - (56) INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
50 * - (57) INV_ERROR_SM_TRANSITION
51 * - (58) INV_ERROR_SM_IMPROPER_STATE
52 * - (62) INV_ERROR_FIFO_OVERFLOW
53 * - (63) INV_ERROR_FIFO_FOOTER
54 * - (64) INV_ERROR_FIFO_READ_COUNT
55 * - (65) INV_ERROR_FIFO_READ_DATA
56 * - (72) INV_ERROR_MEMORY_SET
57 * - (82) INV_ERROR_LOG_MEMORY_ERROR
58 * - (83) INV_ERROR_LOG_OUTPUT_ERROR
59 * - (92) INV_ERROR_OS_BAD_PTR
60 * - (93) INV_ERROR_OS_BAD_HANDLE
61 * - (94) INV_ERROR_OS_CREATE_FAILED
62 * - (95) INV_ERROR_OS_LOCK_FAILED
63 * - (102) INV_ERROR_COMPASS_DATA_OVERFLOW
64 * - (103) INV_ERROR_COMPASS_DATA_UNDERFLOW
65 * - (104) INV_ERROR_COMPASS_DATA_NOT_READY
66 * - (105) INV_ERROR_COMPASS_DATA_ERROR
67 * - (107) INV_ERROR_CALIBRATION_LOAD
68 * - (108) INV_ERROR_CALIBRATION_STORE
69 * - (109) INV_ERROR_CALIBRATION_LEN
70 * - (110) INV_ERROR_CALIBRATION_CHECKSUM
71 * - (111) INV_ERROR_ACCEL_DATA_OVERFLOW
72 * - (112) INV_ERROR_ACCEL_DATA_UNDERFLOW
73 * - (113) INV_ERROR_ACCEL_DATA_NOT_READY
74 * - (114) INV_ERROR_ACCEL_DATA_ERROR
75 *
76 * The available warning codes and their associated values are:
77 * - (115) INV_WARNING_MOTION_RACE
78 * - (116) INV_WARNING_QUAT_TRASHED
79 *
80 * @{
81 * @file mltypes.h
82 * @}
83 */
84
85#ifndef MLTYPES_H
86#define MLTYPES_H
87
88#include <linux/types.h>
89#include <asm-generic/errno-base.h>
90
91
92
93
94/*---------------------------
95 * ML Defines
96 *--------------------------*/
97
98#ifndef NULL
99#define NULL 0
100#endif
101
102/* - ML Errors. - */
103#define ERROR_NAME(x) (#x)
104#define ERROR_CHECK_FIRST(first, x) \
105 { if (INV_SUCCESS == first) first = x; }
106
107#define INV_SUCCESS (0)
108/* Generic Error code. Proprietary Error Codes only */
109#define INV_ERROR_BASE (0x20)
110#define INV_ERROR (INV_ERROR_BASE)
111
112/* Compatibility and other generic error codes */
113#define INV_ERROR_INVALID_PARAMETER (EINVAL)
114#define INV_ERROR_FEATURE_NOT_ENABLED (EPERM)
115#define INV_ERROR_FEATURE_NOT_IMPLEMENTED (INV_ERROR_BASE + 4)
116#define INV_ERROR_DMP_NOT_STARTED (INV_ERROR_BASE + 6)
117#define INV_ERROR_DMP_STARTED (INV_ERROR_BASE + 7)
118#define INV_ERROR_NOT_OPENED (INV_ERROR_BASE + 8)
119#define INV_ERROR_OPENED (INV_ERROR_BASE + 9)
120#define INV_ERROR_INVALID_MODULE (ENODEV)
121#define INV_ERROR_MEMORY_EXAUSTED (ENOMEM)
122#define INV_ERROR_DIVIDE_BY_ZERO (INV_ERROR_BASE + 12)
123#define INV_ERROR_ASSERTION_FAILURE (INV_ERROR_BASE + 13)
124#define INV_ERROR_FILE_OPEN (INV_ERROR_BASE + 14)
125#define INV_ERROR_FILE_READ (INV_ERROR_BASE + 15)
126#define INV_ERROR_FILE_WRITE (INV_ERROR_BASE + 16)
127#define INV_ERROR_INVALID_CONFIGURATION (INV_ERROR_BASE + 17)
128
129/* Serial Communication */
130#define INV_ERROR_SERIAL_CLOSED (INV_ERROR_BASE + 20)
131#define INV_ERROR_SERIAL_OPEN_ERROR (INV_ERROR_BASE + 21)
132#define INV_ERROR_SERIAL_READ (INV_ERROR_BASE + 22)
133#define INV_ERROR_SERIAL_WRITE (INV_ERROR_BASE + 23)
134#define INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (INV_ERROR_BASE + 24)
135
136/* SM = State Machine */
137#define INV_ERROR_SM_TRANSITION (INV_ERROR_BASE + 25)
138#define INV_ERROR_SM_IMPROPER_STATE (INV_ERROR_BASE + 26)
139
140/* Fifo */
141#define INV_ERROR_FIFO_OVERFLOW (INV_ERROR_BASE + 30)
142#define INV_ERROR_FIFO_FOOTER (INV_ERROR_BASE + 31)
143#define INV_ERROR_FIFO_READ_COUNT (INV_ERROR_BASE + 32)
144#define INV_ERROR_FIFO_READ_DATA (INV_ERROR_BASE + 33)
145
146/* Memory & Registers, Set & Get */
147#define INV_ERROR_MEMORY_SET (INV_ERROR_BASE + 40)
148
149#define INV_ERROR_LOG_MEMORY_ERROR (INV_ERROR_BASE + 50)
150#define INV_ERROR_LOG_OUTPUT_ERROR (INV_ERROR_BASE + 51)
151
152/* OS interface errors */
153#define INV_ERROR_OS_BAD_PTR (INV_ERROR_BASE + 60)
154#define INV_ERROR_OS_BAD_HANDLE (INV_ERROR_BASE + 61)
155#define INV_ERROR_OS_CREATE_FAILED (INV_ERROR_BASE + 62)
156#define INV_ERROR_OS_LOCK_FAILED (INV_ERROR_BASE + 63)
157
158/* Compass errors */
159#define INV_ERROR_COMPASS_DATA_OVERFLOW (INV_ERROR_BASE + 70)
160#define INV_ERROR_COMPASS_DATA_UNDERFLOW (INV_ERROR_BASE + 71)
161#define INV_ERROR_COMPASS_DATA_NOT_READY (INV_ERROR_BASE + 72)
162#define INV_ERROR_COMPASS_DATA_ERROR (INV_ERROR_BASE + 73)
163
164/* Load/Store calibration */
165#define INV_ERROR_CALIBRATION_LOAD (INV_ERROR_BASE + 75)
166#define INV_ERROR_CALIBRATION_STORE (INV_ERROR_BASE + 76)
167#define INV_ERROR_CALIBRATION_LEN (INV_ERROR_BASE + 77)
168#define INV_ERROR_CALIBRATION_CHECKSUM (INV_ERROR_BASE + 78)
169
170/* Accel errors */
171#define INV_ERROR_ACCEL_DATA_OVERFLOW (INV_ERROR_BASE + 79)
172#define INV_ERROR_ACCEL_DATA_UNDERFLOW (INV_ERROR_BASE + 80)
173#define INV_ERROR_ACCEL_DATA_NOT_READY (INV_ERROR_BASE + 81)
174#define INV_ERROR_ACCEL_DATA_ERROR (INV_ERROR_BASE + 82)
175
176/* No Motion Warning States */
177#define INV_WARNING_MOTION_RACE (INV_ERROR_BASE + 83)
178#define INV_WARNING_QUAT_TRASHED (INV_ERROR_BASE + 84)
179#define INV_WARNING_GYRO_MAG (INV_ERROR_BASE + 85)
180
181#ifdef INV_USE_LEGACY_NAMES
182#define ML_SUCCESS INV_SUCCESS
183#define ML_ERROR INV_ERROR
184#define ML_ERROR_INVALID_PARAMETER INV_ERROR_INVALID_PARAMETER
185#define ML_ERROR_FEATURE_NOT_ENABLED INV_ERROR_FEATURE_NOT_ENABLED
186#define ML_ERROR_FEATURE_NOT_IMPLEMENTED INV_ERROR_FEATURE_NOT_IMPLEMENTED
187#define ML_ERROR_DMP_NOT_STARTED INV_ERROR_DMP_NOT_STARTED
188#define ML_ERROR_DMP_STARTED INV_ERROR_DMP_STARTED
189#define ML_ERROR_NOT_OPENED INV_ERROR_NOT_OPENED
190#define ML_ERROR_OPENED INV_ERROR_OPENED
191#define ML_ERROR_INVALID_MODULE INV_ERROR_INVALID_MODULE
192#define ML_ERROR_MEMORY_EXAUSTED INV_ERROR_MEMORY_EXAUSTED
193#define ML_ERROR_DIVIDE_BY_ZERO INV_ERROR_DIVIDE_BY_ZERO
194#define ML_ERROR_ASSERTION_FAILURE INV_ERROR_ASSERTION_FAILURE
195#define ML_ERROR_FILE_OPEN INV_ERROR_FILE_OPEN
196#define ML_ERROR_FILE_READ INV_ERROR_FILE_READ
197#define ML_ERROR_FILE_WRITE INV_ERROR_FILE_WRITE
198#define ML_ERROR_INVALID_CONFIGURATION INV_ERROR_INVALID_CONFIGURATION
199#define ML_ERROR_SERIAL_CLOSED INV_ERROR_SERIAL_CLOSED
200#define ML_ERROR_SERIAL_OPEN_ERROR INV_ERROR_SERIAL_OPEN_ERROR
201#define ML_ERROR_SERIAL_READ INV_ERROR_SERIAL_READ
202#define ML_ERROR_SERIAL_WRITE INV_ERROR_SERIAL_WRITE
203#define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED \
204 INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
205#define ML_ERROR_SM_TRANSITION INV_ERROR_SM_TRANSITION
206#define ML_ERROR_SM_IMPROPER_STATE INV_ERROR_SM_IMPROPER_STATE
207#define ML_ERROR_FIFO_OVERFLOW INV_ERROR_FIFO_OVERFLOW
208#define ML_ERROR_FIFO_FOOTER INV_ERROR_FIFO_FOOTER
209#define ML_ERROR_FIFO_READ_COUNT INV_ERROR_FIFO_READ_COUNT
210#define ML_ERROR_FIFO_READ_DATA INV_ERROR_FIFO_READ_DATA
211#define ML_ERROR_MEMORY_SET INV_ERROR_MEMORY_SET
212#define ML_ERROR_LOG_MEMORY_ERROR INV_ERROR_LOG_MEMORY_ERROR
213#define ML_ERROR_LOG_OUTPUT_ERROR INV_ERROR_LOG_OUTPUT_ERROR
214#define ML_ERROR_OS_BAD_PTR INV_ERROR_OS_BAD_PTR
215#define ML_ERROR_OS_BAD_HANDLE INV_ERROR_OS_BAD_HANDLE
216#define ML_ERROR_OS_CREATE_FAILED INV_ERROR_OS_CREATE_FAILED
217#define ML_ERROR_OS_LOCK_FAILED INV_ERROR_OS_LOCK_FAILED
218#define ML_ERROR_COMPASS_DATA_OVERFLOW INV_ERROR_COMPASS_DATA_OVERFLOW
219#define ML_ERROR_COMPASS_DATA_UNDERFLOW INV_ERROR_COMPASS_DATA_UNDERFLOW
220#define ML_ERROR_COMPASS_DATA_NOT_READY INV_ERROR_COMPASS_DATA_NOT_READY
221#define ML_ERROR_COMPASS_DATA_ERROR INV_ERROR_COMPASS_DATA_ERROR
222#define ML_ERROR_CALIBRATION_LOAD INV_ERROR_CALIBRATION_LOAD
223#define ML_ERROR_CALIBRATION_STORE INV_ERROR_CALIBRATION_STORE
224#define ML_ERROR_CALIBRATION_LEN INV_ERROR_CALIBRATION_LEN
225#define ML_ERROR_CALIBRATION_CHECKSUM INV_ERROR_CALIBRATION_CHECKSUM
226#define ML_ERROR_ACCEL_DATA_OVERFLOW INV_ERROR_ACCEL_DATA_OVERFLOW
227#define ML_ERROR_ACCEL_DATA_UNDERFLOW INV_ERROR_ACCEL_DATA_UNDERFLOW
228#define ML_ERROR_ACCEL_DATA_NOT_READY INV_ERROR_ACCEL_DATA_NOT_READY
229#define ML_ERROR_ACCEL_DATA_ERROR INV_ERROR_ACCEL_DATA_ERROR
230#endif
231
232/* For Linux coding compliance */
233
234#endif /* MLTYPES_H */
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h
new file mode 100644
index 00000000000..b6a4fcfac58
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.h
@@ -0,0 +1,36 @@
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#ifndef __MPU_DEV_H__
22#define __MPU_DEV_H__
23
24#include <linux/i2c.h>
25#include <linux/module.h>
26#include <linux/mpu.h>
27
28int inv_mpu_register_slave(struct module *slave_module,
29 struct i2c_client *client,
30 struct ext_slave_platform_data *pdata,
31 struct ext_slave_descr *(*slave_descr)(void));
32
33void inv_mpu_unregister_slave(struct i2c_client *client,
34 struct ext_slave_platform_data *pdata,
35 struct ext_slave_descr *(*slave_descr)(void));
36#endif
diff --git a/drivers/misc/inv_mpu/mpu3050.h b/drivers/misc/inv_mpu/mpu3050.h
new file mode 100644
index 00000000000..02af16ed121
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050.h
@@ -0,0 +1,251 @@
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#ifndef __MPU_H_
21#error Do not include this file directly. Include mpu.h instead.
22#endif
23
24#ifndef __MPU3050_H_
25#define __MPU3050_H_
26
27#include <linux/types.h>
28
29
30#define MPU_NAME "mpu3050"
31#define DEFAULT_MPU_SLAVEADDR 0x68
32
33/*==== MPU REGISTER SET ====*/
34enum mpu_register {
35 MPUREG_WHO_AM_I = 0, /* 00 0x00 */
36 MPUREG_PRODUCT_ID, /* 01 0x01 */
37 MPUREG_02_RSVD, /* 02 0x02 */
38 MPUREG_03_RSVD, /* 03 0x03 */
39 MPUREG_04_RSVD, /* 04 0x04 */
40 MPUREG_XG_OFFS_TC, /* 05 0x05 */
41 MPUREG_06_RSVD, /* 06 0x06 */
42 MPUREG_07_RSVD, /* 07 0x07 */
43 MPUREG_YG_OFFS_TC, /* 08 0x08 */
44 MPUREG_09_RSVD, /* 09 0x09 */
45 MPUREG_0A_RSVD, /* 10 0x0a */
46 MPUREG_ZG_OFFS_TC, /* 11 0x0b */
47 MPUREG_X_OFFS_USRH, /* 12 0x0c */
48 MPUREG_X_OFFS_USRL, /* 13 0x0d */
49 MPUREG_Y_OFFS_USRH, /* 14 0x0e */
50 MPUREG_Y_OFFS_USRL, /* 15 0x0f */
51 MPUREG_Z_OFFS_USRH, /* 16 0x10 */
52 MPUREG_Z_OFFS_USRL, /* 17 0x11 */
53 MPUREG_FIFO_EN1, /* 18 0x12 */
54 MPUREG_FIFO_EN2, /* 19 0x13 */
55 MPUREG_AUX_SLV_ADDR, /* 20 0x14 */
56 MPUREG_SMPLRT_DIV, /* 21 0x15 */
57 MPUREG_DLPF_FS_SYNC, /* 22 0x16 */
58 MPUREG_INT_CFG, /* 23 0x17 */
59 MPUREG_ACCEL_BURST_ADDR,/* 24 0x18 */
60 MPUREG_19_RSVD, /* 25 0x19 */
61 MPUREG_INT_STATUS, /* 26 0x1a */
62 MPUREG_TEMP_OUT_H, /* 27 0x1b */
63 MPUREG_TEMP_OUT_L, /* 28 0x1c */
64 MPUREG_GYRO_XOUT_H, /* 29 0x1d */
65 MPUREG_GYRO_XOUT_L, /* 30 0x1e */
66 MPUREG_GYRO_YOUT_H, /* 31 0x1f */
67 MPUREG_GYRO_YOUT_L, /* 32 0x20 */
68 MPUREG_GYRO_ZOUT_H, /* 33 0x21 */
69 MPUREG_GYRO_ZOUT_L, /* 34 0x22 */
70 MPUREG_23_RSVD, /* 35 0x23 */
71 MPUREG_24_RSVD, /* 36 0x24 */
72 MPUREG_25_RSVD, /* 37 0x25 */
73 MPUREG_26_RSVD, /* 38 0x26 */
74 MPUREG_27_RSVD, /* 39 0x27 */
75 MPUREG_28_RSVD, /* 40 0x28 */
76 MPUREG_29_RSVD, /* 41 0x29 */
77 MPUREG_2A_RSVD, /* 42 0x2a */
78 MPUREG_2B_RSVD, /* 43 0x2b */
79 MPUREG_2C_RSVD, /* 44 0x2c */
80 MPUREG_2D_RSVD, /* 45 0x2d */
81 MPUREG_2E_RSVD, /* 46 0x2e */
82 MPUREG_2F_RSVD, /* 47 0x2f */
83 MPUREG_30_RSVD, /* 48 0x30 */
84 MPUREG_31_RSVD, /* 49 0x31 */
85 MPUREG_32_RSVD, /* 50 0x32 */
86 MPUREG_33_RSVD, /* 51 0x33 */
87 MPUREG_34_RSVD, /* 52 0x34 */
88 MPUREG_DMP_CFG_1, /* 53 0x35 */
89 MPUREG_DMP_CFG_2, /* 54 0x36 */
90 MPUREG_BANK_SEL, /* 55 0x37 */
91 MPUREG_MEM_START_ADDR, /* 56 0x38 */
92 MPUREG_MEM_R_W, /* 57 0x39 */
93 MPUREG_FIFO_COUNTH, /* 58 0x3a */
94 MPUREG_FIFO_COUNTL, /* 59 0x3b */
95 MPUREG_FIFO_R_W, /* 60 0x3c */
96 MPUREG_USER_CTRL, /* 61 0x3d */
97 MPUREG_PWR_MGM, /* 62 0x3e */
98 MPUREG_3F_RSVD, /* 63 0x3f */
99 NUM_OF_MPU_REGISTERS /* 64 0x40 */
100};
101
102/*==== BITS FOR MPU ====*/
103
104/*---- MPU 'FIFO_EN1' register (12) ----*/
105#define BIT_TEMP_OUT 0x80
106#define BIT_GYRO_XOUT 0x40
107#define BIT_GYRO_YOUT 0x20
108#define BIT_GYRO_ZOUT 0x10
109#define BIT_ACCEL_XOUT 0x08
110#define BIT_ACCEL_YOUT 0x04
111#define BIT_ACCEL_ZOUT 0x02
112#define BIT_AUX_1OUT 0x01
113/*---- MPU 'FIFO_EN2' register (13) ----*/
114#define BIT_AUX_2OUT 0x02
115#define BIT_AUX_3OUT 0x01
116/*---- MPU 'DLPF_FS_SYNC' register (16) ----*/
117#define BITS_EXT_SYNC_NONE 0x00
118#define BITS_EXT_SYNC_TEMP 0x20
119#define BITS_EXT_SYNC_GYROX 0x40
120#define BITS_EXT_SYNC_GYROY 0x60
121#define BITS_EXT_SYNC_GYROZ 0x80
122#define BITS_EXT_SYNC_ACCELX 0xA0
123#define BITS_EXT_SYNC_ACCELY 0xC0
124#define BITS_EXT_SYNC_ACCELZ 0xE0
125#define BITS_EXT_SYNC_MASK 0xE0
126#define BITS_FS_250DPS 0x00
127#define BITS_FS_500DPS 0x08
128#define BITS_FS_1000DPS 0x10
129#define BITS_FS_2000DPS 0x18
130#define BITS_FS_MASK 0x18
131#define BITS_DLPF_CFG_256HZ_NOLPF2 0x00
132#define BITS_DLPF_CFG_188HZ 0x01
133#define BITS_DLPF_CFG_98HZ 0x02
134#define BITS_DLPF_CFG_42HZ 0x03
135#define BITS_DLPF_CFG_20HZ 0x04
136#define BITS_DLPF_CFG_10HZ 0x05
137#define BITS_DLPF_CFG_5HZ 0x06
138#define BITS_DLPF_CFG_2100HZ_NOLPF 0x07
139#define BITS_DLPF_CFG_MASK 0x07
140/*---- MPU 'INT_CFG' register (17) ----*/
141#define BIT_ACTL 0x80
142#define BIT_ACTL_LOW 0x80
143#define BIT_ACTL_HIGH 0x00
144#define BIT_OPEN 0x40
145#define BIT_OPEN_DRAIN 0x40
146#define BIT_PUSH_PULL 0x00
147#define BIT_LATCH_INT_EN 0x20
148#define BIT_INT_PULSE_WIDTH_50US 0x00
149#define BIT_INT_ANYRD_2CLEAR 0x10
150#define BIT_INT_STAT_READ_2CLEAR 0x00
151#define BIT_MPU_RDY_EN 0x04
152#define BIT_DMP_INT_EN 0x02
153#define BIT_RAW_RDY_EN 0x01
154/*---- MPU 'INT_STATUS' register (1A) ----*/
155#define BIT_INT_STATUS_FIFO_OVERLOW 0x80
156#define BIT_MPU_RDY 0x04
157#define BIT_DMP_INT 0x02
158#define BIT_RAW_RDY 0x01
159/*---- MPU 'BANK_SEL' register (37) ----*/
160#define BIT_PRFTCH_EN 0x20
161#define BIT_CFG_USER_BANK 0x10
162#define BITS_MEM_SEL 0x0f
163/*---- MPU 'USER_CTRL' register (3D) ----*/
164#define BIT_DMP_EN 0x80
165#define BIT_FIFO_EN 0x40
166#define BIT_AUX_IF_EN 0x20
167#define BIT_AUX_RD_LENG 0x10
168#define BIT_AUX_IF_RST 0x08
169#define BIT_DMP_RST 0x04
170#define BIT_FIFO_RST 0x02
171#define BIT_GYRO_RST 0x01
172/*---- MPU 'PWR_MGM' register (3E) ----*/
173#define BIT_H_RESET 0x80
174#define BIT_SLEEP 0x40
175#define BIT_STBY_XG 0x20
176#define BIT_STBY_YG 0x10
177#define BIT_STBY_ZG 0x08
178#define BITS_CLKSEL 0x07
179
180/*---- MPU Silicon Revision ----*/
181#define MPU_SILICON_REV_A4 1 /* MPU A4 Device */
182#define MPU_SILICON_REV_B1 2 /* MPU B1 Device */
183#define MPU_SILICON_REV_B4 3 /* MPU B4 Device */
184#define MPU_SILICON_REV_B6 4 /* MPU B6 Device */
185
186/*---- MPU Memory ----*/
187#define MPU_MEM_BANK_SIZE (256)
188#define FIFO_HW_SIZE (512)
189
190enum MPU_MEMORY_BANKS {
191 MPU_MEM_RAM_BANK_0 = 0,
192 MPU_MEM_RAM_BANK_1,
193 MPU_MEM_RAM_BANK_2,
194 MPU_MEM_RAM_BANK_3,
195 MPU_MEM_NUM_RAM_BANKS,
196 MPU_MEM_OTP_BANK_0 = MPU_MEM_NUM_RAM_BANKS,
197 /* This one is always last */
198 MPU_MEM_NUM_BANKS
199};
200
201/*---- structure containing control variables used by MLDL ----*/
202/*---- MPU clock source settings ----*/
203/*---- MPU filter selections ----*/
204enum mpu_filter {
205 MPU_FILTER_256HZ_NOLPF2 = 0,
206 MPU_FILTER_188HZ,
207 MPU_FILTER_98HZ,
208 MPU_FILTER_42HZ,
209 MPU_FILTER_20HZ,
210 MPU_FILTER_10HZ,
211 MPU_FILTER_5HZ,
212 MPU_FILTER_2100HZ_NOLPF,
213 NUM_MPU_FILTER
214};
215
216enum mpu_fullscale {
217 MPU_FS_250DPS = 0,
218 MPU_FS_500DPS,
219 MPU_FS_1000DPS,
220 MPU_FS_2000DPS,
221 NUM_MPU_FS
222};
223
224enum mpu_clock_sel {
225 MPU_CLK_SEL_INTERNAL = 0,
226 MPU_CLK_SEL_PLLGYROX,
227 MPU_CLK_SEL_PLLGYROY,
228 MPU_CLK_SEL_PLLGYROZ,
229 MPU_CLK_SEL_PLLEXT32K,
230 MPU_CLK_SEL_PLLEXT19M,
231 MPU_CLK_SEL_RESERVED,
232 MPU_CLK_SEL_STOP,
233 NUM_CLK_SEL
234};
235
236enum mpu_ext_sync {
237 MPU_EXT_SYNC_NONE = 0,
238 MPU_EXT_SYNC_TEMP,
239 MPU_EXT_SYNC_GYROX,
240 MPU_EXT_SYNC_GYROY,
241 MPU_EXT_SYNC_GYROZ,
242 MPU_EXT_SYNC_ACCELX,
243 MPU_EXT_SYNC_ACCELY,
244 MPU_EXT_SYNC_ACCELZ,
245 NUM_MPU_EXT_SYNC
246};
247
248#define DLPF_FS_SYNC_VALUE(ext_sync, full_scale, lpf) \
249 ((ext_sync << 5) | (full_scale << 3) | lpf)
250
251#endif /* __MPU3050_H_ */
diff --git a/drivers/misc/inv_mpu/mpu3050/Makefile b/drivers/misc/inv_mpu/mpu3050/Makefile
new file mode 100644
index 00000000000..9e573930238
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/Makefile
@@ -0,0 +1,17 @@
1
2# Kernel makefile for motions sensors
3#
4#
5
6obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o
7
8ccflags-y := -DMPU_CURRENT_BUILD_MPU3050
9
10mpu3050-objs += mldl_cfg.o
11mpu3050-objs += mldl_print_cfg.o
12mpu3050-objs += mlsl-kernel.o
13mpu3050-objs += mpu-dev.o
14
15EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
16EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
17EXTRA_CFLAGS += -DINV_CACHE_DMP=1
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c
new file mode 100644
index 00000000000..ccacc8ec0b5
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/mldl_cfg.c
@@ -0,0 +1,1765 @@
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 MLDL
22 *
23 * @{
24 * @file mldl_cfg.c
25 * @brief The Motion Library Driver Layer.
26 */
27
28/* -------------------------------------------------------------------------- */
29#include <linux/delay.h>
30#include <linux/slab.h>
31
32#include <stddef.h>
33
34#include "mldl_cfg.h"
35#include <linux/mpu.h>
36#include "mpu3050.h"
37
38#include "mlsl.h"
39#include "mldl_print_cfg.h"
40#include "log.h"
41#undef MPL_LOG_TAG
42#define MPL_LOG_TAG "mldl_cfg:"
43
44/* -------------------------------------------------------------------------- */
45
46#define SLEEP 1
47#define WAKE_UP 0
48#define RESET 1
49#define STANDBY 1
50
51/* -------------------------------------------------------------------------- */
52
53/**
54 * @brief Stop the DMP running
55 *
56 * @return INV_SUCCESS or non-zero error code
57 */
58static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle)
59{
60 unsigned char user_ctrl_reg;
61 int result;
62
63 if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)
64 return INV_SUCCESS;
65
66 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
67 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
68 if (result) {
69 LOG_RESULT_LOCATION(result);
70 return result;
71 }
72 user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST;
73 user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST;
74
75 result = inv_serial_single_write(gyro_handle,
76 mldl_cfg->mpu_chip_info->addr,
77 MPUREG_USER_CTRL, user_ctrl_reg);
78 if (result) {
79 LOG_RESULT_LOCATION(result);
80 return result;
81 }
82 mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED;
83
84 return result;
85}
86
87/**
88 * @brief Starts the DMP running
89 *
90 * @return INV_SUCCESS or non-zero error code
91 */
92static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle)
93{
94 unsigned char user_ctrl_reg;
95 int result;
96
97 if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
98 mldl_cfg->mpu_gyro_cfg->dmp_enable)
99 ||
100 ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
101 !mldl_cfg->mpu_gyro_cfg->dmp_enable))
102 return INV_SUCCESS;
103
104 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
105 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
106 if (result) {
107 LOG_RESULT_LOCATION(result);
108 return result;
109 }
110
111 result = inv_serial_single_write(
112 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
113 MPUREG_USER_CTRL,
114 ((user_ctrl_reg & (~BIT_FIFO_EN))
115 | BIT_FIFO_RST));
116 if (result) {
117 LOG_RESULT_LOCATION(result);
118 return result;
119 }
120
121 result = inv_serial_single_write(
122 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
123 MPUREG_USER_CTRL, user_ctrl_reg);
124 if (result) {
125 LOG_RESULT_LOCATION(result);
126 return result;
127 }
128
129 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
130 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
131 if (result) {
132 LOG_RESULT_LOCATION(result);
133 return result;
134 }
135
136 user_ctrl_reg |= BIT_DMP_EN;
137
138 if (mldl_cfg->mpu_gyro_cfg->fifo_enable)
139 user_ctrl_reg |= BIT_FIFO_EN;
140 else
141 user_ctrl_reg &= ~BIT_FIFO_EN;
142
143 user_ctrl_reg |= BIT_DMP_RST;
144
145 result = inv_serial_single_write(
146 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
147 MPUREG_USER_CTRL, user_ctrl_reg);
148 if (result) {
149 LOG_RESULT_LOCATION(result);
150 return result;
151 }
152 mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED;
153
154 return result;
155}
156
157
158
159static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
160 void *mlsl_handle, unsigned char enable)
161{
162 unsigned char b;
163 int result;
164 unsigned char status = mldl_cfg->inv_mpu_state->status;
165
166 if ((status & MPU_GYRO_IS_BYPASSED && enable) ||
167 (!(status & MPU_GYRO_IS_BYPASSED) && !enable))
168 return INV_SUCCESS;
169
170 /*---- get current 'USER_CTRL' into b ----*/
171 result = inv_serial_read(
172 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
173 MPUREG_USER_CTRL, 1, &b);
174 if (result) {
175 LOG_RESULT_LOCATION(result);
176 return result;
177 }
178
179 b &= ~BIT_AUX_IF_EN;
180
181 if (!enable) {
182 result = inv_serial_single_write(
183 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
184 MPUREG_USER_CTRL,
185 (b | BIT_AUX_IF_EN));
186 if (result) {
187 LOG_RESULT_LOCATION(result);
188 return result;
189 }
190 } else {
191 /* Coming out of I2C is tricky due to several erratta. Do not
192 * modify this algorithm
193 */
194 /*
195 * 1) wait for the right time and send the command to change
196 * the aux i2c slave address to an invalid address that will
197 * get nack'ed
198 *
199 * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
200 */
201 result = inv_serial_single_write(
202 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
203 MPUREG_AUX_SLV_ADDR, 0x7F);
204 if (result) {
205 LOG_RESULT_LOCATION(result);
206 return result;
207 }
208 /*
209 * 2) wait enough time for a nack to occur, then go into
210 * bypass mode:
211 */
212 msleep(2);
213 result = inv_serial_single_write(
214 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
215 MPUREG_USER_CTRL, (b));
216 if (result) {
217 LOG_RESULT_LOCATION(result);
218 return result;
219 }
220 /*
221 * 3) wait for up to one MPU cycle then restore the slave
222 * address
223 */
224 msleep(inv_mpu_get_sampling_period_us(mldl_cfg->mpu_gyro_cfg)
225 / 1000);
226 result = inv_serial_single_write(
227 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
228 MPUREG_AUX_SLV_ADDR,
229 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL]
230 ->address);
231 if (result) {
232 LOG_RESULT_LOCATION(result);
233 return result;
234 }
235
236 /*
237 * 4) reset the ime interface
238 */
239
240 result = inv_serial_single_write(
241 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
242 MPUREG_USER_CTRL,
243 (b | BIT_AUX_IF_RST));
244 if (result) {
245 LOG_RESULT_LOCATION(result);
246 return result;
247 }
248 msleep(2);
249 }
250 if (enable)
251 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
252 else
253 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
254
255 return result;
256}
257
258
259/**
260 * @brief enables/disables the I2C bypass to an external device
261 * connected to MPU's secondary I2C bus.
262 * @param enable
263 * Non-zero to enable pass through.
264 * @return INV_SUCCESS if successful, a non-zero error code otherwise.
265 */
266static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
267 unsigned char enable)
268{
269 return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
270}
271
272
273#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
274
275/* NOTE : when not indicated, product revision
276 is considered an 'npp'; non production part */
277
278struct prod_rev_map_t {
279 unsigned char silicon_rev;
280 unsigned short gyro_trim;
281};
282
283#define OLDEST_PROD_REV_SUPPORTED 11
284static struct prod_rev_map_t prod_rev_map[] = {
285 {0, 0},
286 {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */
287 {MPU_SILICON_REV_A4, 131}, /* 2 | */
288 {MPU_SILICON_REV_A4, 131}, /* 3 | */
289 {MPU_SILICON_REV_A4, 131}, /* 4 | */
290 {MPU_SILICON_REV_A4, 131}, /* 5 | */
291 {MPU_SILICON_REV_A4, 131}, /* 6 | */
292 {MPU_SILICON_REV_A4, 131}, /* 7 | */
293 {MPU_SILICON_REV_A4, 131}, /* 8 | */
294 {MPU_SILICON_REV_A4, 131}, /* 9 | */
295 {MPU_SILICON_REV_A4, 131}, /* 10 V */
296 {MPU_SILICON_REV_B1, 131}, /* 11 B1 */
297 {MPU_SILICON_REV_B1, 131}, /* 12 | */
298 {MPU_SILICON_REV_B1, 131}, /* 13 | */
299 {MPU_SILICON_REV_B1, 131}, /* 14 V */
300 {MPU_SILICON_REV_B4, 131}, /* 15 B4 */
301 {MPU_SILICON_REV_B4, 131}, /* 16 | */
302 {MPU_SILICON_REV_B4, 131}, /* 17 | */
303 {MPU_SILICON_REV_B4, 131}, /* 18 | */
304 {MPU_SILICON_REV_B4, 115}, /* 19 | */
305 {MPU_SILICON_REV_B4, 115}, /* 20 V */
306 {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */
307 {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */
308 {MPU_SILICON_REV_B6, 0}, /* 23 B6 */
309 {MPU_SILICON_REV_B6, 0}, /* 24 | */
310 {MPU_SILICON_REV_B6, 0}, /* 25 | */
311 {MPU_SILICON_REV_B6, 131}, /* 26 V (B6/A11) */
312};
313
314/**
315 * @internal
316 * @brief Get the silicon revision ID from OTP for MPU3050.
317 * The silicon revision number is in read from OTP bank 0,
318 * ADDR6[7:2]. The corresponding ID is retrieved by lookup
319 * in a map.
320 *
321 * @param mldl_cfg
322 * a pointer to the mldl config data structure.
323 * @param mlsl_handle
324 * an file handle to the serial communication device the
325 * device is connected to.
326 *
327 * @return 0 on success, a non-zero error code otherwise.
328 */
329static int inv_get_silicon_rev_mpu3050(
330 struct mldl_cfg *mldl_cfg, void *mlsl_handle)
331{
332 int result;
333 unsigned char index = 0x00;
334 unsigned char bank =
335 (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
336 unsigned short mem_addr = ((bank << 8) | 0x06);
337 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
338
339 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
340 MPUREG_PRODUCT_ID, 1,
341 &mpu_chip_info->product_id);
342 if (result) {
343 LOG_RESULT_LOCATION(result);
344 return result;
345 }
346
347 result = inv_serial_read_mem(
348 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
349 mem_addr, 1, &index);
350 if (result) {
351 LOG_RESULT_LOCATION(result);
352 return result;
353 }
354 index >>= 2;
355
356 /* clean the prefetch and cfg user bank bits */
357 result = inv_serial_single_write(
358 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
359 MPUREG_BANK_SEL, 0);
360 if (result) {
361 LOG_RESULT_LOCATION(result);
362 return result;
363 }
364
365 if (index < OLDEST_PROD_REV_SUPPORTED || index >= NUM_OF_PROD_REVS) {
366 mpu_chip_info->silicon_revision = 0;
367 mpu_chip_info->gyro_sens_trim = 0;
368 MPL_LOGE("Unsupported Product Revision Detected : %d\n", index);
369 return INV_ERROR_INVALID_MODULE;
370 }
371
372 mpu_chip_info->product_revision = index;
373 mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev;
374 mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim;
375 if (mpu_chip_info->gyro_sens_trim == 0) {
376 MPL_LOGE("gyro sensitivity trim is 0"
377 " - unsupported non production part.\n");
378 return INV_ERROR_INVALID_MODULE;
379 }
380
381 return result;
382}
383#define inv_get_silicon_rev inv_get_silicon_rev_mpu3050
384
385
386/**
387 * @brief Enable / Disable the use MPU's secondary I2C interface level
388 * shifters.
389 * When enabled the secondary I2C interface to which the external
390 * device is connected runs at VDD voltage (main supply).
391 * When disabled the 2nd interface runs at VDDIO voltage.
392 * See the device specification for more details.
393 *
394 * @note using this API may produce unpredictable results, depending on how
395 * the MPU and slave device are setup on the target platform.
396 * Use of this API should entirely be restricted to system
397 * integrators. Once the correct value is found, there should be no
398 * need to change the level shifter at runtime.
399 *
400 * @pre Must be called after inv_serial_start().
401 * @note Typically called before inv_dmp_open().
402 *
403 * @param[in] enable:
404 * 0 to run at VDDIO (default),
405 * 1 to run at VDD.
406 *
407 * @return INV_SUCCESS if successfull, a non-zero error code otherwise.
408 */
409static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg,
410 void *mlsl_handle, unsigned char enable)
411{
412 int result;
413 unsigned char regval;
414
415 unsigned char reg;
416 unsigned char mask;
417
418 if (0 == mldl_cfg->mpu_chip_info->silicon_revision)
419 return INV_ERROR_INVALID_PARAMETER;
420
421 /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR --
422 NOTE: this is incompatible with ST accelerometers where the VDDIO
423 bit MUST be set to enable ST's internal logic to autoincrement
424 the register address on burst reads --*/
425 if ((mldl_cfg->mpu_chip_info->silicon_revision & 0xf)
426 < MPU_SILICON_REV_B6) {
427 reg = MPUREG_ACCEL_BURST_ADDR;
428 mask = 0x80;
429 } else {
430 /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 =>
431 the mask is always 0x04 --*/
432 reg = MPUREG_FIFO_EN2;
433 mask = 0x04;
434 }
435
436 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
437 reg, 1, &regval);
438 if (result) {
439 LOG_RESULT_LOCATION(result);
440 return result;
441 }
442
443 if (enable)
444 regval |= mask;
445 else
446 regval &= ~mask;
447
448 result = inv_serial_single_write(
449 mlsl_handle, mldl_cfg->mpu_chip_info->addr, reg, regval);
450 if (result) {
451 LOG_RESULT_LOCATION(result);
452 return result;
453 }
454 return result;
455 return INV_SUCCESS;
456}
457
458
459/**
460 * @internal
461 * @brief This function controls the power management on the MPU device.
462 * The entire chip can be put to low power sleep mode, or individual
463 * gyros can be turned on/off.
464 *
465 * Putting the device into sleep mode depending upon the changing needs
466 * of the associated applications is a recommended method for reducing
467 * power consuption. It is a safe opearation in that sleep/wake up of
468 * gyros while running will not result in any interruption of data.
469 *
470 * Although it is entirely allowed to put the device into full sleep
471 * while running the DMP, it is not recomended because it will disrupt
472 * the ongoing calculations carried on inside the DMP and consequently
473 * the sensor fusion algorithm. Furthermore, while in sleep mode
474 * read & write operation from the app processor on both registers and
475 * memory are disabled and can only regained by restoring the MPU in
476 * normal power mode.
477 * Disabling any of the gyro axis will reduce the associated power
478 * consuption from the PLL but will not stop the DMP from running
479 * state.
480 *
481 * @param reset
482 * Non-zero to reset the device. Note that this setting
483 * is volatile and the corresponding register bit will
484 * clear itself right after being applied.
485 * @param sleep
486 * Non-zero to put device into full sleep.
487 * @param disable_gx
488 * Non-zero to disable gyro X.
489 * @param disable_gy
490 * Non-zero to disable gyro Y.
491 * @param disable_gz
492 * Non-zero to disable gyro Z.
493 *
494 * @return INV_SUCCESS if successfull; a non-zero error code otherwise.
495 */
496static int mpu3050_pwr_mgmt(struct mldl_cfg *mldl_cfg,
497 void *mlsl_handle,
498 unsigned char reset,
499 unsigned char sleep,
500 unsigned char disable_gx,
501 unsigned char disable_gy,
502 unsigned char disable_gz)
503{
504 unsigned char b;
505 int result;
506
507 result =
508 inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
509 MPUREG_PWR_MGM, 1, &b);
510 if (result) {
511 LOG_RESULT_LOCATION(result);
512 return result;
513 }
514
515 /* If we are awake, we need to put it in bypass before resetting */
516 if ((!(b & BIT_SLEEP)) && reset)
517 result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
518
519 /* Reset if requested */
520 if (reset) {
521 MPL_LOGV("Reset MPU3050\n");
522 result = inv_serial_single_write(
523 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
524 MPUREG_PWR_MGM, b | BIT_H_RESET);
525 if (result) {
526 LOG_RESULT_LOCATION(result);
527 return result;
528 }
529 msleep(5);
530 /* Some chips are awake after reset and some are asleep,
531 * check the status */
532 result = inv_serial_read(
533 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
534 MPUREG_PWR_MGM, 1, &b);
535 if (result) {
536 LOG_RESULT_LOCATION(result);
537 return result;
538 }
539 }
540
541 /* Update the suspended state just in case we return early */
542 if (b & BIT_SLEEP) {
543 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED;
544 mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED;
545 } else {
546 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED;
547 mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED;
548 }
549
550 /* if power status match requested, nothing else's left to do */
551 if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
552 (((sleep != 0) * BIT_SLEEP) |
553 ((disable_gx != 0) * BIT_STBY_XG) |
554 ((disable_gy != 0) * BIT_STBY_YG) |
555 ((disable_gz != 0) * BIT_STBY_ZG))) {
556 return INV_SUCCESS;
557 }
558
559 /*
560 * This specific transition between states needs to be reinterpreted:
561 * (1,1,1,1) -> (0,1,1,1) has to become
562 * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1)
563 * where
564 * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1)
565 */
566 if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
567 (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
568 && ((!sleep) && disable_gx && disable_gy && disable_gz)) {
569 result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0);
570 if (result)
571 return result;
572 b |= BIT_SLEEP;
573 b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
574 }
575
576 if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) {
577 if (sleep) {
578 result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
579 if (result) {
580 LOG_RESULT_LOCATION(result);
581 return result;
582 }
583 b |= BIT_SLEEP;
584 result =
585 inv_serial_single_write(
586 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
587 MPUREG_PWR_MGM, b);
588 if (result) {
589 LOG_RESULT_LOCATION(result);
590 return result;
591 }
592 mldl_cfg->inv_mpu_state->status |=
593 MPU_GYRO_IS_SUSPENDED;
594 mldl_cfg->inv_mpu_state->status |=
595 MPU_DEVICE_IS_SUSPENDED;
596 } else {
597 b &= ~BIT_SLEEP;
598 result =
599 inv_serial_single_write(
600 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
601 MPUREG_PWR_MGM, b);
602 if (result) {
603 LOG_RESULT_LOCATION(result);
604 return result;
605 }
606 mldl_cfg->inv_mpu_state->status &=
607 ~MPU_GYRO_IS_SUSPENDED;
608 mldl_cfg->inv_mpu_state->status &=
609 ~MPU_DEVICE_IS_SUSPENDED;
610 msleep(5);
611 }
612 }
613 /*---
614 WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE
615 1) put one axis at a time in stand-by
616 ---*/
617 if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) {
618 b ^= BIT_STBY_XG;
619 result = inv_serial_single_write(
620 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
621 MPUREG_PWR_MGM, b);
622 if (result) {
623 LOG_RESULT_LOCATION(result);
624 return result;
625 }
626 }
627 if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) {
628 b ^= BIT_STBY_YG;
629 result = inv_serial_single_write(
630 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
631 MPUREG_PWR_MGM, b);
632 if (result) {
633 LOG_RESULT_LOCATION(result);
634 return result;
635 }
636 }
637 if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) {
638 b ^= BIT_STBY_ZG;
639 result = inv_serial_single_write(
640 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
641 MPUREG_PWR_MGM, b);
642 if (result) {
643 LOG_RESULT_LOCATION(result);
644 return result;
645 }
646 }
647
648 return INV_SUCCESS;
649}
650
651
652/**
653 * @brief sets the clock source for the gyros.
654 * @param mldl_cfg
655 * a pointer to the struct mldl_cfg data structure.
656 * @param gyro_handle
657 * an handle to the serial device the gyro is assigned to.
658 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
659 */
660static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
661{
662 int result;
663 unsigned char cur_clk_src;
664 unsigned char reg;
665
666 /* clock source selection */
667 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
668 MPUREG_PWR_MGM, 1, &reg);
669 if (result) {
670 LOG_RESULT_LOCATION(result);
671 return result;
672 }
673 cur_clk_src = reg & BITS_CLKSEL;
674 reg &= ~BITS_CLKSEL;
675
676
677 result = inv_serial_single_write(
678 gyro_handle, mldl_cfg->mpu_chip_info->addr,
679 MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg);
680 if (result) {
681 LOG_RESULT_LOCATION(result);
682 return result;
683 }
684 /* TODO : workarounds to be determined and implemented */
685
686 return result;
687}
688
689/**
690 * Configures the MPU I2C Master
691 *
692 * @mldl_cfg Handle to the configuration data
693 * @gyro_handle handle to the gyro communictation interface
694 * @slave Can be Null if turning off the slave
695 * @slave_pdata Can be null if turning off the slave
696 * @slave_id enum ext_slave_type to determine which index to use
697 *
698 *
699 * This fucntion configures the slaves by:
700 * 1) Setting up the read
701 * a) Read Register
702 * b) Read Length
703 * 2) Set up the data trigger (MPU6050 only)
704 * a) Set trigger write register
705 * b) Set Trigger write value
706 * 3) Set up the divider (MPU6050 only)
707 * 4) Set the slave bypass mode depending on slave
708 *
709 * returns INV_SUCCESS or non-zero error code
710 */
711static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg,
712 void *gyro_handle,
713 struct ext_slave_descr *slave,
714 struct ext_slave_platform_data *slave_pdata,
715 int slave_id)
716{
717 int result;
718 unsigned char reg;
719 unsigned char slave_reg;
720 unsigned char slave_len;
721 unsigned char slave_endian;
722 unsigned char slave_address;
723
724 if (slave_id != EXT_SLAVE_TYPE_ACCEL)
725 return 0;
726
727 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
728
729 if (NULL == slave || NULL == slave_pdata) {
730 slave_reg = 0;
731 slave_len = 0;
732 slave_endian = 0;
733 slave_address = 0;
734 mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0;
735 } else {
736 slave_reg = slave->read_reg;
737 slave_len = slave->read_len;
738 slave_endian = slave->endian;
739 slave_address = slave_pdata->address;
740 mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 1;
741 }
742
743 /* Address */
744 result = inv_serial_single_write(gyro_handle,
745 mldl_cfg->mpu_chip_info->addr,
746 MPUREG_AUX_SLV_ADDR, slave_address);
747 if (result) {
748 LOG_RESULT_LOCATION(result);
749 return result;
750 }
751 /* Register */
752 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
753 MPUREG_ACCEL_BURST_ADDR, 1, &reg);
754 if (result) {
755 LOG_RESULT_LOCATION(result);
756 return result;
757 }
758 reg = ((reg & 0x80) | slave_reg);
759 result = inv_serial_single_write(gyro_handle,
760 mldl_cfg->mpu_chip_info->addr,
761 MPUREG_ACCEL_BURST_ADDR, reg);
762 if (result) {
763 LOG_RESULT_LOCATION(result);
764 return result;
765 }
766
767 /* Length */
768 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
769 MPUREG_USER_CTRL, 1, &reg);
770 if (result) {
771 LOG_RESULT_LOCATION(result);
772 return result;
773 }
774 reg = (reg & ~BIT_AUX_RD_LENG);
775 result = inv_serial_single_write(
776 gyro_handle, mldl_cfg->mpu_chip_info->addr,
777 MPUREG_USER_CTRL, reg);
778 if (result) {
779 LOG_RESULT_LOCATION(result);
780 return result;
781 }
782
783 return result;
784}
785
786
787static int mpu_set_slave(struct mldl_cfg *mldl_cfg,
788 void *gyro_handle,
789 struct ext_slave_descr *slave,
790 struct ext_slave_platform_data *slave_pdata,
791 int slave_id)
792{
793 return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave,
794 slave_pdata, slave_id);
795}
796/**
797 * Check to see if the gyro was reset by testing a couple of registers known
798 * to change on reset.
799 *
800 * @mldl_cfg mldl configuration structure
801 * @gyro_handle handle used to communicate with the gyro
802 *
803 * @return INV_SUCCESS or non-zero error code
804 */
805static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle)
806{
807 int result = INV_SUCCESS;
808 unsigned char reg;
809
810 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
811 MPUREG_DMP_CFG_2, 1, &reg);
812 if (result) {
813 LOG_RESULT_LOCATION(result);
814 return result;
815 }
816
817 if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg)
818 return true;
819
820 if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1)
821 return false;
822
823 /* Inconclusive assume it was reset */
824 return true;
825}
826
827
828int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
829 const unsigned char *data, int size)
830{
831 int bank, offset, write_size;
832 int result;
833 unsigned char read[MPU_MEM_BANK_SIZE];
834
835 if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) {
836#if INV_CACHE_DMP == 1
837 memcpy(mldl_cfg->mpu_ram->ram, data, size);
838 return INV_SUCCESS;
839#else
840 LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
841 return INV_ERROR_MEMORY_SET;
842#endif
843 }
844
845 if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) {
846 LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
847 return INV_ERROR_MEMORY_SET;
848 }
849 /* Write and verify memory */
850 for (bank = 0; size > 0; bank++,
851 size -= write_size,
852 data += write_size) {
853 if (size > MPU_MEM_BANK_SIZE)
854 write_size = MPU_MEM_BANK_SIZE;
855 else
856 write_size = size;
857
858 result = inv_serial_write_mem(mlsl_handle,
859 mldl_cfg->mpu_chip_info->addr,
860 ((bank << 8) | 0x00),
861 write_size,
862 data);
863 if (result) {
864 LOG_RESULT_LOCATION(result);
865 MPL_LOGE("Write mem error in bank %d\n", bank);
866 return result;
867 }
868 result = inv_serial_read_mem(mlsl_handle,
869 mldl_cfg->mpu_chip_info->addr,
870 ((bank << 8) | 0x00),
871 write_size,
872 read);
873 if (result) {
874 LOG_RESULT_LOCATION(result);
875 MPL_LOGE("Read mem error in bank %d\n", bank);
876 return result;
877 }
878
879#define ML_SKIP_CHECK 20
880 for (offset = 0; offset < write_size; offset++) {
881 /* skip the register memory locations */
882 if (bank == 0 && offset < ML_SKIP_CHECK)
883 continue;
884 if (data[offset] != read[offset]) {
885 result = INV_ERROR_SERIAL_WRITE;
886 break;
887 }
888 }
889 if (result != INV_SUCCESS) {
890 LOG_RESULT_LOCATION(result);
891 MPL_LOGE("Read data mismatch at bank %d, offset %d\n",
892 bank, offset);
893 return result;
894 }
895 }
896 return INV_SUCCESS;
897}
898
899static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
900 unsigned long sensors)
901{
902 int result;
903 int ii;
904 unsigned char reg;
905 unsigned char regs[7];
906
907 /* Wake up the part */
908 result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, false, false,
909 !(sensors & INV_X_GYRO),
910 !(sensors & INV_Y_GYRO),
911 !(sensors & INV_Z_GYRO));
912
913 if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) &&
914 !mpu_was_reset(mldl_cfg, gyro_handle)) {
915 return INV_SUCCESS;
916 }
917
918 if (result) {
919 LOG_RESULT_LOCATION(result);
920 return result;
921 }
922 result = inv_serial_single_write(
923 gyro_handle, mldl_cfg->mpu_chip_info->addr,
924 MPUREG_INT_CFG,
925 (mldl_cfg->mpu_gyro_cfg->int_config |
926 mldl_cfg->pdata->int_config));
927 if (result) {
928 LOG_RESULT_LOCATION(result);
929 return result;
930 }
931 result = inv_serial_single_write(
932 gyro_handle, mldl_cfg->mpu_chip_info->addr,
933 MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider);
934 if (result) {
935 LOG_RESULT_LOCATION(result);
936 return result;
937 }
938 result = mpu_set_clock_source(gyro_handle, mldl_cfg);
939 if (result) {
940 LOG_RESULT_LOCATION(result);
941 return result;
942 }
943
944 reg = DLPF_FS_SYNC_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync,
945 mldl_cfg->mpu_gyro_cfg->full_scale,
946 mldl_cfg->mpu_gyro_cfg->lpf);
947 result = inv_serial_single_write(
948 gyro_handle, mldl_cfg->mpu_chip_info->addr,
949 MPUREG_DLPF_FS_SYNC, reg);
950 if (result) {
951 LOG_RESULT_LOCATION(result);
952 return result;
953 }
954 result = inv_serial_single_write(
955 gyro_handle, mldl_cfg->mpu_chip_info->addr,
956 MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1);
957 if (result) {
958 LOG_RESULT_LOCATION(result);
959 return result;
960 }
961 result = inv_serial_single_write(
962 gyro_handle, mldl_cfg->mpu_chip_info->addr,
963 MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2);
964 if (result) {
965 LOG_RESULT_LOCATION(result);
966 return result;
967 }
968
969 /* Write and verify memory */
970#if INV_CACHE_DMP != 0
971 inv_mpu_set_firmware(mldl_cfg, gyro_handle,
972 mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length);
973#endif
974
975 result = inv_serial_single_write(
976 gyro_handle, mldl_cfg->mpu_chip_info->addr,
977 MPUREG_XG_OFFS_TC, mldl_cfg->mpu_offsets->tc[0]);
978 if (result) {
979 LOG_RESULT_LOCATION(result);
980 return result;
981 }
982 result = inv_serial_single_write(
983 gyro_handle, mldl_cfg->mpu_chip_info->addr,
984 MPUREG_YG_OFFS_TC, mldl_cfg->mpu_offsets->tc[1]);
985 if (result) {
986 LOG_RESULT_LOCATION(result);
987 return result;
988 }
989 result = inv_serial_single_write(
990 gyro_handle, mldl_cfg->mpu_chip_info->addr,
991 MPUREG_ZG_OFFS_TC, mldl_cfg->mpu_offsets->tc[2]);
992 if (result) {
993 LOG_RESULT_LOCATION(result);
994 return result;
995 }
996 regs[0] = MPUREG_X_OFFS_USRH;
997 for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) {
998 regs[1 + ii * 2] =
999 (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8)
1000 & 0xff;
1001 regs[1 + ii * 2 + 1] =
1002 (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff);
1003 }
1004 result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1005 7, regs);
1006 if (result) {
1007 LOG_RESULT_LOCATION(result);
1008 return result;
1009 }
1010
1011 /* Configure slaves */
1012 result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
1013 mldl_cfg->pdata->level_shifter);
1014 if (result) {
1015 LOG_RESULT_LOCATION(result);
1016 return result;
1017 }
1018 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG;
1019
1020 return result;
1021}
1022
1023int gyro_config(void *mlsl_handle,
1024 struct mldl_cfg *mldl_cfg,
1025 struct ext_slave_config *data)
1026{
1027 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
1028 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
1029 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
1030 int ii;
1031
1032 if (!data->data)
1033 return INV_ERROR_INVALID_PARAMETER;
1034
1035 switch (data->key) {
1036 case MPU_SLAVE_INT_CONFIG:
1037 mpu_gyro_cfg->int_config = *((__u8 *)data->data);
1038 break;
1039 case MPU_SLAVE_EXT_SYNC:
1040 mpu_gyro_cfg->ext_sync = *((__u8 *)data->data);
1041 break;
1042 case MPU_SLAVE_FULL_SCALE:
1043 mpu_gyro_cfg->full_scale = *((__u8 *)data->data);
1044 break;
1045 case MPU_SLAVE_LPF:
1046 mpu_gyro_cfg->lpf = *((__u8 *)data->data);
1047 break;
1048 case MPU_SLAVE_CLK_SRC:
1049 mpu_gyro_cfg->clk_src = *((__u8 *)data->data);
1050 break;
1051 case MPU_SLAVE_DIVIDER:
1052 mpu_gyro_cfg->divider = *((__u8 *)data->data);
1053 break;
1054 case MPU_SLAVE_DMP_ENABLE:
1055 mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data);
1056 break;
1057 case MPU_SLAVE_FIFO_ENABLE:
1058 mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data);
1059 break;
1060 case MPU_SLAVE_DMP_CFG1:
1061 mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data);
1062 break;
1063 case MPU_SLAVE_DMP_CFG2:
1064 mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data);
1065 break;
1066 case MPU_SLAVE_TC:
1067 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1068 mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii];
1069 break;
1070 case MPU_SLAVE_GYRO:
1071 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1072 mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii];
1073 break;
1074 case MPU_SLAVE_ADDR:
1075 mpu_chip_info->addr = *((__u8 *)data->data);
1076 break;
1077 case MPU_SLAVE_PRODUCT_REVISION:
1078 mpu_chip_info->product_revision = *((__u8 *)data->data);
1079 break;
1080 case MPU_SLAVE_SILICON_REVISION:
1081 mpu_chip_info->silicon_revision = *((__u8 *)data->data);
1082 break;
1083 case MPU_SLAVE_PRODUCT_ID:
1084 mpu_chip_info->product_id = *((__u8 *)data->data);
1085 break;
1086 case MPU_SLAVE_GYRO_SENS_TRIM:
1087 mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data);
1088 break;
1089 case MPU_SLAVE_ACCEL_SENS_TRIM:
1090 mpu_chip_info->accel_sens_trim = *((__u16 *)data->data);
1091 break;
1092 case MPU_SLAVE_RAM:
1093 if (data->len != mldl_cfg->mpu_ram->length)
1094 return INV_ERROR_INVALID_PARAMETER;
1095
1096 memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len);
1097 break;
1098 default:
1099 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
1100 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1101 };
1102 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG;
1103 return INV_SUCCESS;
1104}
1105
1106int gyro_get_config(void *mlsl_handle,
1107 struct mldl_cfg *mldl_cfg,
1108 struct ext_slave_config *data)
1109{
1110 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
1111 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
1112 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
1113 int ii;
1114
1115 if (!data->data)
1116 return INV_ERROR_INVALID_PARAMETER;
1117
1118 switch (data->key) {
1119 case MPU_SLAVE_INT_CONFIG:
1120 *((__u8 *)data->data) = mpu_gyro_cfg->int_config;
1121 break;
1122 case MPU_SLAVE_EXT_SYNC:
1123 *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync;
1124 break;
1125 case MPU_SLAVE_FULL_SCALE:
1126 *((__u8 *)data->data) = mpu_gyro_cfg->full_scale;
1127 break;
1128 case MPU_SLAVE_LPF:
1129 *((__u8 *)data->data) = mpu_gyro_cfg->lpf;
1130 break;
1131 case MPU_SLAVE_CLK_SRC:
1132 *((__u8 *)data->data) = mpu_gyro_cfg->clk_src;
1133 break;
1134 case MPU_SLAVE_DIVIDER:
1135 *((__u8 *)data->data) = mpu_gyro_cfg->divider;
1136 break;
1137 case MPU_SLAVE_DMP_ENABLE:
1138 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable;
1139 break;
1140 case MPU_SLAVE_FIFO_ENABLE:
1141 *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable;
1142 break;
1143 case MPU_SLAVE_DMP_CFG1:
1144 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1;
1145 break;
1146 case MPU_SLAVE_DMP_CFG2:
1147 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2;
1148 break;
1149 case MPU_SLAVE_TC:
1150 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1151 ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii];
1152 break;
1153 case MPU_SLAVE_GYRO:
1154 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1155 ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii];
1156 break;
1157 case MPU_SLAVE_ADDR:
1158 *((__u8 *)data->data) = mpu_chip_info->addr;
1159 break;
1160 case MPU_SLAVE_PRODUCT_REVISION:
1161 *((__u8 *)data->data) = mpu_chip_info->product_revision;
1162 break;
1163 case MPU_SLAVE_SILICON_REVISION:
1164 *((__u8 *)data->data) = mpu_chip_info->silicon_revision;
1165 break;
1166 case MPU_SLAVE_PRODUCT_ID:
1167 *((__u8 *)data->data) = mpu_chip_info->product_id;
1168 break;
1169 case MPU_SLAVE_GYRO_SENS_TRIM:
1170 *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim;
1171 break;
1172 case MPU_SLAVE_ACCEL_SENS_TRIM:
1173 *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim;
1174 break;
1175 case MPU_SLAVE_RAM:
1176 if (data->len != mldl_cfg->mpu_ram->length)
1177 return INV_ERROR_INVALID_PARAMETER;
1178
1179 memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len);
1180 break;
1181 default:
1182 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
1183 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1184 };
1185
1186 return INV_SUCCESS;
1187}
1188
1189
1190/*******************************************************************************
1191 *******************************************************************************
1192 * Exported functions
1193 *******************************************************************************
1194 ******************************************************************************/
1195
1196/**
1197 * Initializes the pdata structure to defaults.
1198 *
1199 * Opens the device to read silicon revision, product id and whoami.
1200 *
1201 * @mldl_cfg
1202 * The internal device configuration data structure.
1203 * @mlsl_handle
1204 * The serial communication handle.
1205 *
1206 * @return INV_SUCCESS if silicon revision, product id and woami are supported
1207 * by this software.
1208 */
1209int inv_mpu_open(struct mldl_cfg *mldl_cfg,
1210 void *gyro_handle,
1211 void *accel_handle,
1212 void *compass_handle, void *pressure_handle)
1213{
1214 int result;
1215 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1216 int ii;
1217
1218 /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
1219 ii = 0;
1220 mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false;
1221 mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN;
1222 mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
1223 mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ;
1224 mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS;
1225 mldl_cfg->mpu_gyro_cfg->divider = 4;
1226 mldl_cfg->mpu_gyro_cfg->dmp_enable = 1;
1227 mldl_cfg->mpu_gyro_cfg->fifo_enable = 1;
1228 mldl_cfg->mpu_gyro_cfg->ext_sync = 0;
1229 mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0;
1230 mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0;
1231 mldl_cfg->inv_mpu_state->status =
1232 MPU_DMP_IS_SUSPENDED |
1233 MPU_GYRO_IS_SUSPENDED |
1234 MPU_ACCEL_IS_SUSPENDED |
1235 MPU_COMPASS_IS_SUSPENDED |
1236 MPU_PRESSURE_IS_SUSPENDED |
1237 MPU_DEVICE_IS_SUSPENDED;
1238 mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0;
1239
1240 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1241 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1242 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1243 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1244
1245 if (mldl_cfg->mpu_chip_info->addr == 0) {
1246 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
1247 return INV_ERROR_INVALID_PARAMETER;
1248 }
1249
1250 /*
1251 * Reset,
1252 * Take the DMP out of sleep, and
1253 * read the product_id, sillicon rev and whoami
1254 */
1255 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
1256 result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, RESET, 0, 0, 0, 0);
1257 if (result) {
1258 LOG_RESULT_LOCATION(result);
1259 return result;
1260 }
1261
1262 result = inv_get_silicon_rev(mldl_cfg, gyro_handle);
1263 if (result) {
1264 LOG_RESULT_LOCATION(result);
1265 return result;
1266 }
1267
1268 /* Get the factory temperature compensation offsets */
1269 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1270 MPUREG_XG_OFFS_TC, 1,
1271 &mldl_cfg->mpu_offsets->tc[0]);
1272 if (result) {
1273 LOG_RESULT_LOCATION(result);
1274 return result;
1275 }
1276 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1277 MPUREG_YG_OFFS_TC, 1,
1278 &mldl_cfg->mpu_offsets->tc[1]);
1279 if (result) {
1280 LOG_RESULT_LOCATION(result);
1281 return result;
1282 }
1283 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1284 MPUREG_ZG_OFFS_TC, 1,
1285 &mldl_cfg->mpu_offsets->tc[2]);
1286 if (result) {
1287 LOG_RESULT_LOCATION(result);
1288 return result;
1289 }
1290
1291 /* Into bypass mode before sleeping and calling the slaves init */
1292 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
1293 if (result) {
1294 LOG_RESULT_LOCATION(result);
1295 return result;
1296 }
1297 result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
1298 mldl_cfg->pdata->level_shifter);
1299 if (result) {
1300 LOG_RESULT_LOCATION(result);
1301 return result;
1302 }
1303
1304
1305#if INV_CACHE_DMP != 0
1306 result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP, 0, 0, 0);
1307#endif
1308 if (result) {
1309 LOG_RESULT_LOCATION(result);
1310 return result;
1311 }
1312
1313
1314 return result;
1315
1316}
1317
1318/**
1319 * Close the mpu interface
1320 *
1321 * @mldl_cfg pointer to the configuration structure
1322 * @mlsl_handle pointer to the serial layer handle
1323 *
1324 * @return INV_SUCCESS or non-zero error code
1325 */
1326int inv_mpu_close(struct mldl_cfg *mldl_cfg,
1327 void *gyro_handle,
1328 void *accel_handle,
1329 void *compass_handle,
1330 void *pressure_handle)
1331{
1332 return 0;
1333}
1334
1335/**
1336 * @brief resume the MPU device and all the other sensor
1337 * devices from their low power state.
1338 *
1339 * @mldl_cfg
1340 * pointer to the configuration structure
1341 * @gyro_handle
1342 * the main file handle to the MPU device.
1343 * @accel_handle
1344 * an handle to the accelerometer device, if sitting
1345 * onto a separate bus. Can match mlsl_handle if
1346 * the accelerometer device operates on the same
1347 * primary bus of MPU.
1348 * @compass_handle
1349 * an handle to the compass device, if sitting
1350 * onto a separate bus. Can match mlsl_handle if
1351 * the compass device operates on the same
1352 * primary bus of MPU.
1353 * @pressure_handle
1354 * an handle to the pressure sensor device, if sitting
1355 * onto a separate bus. Can match mlsl_handle if
1356 * the pressure sensor device operates on the same
1357 * primary bus of MPU.
1358 * @resume_gyro
1359 * whether resuming the gyroscope device is
1360 * actually needed (if the device supports low power
1361 * mode of some sort).
1362 * @resume_accel
1363 * whether resuming the accelerometer device is
1364 * actually needed (if the device supports low power
1365 * mode of some sort).
1366 * @resume_compass
1367 * whether resuming the compass device is
1368 * actually needed (if the device supports low power
1369 * mode of some sort).
1370 * @resume_pressure
1371 * whether resuming the pressure sensor device is
1372 * actually needed (if the device supports low power
1373 * mode of some sort).
1374 * @return INV_SUCCESS or a non-zero error code.
1375 */
1376int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
1377 void *gyro_handle,
1378 void *accel_handle,
1379 void *compass_handle,
1380 void *pressure_handle,
1381 unsigned long sensors)
1382{
1383 int result = INV_SUCCESS;
1384 int ii;
1385 bool resume_slave[EXT_SLAVE_NUM_TYPES];
1386 bool resume_dmp = sensors & INV_DMP_PROCESSOR;
1387 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1388 resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
1389 (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
1390 resume_slave[EXT_SLAVE_TYPE_ACCEL] =
1391 sensors & INV_THREE_AXIS_ACCEL;
1392 resume_slave[EXT_SLAVE_TYPE_COMPASS] =
1393 sensors & INV_THREE_AXIS_COMPASS;
1394 resume_slave[EXT_SLAVE_TYPE_PRESSURE] =
1395 sensors & INV_THREE_AXIS_PRESSURE;
1396
1397 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1398 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1399 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1400 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1401
1402
1403 mldl_print_cfg(mldl_cfg);
1404
1405 /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */
1406 for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1407 if (resume_slave[ii] &&
1408 ((!mldl_cfg->slave[ii]) ||
1409 (!mldl_cfg->slave[ii]->resume))) {
1410 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
1411 return INV_ERROR_INVALID_PARAMETER;
1412 }
1413 }
1414
1415 if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp)
1416 && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) ||
1417 (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) {
1418 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1419 if (result) {
1420 LOG_RESULT_LOCATION(result);
1421 return result;
1422 }
1423 result = dmp_stop(mldl_cfg, gyro_handle);
1424 if (result) {
1425 LOG_RESULT_LOCATION(result);
1426 return result;
1427 }
1428 result = gyro_resume(mldl_cfg, gyro_handle, sensors);
1429 if (result) {
1430 LOG_RESULT_LOCATION(result);
1431 return result;
1432 }
1433 }
1434
1435 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1436 if (!mldl_cfg->slave[ii] ||
1437 !mldl_cfg->pdata_slave[ii] ||
1438 !resume_slave[ii] ||
1439 !(mldl_cfg->inv_mpu_state->status & (1 << ii)))
1440 continue;
1441
1442 if (EXT_SLAVE_BUS_SECONDARY ==
1443 mldl_cfg->pdata_slave[ii]->bus) {
1444 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
1445 true);
1446 if (result) {
1447 LOG_RESULT_LOCATION(result);
1448 return result;
1449 }
1450 }
1451 result = mldl_cfg->slave[ii]->resume(slave_handle[ii],
1452 mldl_cfg->slave[ii],
1453 mldl_cfg->pdata_slave[ii]);
1454 if (result) {
1455 LOG_RESULT_LOCATION(result);
1456 return result;
1457 }
1458 mldl_cfg->inv_mpu_state->status &= ~(1 << ii);
1459 }
1460
1461 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1462 if (resume_dmp &&
1463 !(mldl_cfg->inv_mpu_state->status & (1 << ii)) &&
1464 mldl_cfg->pdata_slave[ii] &&
1465 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) {
1466 result = mpu_set_slave(mldl_cfg,
1467 gyro_handle,
1468 mldl_cfg->slave[ii],
1469 mldl_cfg->pdata_slave[ii],
1470 mldl_cfg->slave[ii]->type);
1471 if (result) {
1472 LOG_RESULT_LOCATION(result);
1473 return result;
1474 }
1475 }
1476 }
1477
1478 /* Turn on the master i2c iterface if necessary */
1479 if (resume_dmp) {
1480 result = mpu_set_i2c_bypass(
1481 mldl_cfg, gyro_handle,
1482 !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
1483 if (result) {
1484 LOG_RESULT_LOCATION(result);
1485 return result;
1486 }
1487
1488 /* Now start */
1489 result = dmp_start(mldl_cfg, gyro_handle);
1490 if (result) {
1491 LOG_RESULT_LOCATION(result);
1492 return result;
1493 }
1494 }
1495 mldl_cfg->inv_mpu_cfg->requested_sensors = sensors;
1496
1497 return result;
1498}
1499
1500/**
1501 * @brief suspend the MPU device and all the other sensor
1502 * devices into their low power state.
1503 * @mldl_cfg
1504 * a pointer to the struct mldl_cfg internal data
1505 * structure.
1506 * @gyro_handle
1507 * the main file handle to the MPU device.
1508 * @accel_handle
1509 * an handle to the accelerometer device, if sitting
1510 * onto a separate bus. Can match gyro_handle if
1511 * the accelerometer device operates on the same
1512 * primary bus of MPU.
1513 * @compass_handle
1514 * an handle to the compass device, if sitting
1515 * onto a separate bus. Can match gyro_handle if
1516 * the compass device operates on the same
1517 * primary bus of MPU.
1518 * @pressure_handle
1519 * an handle to the pressure sensor device, if sitting
1520 * onto a separate bus. Can match gyro_handle if
1521 * the pressure sensor device operates on the same
1522 * primary bus of MPU.
1523 * @accel
1524 * whether suspending the accelerometer device is
1525 * actually needed (if the device supports low power
1526 * mode of some sort).
1527 * @compass
1528 * whether suspending the compass device is
1529 * actually needed (if the device supports low power
1530 * mode of some sort).
1531 * @pressure
1532 * whether suspending the pressure sensor device is
1533 * actually needed (if the device supports low power
1534 * mode of some sort).
1535 * @return INV_SUCCESS or a non-zero error code.
1536 */
1537int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
1538 void *gyro_handle,
1539 void *accel_handle,
1540 void *compass_handle,
1541 void *pressure_handle,
1542 unsigned long sensors)
1543{
1544 int result = INV_SUCCESS;
1545 int ii;
1546 struct ext_slave_descr **slave = mldl_cfg->slave;
1547 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
1548 bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR);
1549 bool suspend_slave[EXT_SLAVE_NUM_TYPES];
1550 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1551
1552 suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
1553 ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO))
1554 == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
1555 suspend_slave[EXT_SLAVE_TYPE_ACCEL] =
1556 ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL);
1557 suspend_slave[EXT_SLAVE_TYPE_COMPASS] =
1558 ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS);
1559 suspend_slave[EXT_SLAVE_TYPE_PRESSURE] =
1560 ((sensors & INV_THREE_AXIS_PRESSURE) ==
1561 INV_THREE_AXIS_PRESSURE);
1562
1563 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1564 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1565 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1566 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1567
1568 if (suspend_dmp) {
1569 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1570 if (result) {
1571 LOG_RESULT_LOCATION(result);
1572 return result;
1573 }
1574 result = dmp_stop(mldl_cfg, gyro_handle);
1575 if (result) {
1576 LOG_RESULT_LOCATION(result);
1577 return result;
1578 }
1579 }
1580
1581 /* Gyro */
1582 if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] &&
1583 !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) {
1584 result = mpu3050_pwr_mgmt(
1585 mldl_cfg, gyro_handle, 0,
1586 suspend_dmp && suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE],
1587 (unsigned char)(sensors & INV_X_GYRO),
1588 (unsigned char)(sensors & INV_Y_GYRO),
1589 (unsigned char)(sensors & INV_Z_GYRO));
1590 if (result) {
1591 LOG_RESULT_LOCATION(result);
1592 return result;
1593 }
1594 }
1595
1596 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1597 bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii);
1598 if (!slave[ii] || !pdata_slave[ii] ||
1599 is_suspended || !suspend_slave[ii])
1600 continue;
1601
1602 if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
1603 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1604 if (result) {
1605 LOG_RESULT_LOCATION(result);
1606 return result;
1607 }
1608 }
1609 result = slave[ii]->suspend(slave_handle[ii],
1610 slave[ii],
1611 pdata_slave[ii]);
1612 if (result) {
1613 LOG_RESULT_LOCATION(result);
1614 return result;
1615 }
1616 if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
1617 result = mpu_set_slave(mldl_cfg, gyro_handle,
1618 NULL, NULL,
1619 slave[ii]->type);
1620 if (result) {
1621 LOG_RESULT_LOCATION(result);
1622 return result;
1623 }
1624 }
1625 mldl_cfg->inv_mpu_state->status |= (1 << ii);
1626 }
1627
1628 /* Re-enable the i2c master if there are configured slaves and DMP */
1629 if (!suspend_dmp) {
1630 result = mpu_set_i2c_bypass(
1631 mldl_cfg, gyro_handle,
1632 !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
1633 if (result) {
1634 LOG_RESULT_LOCATION(result);
1635 return result;
1636 }
1637 }
1638 mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS;
1639
1640 return result;
1641}
1642
1643int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
1644 void *gyro_handle,
1645 void *slave_handle,
1646 struct ext_slave_descr *slave,
1647 struct ext_slave_platform_data *pdata,
1648 unsigned char *data)
1649{
1650 int result;
1651 int bypass_result;
1652 int remain_bypassed = true;
1653
1654 if (NULL == slave || NULL == slave->read) {
1655 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1656 return INV_ERROR_INVALID_CONFIGURATION;
1657 }
1658
1659 if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1660 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1661 remain_bypassed = false;
1662 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1663 if (result) {
1664 LOG_RESULT_LOCATION(result);
1665 return result;
1666 }
1667 }
1668
1669 result = slave->read(slave_handle, slave, pdata, data);
1670
1671 if (!remain_bypassed) {
1672 bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1673 if (bypass_result) {
1674 LOG_RESULT_LOCATION(bypass_result);
1675 return bypass_result;
1676 }
1677 }
1678 return result;
1679}
1680
1681int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
1682 void *gyro_handle,
1683 void *slave_handle,
1684 struct ext_slave_config *data,
1685 struct ext_slave_descr *slave,
1686 struct ext_slave_platform_data *pdata)
1687{
1688 int result;
1689 int remain_bypassed = true;
1690
1691 if (NULL == slave || NULL == slave->config) {
1692 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1693 return INV_ERROR_INVALID_CONFIGURATION;
1694 }
1695
1696 if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1697 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1698 remain_bypassed = false;
1699 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1700 if (result) {
1701 LOG_RESULT_LOCATION(result);
1702 return result;
1703 }
1704 }
1705
1706 result = slave->config(slave_handle, slave, pdata, data);
1707 if (result) {
1708 LOG_RESULT_LOCATION(result);
1709 return result;
1710 }
1711
1712 if (!remain_bypassed) {
1713 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1714 if (result) {
1715 LOG_RESULT_LOCATION(result);
1716 return result;
1717 }
1718 }
1719 return result;
1720}
1721
1722int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
1723 void *gyro_handle,
1724 void *slave_handle,
1725 struct ext_slave_config *data,
1726 struct ext_slave_descr *slave,
1727 struct ext_slave_platform_data *pdata)
1728{
1729 int result;
1730 int remain_bypassed = true;
1731
1732 if (NULL == slave || NULL == slave->get_config) {
1733 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1734 return INV_ERROR_INVALID_CONFIGURATION;
1735 }
1736
1737 if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1738 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1739 remain_bypassed = false;
1740 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1741 if (result) {
1742 LOG_RESULT_LOCATION(result);
1743 return result;
1744 }
1745 }
1746
1747 result = slave->get_config(slave_handle, slave, pdata, data);
1748 if (result) {
1749 LOG_RESULT_LOCATION(result);
1750 return result;
1751 }
1752
1753 if (!remain_bypassed) {
1754 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1755 if (result) {
1756 LOG_RESULT_LOCATION(result);
1757 return result;
1758 }
1759 }
1760 return result;
1761}
1762
1763/**
1764 * @}
1765 */
diff --git a/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c
new file mode 100644
index 00000000000..e2b8d30ceba
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/mldl_print_cfg.c
@@ -0,0 +1,137 @@
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 MLDL
22 *
23 * @{
24 * @file mldl_print_cfg.c
25 * @brief The Motion Library Driver Layer.
26 */
27
28#include <stddef.h>
29#include "mldl_cfg.h"
30#include "mlsl.h"
31#include "linux/mpu.h"
32
33#include "log.h"
34#undef MPL_LOG_TAG
35#define MPL_LOG_TAG "mldl_print_cfg:"
36
37#undef MPL_LOG_NDEBUG
38#define MPL_LOG_NDEBUG 1
39
40void mldl_print_cfg(struct mldl_cfg *mldl_cfg)
41{
42 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
43 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
44 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
45 struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg;
46 struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state;
47 struct ext_slave_descr **slave = mldl_cfg->slave;
48 struct mpu_platform_data *pdata = mldl_cfg->pdata;
49 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
50 int ii;
51
52 /* mpu_gyro_cfg */
53 MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config);
54 MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync);
55 MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale);
56 MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf);
57 MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src);
58 MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider);
59 MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable);
60 MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable);
61 MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1);
62 MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2);
63 /* mpu_offsets */
64 MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]);
65 MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]);
66 MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]);
67 MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]);
68 MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]);
69 MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]);
70
71 /* mpu_chip_info */
72 MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr);
73
74 MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision);
75 MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision);
76 MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id);
77 MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim);
78
79 MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors);
80 MPL_LOGV("ignore_system_suspend= %04x\n",
81 inv_mpu_cfg->ignore_system_suspend);
82 MPL_LOGV("status = %04x\n", inv_mpu_state->status);
83 MPL_LOGV("i2c_slaves_enabled= %04x\n",
84 inv_mpu_state->i2c_slaves_enabled);
85
86 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
87 if (!slave[ii])
88 continue;
89 MPL_LOGV("SLAVE %d:\n", ii);
90 MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend);
91 MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume);
92 MPL_LOGV(" read = %02x\n", (int)slave[ii]->read);
93 MPL_LOGV(" type = %02x\n", slave[ii]->type);
94 MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg);
95 MPL_LOGV(" len = %02x\n", slave[ii]->read_len);
96 MPL_LOGV(" endian = %02x\n", slave[ii]->endian);
97 MPL_LOGV(" range.mantissa= %02x\n",
98 slave[ii]->range.mantissa);
99 MPL_LOGV(" range.fraction= %02x\n",
100 slave[ii]->range.fraction);
101 }
102
103 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
104 if (!pdata_slave[ii])
105 continue;
106 MPL_LOGV("PDATA_SLAVE[%d]\n", ii);
107 MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq);
108 MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num);
109 MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus);
110 MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address);
111 MPL_LOGV(" orientation=\n"
112 " %2d %2d %2d\n"
113 " %2d %2d %2d\n"
114 " %2d %2d %2d\n",
115 pdata_slave[ii]->orientation[0],
116 pdata_slave[ii]->orientation[1],
117 pdata_slave[ii]->orientation[2],
118 pdata_slave[ii]->orientation[3],
119 pdata_slave[ii]->orientation[4],
120 pdata_slave[ii]->orientation[5],
121 pdata_slave[ii]->orientation[6],
122 pdata_slave[ii]->orientation[7],
123 pdata_slave[ii]->orientation[8]);
124 }
125
126 MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config);
127 MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter);
128 MPL_LOGV("pdata->orientation =\n"
129 " %2d %2d %2d\n"
130 " %2d %2d %2d\n"
131 " %2d %2d %2d\n",
132 pdata->orientation[0], pdata->orientation[1],
133 pdata->orientation[2], pdata->orientation[3],
134 pdata->orientation[4], pdata->orientation[5],
135 pdata->orientation[6], pdata->orientation[7],
136 pdata->orientation[8]);
137}
diff --git a/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c
new file mode 100644
index 00000000000..19adf5182c0
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/mlsl-kernel.c
@@ -0,0 +1,420 @@
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#include "mlsl.h"
21#include <linux/i2c.h>
22#include "log.h"
23#include "mpu3050.h"
24
25static int inv_i2c_write(struct i2c_adapter *i2c_adap,
26 unsigned char address,
27 unsigned int len, unsigned char const *data)
28{
29 struct i2c_msg msgs[1];
30 int res;
31
32 if (!data || !i2c_adap) {
33 LOG_RESULT_LOCATION(-EINVAL);
34 return -EINVAL;
35 }
36
37 msgs[0].addr = address;
38 msgs[0].flags = 0; /* write */
39 msgs[0].buf = (unsigned char *)data;
40 msgs[0].len = len;
41
42 res = i2c_transfer(i2c_adap, msgs, 1);
43 if (res < 1) {
44 if (res == 0)
45 res = -EIO;
46 LOG_RESULT_LOCATION(res);
47 return res;
48 } else
49 return 0;
50}
51
52static int inv_i2c_write_register(struct i2c_adapter *i2c_adap,
53 unsigned char address,
54 unsigned char reg, unsigned char value)
55{
56 unsigned char data[2];
57
58 data[0] = reg;
59 data[1] = value;
60 return inv_i2c_write(i2c_adap, address, 2, data);
61}
62
63static int inv_i2c_read(struct i2c_adapter *i2c_adap,
64 unsigned char address, unsigned char reg,
65 unsigned int len, unsigned char *data)
66{
67 struct i2c_msg msgs[2];
68 int res;
69
70 if (!data || !i2c_adap) {
71 LOG_RESULT_LOCATION(-EINVAL);
72 return -EINVAL;
73 }
74
75 msgs[0].addr = address;
76 msgs[0].flags = 0; /* write */
77 msgs[0].buf = &reg;
78 msgs[0].len = 1;
79
80 msgs[1].addr = address;
81 msgs[1].flags = I2C_M_RD;
82 msgs[1].buf = data;
83 msgs[1].len = len;
84
85 res = i2c_transfer(i2c_adap, msgs, 2);
86 if (res < 2) {
87 if (res >= 0)
88 res = -EIO;
89 LOG_RESULT_LOCATION(res);
90 return res;
91 } else
92 return 0;
93}
94
95static int mpu_memory_read(struct i2c_adapter *i2c_adap,
96 unsigned char mpu_addr,
97 unsigned short mem_addr,
98 unsigned int len, unsigned char *data)
99{
100 unsigned char bank[2];
101 unsigned char addr[2];
102 unsigned char buf;
103
104 struct i2c_msg msgs[4];
105 int res;
106
107 if (!data || !i2c_adap) {
108 LOG_RESULT_LOCATION(-EINVAL);
109 return -EINVAL;
110 }
111
112 bank[0] = MPUREG_BANK_SEL;
113 bank[1] = mem_addr >> 8;
114
115 addr[0] = MPUREG_MEM_START_ADDR;
116 addr[1] = mem_addr & 0xFF;
117
118 buf = MPUREG_MEM_R_W;
119
120 /* write message */
121 msgs[0].addr = mpu_addr;
122 msgs[0].flags = 0;
123 msgs[0].buf = bank;
124 msgs[0].len = sizeof(bank);
125
126 msgs[1].addr = mpu_addr;
127 msgs[1].flags = 0;
128 msgs[1].buf = addr;
129 msgs[1].len = sizeof(addr);
130
131 msgs[2].addr = mpu_addr;
132 msgs[2].flags = 0;
133 msgs[2].buf = &buf;
134 msgs[2].len = 1;
135
136 msgs[3].addr = mpu_addr;
137 msgs[3].flags = I2C_M_RD;
138 msgs[3].buf = data;
139 msgs[3].len = len;
140
141 res = i2c_transfer(i2c_adap, msgs, 4);
142 if (res != 4) {
143 if (res >= 0)
144 res = -EIO;
145 LOG_RESULT_LOCATION(res);
146 return res;
147 } else
148 return 0;
149}
150
151static int mpu_memory_write(struct i2c_adapter *i2c_adap,
152 unsigned char mpu_addr,
153 unsigned short mem_addr,
154 unsigned int len, unsigned char const *data)
155{
156 unsigned char bank[2];
157 unsigned char addr[2];
158 unsigned char buf[513];
159
160 struct i2c_msg msgs[3];
161 int res;
162
163 if (!data || !i2c_adap) {
164 LOG_RESULT_LOCATION(-EINVAL);
165 return -EINVAL;
166 }
167 if (len >= (sizeof(buf) - 1)) {
168 LOG_RESULT_LOCATION(-ENOMEM);
169 return -ENOMEM;
170 }
171
172 bank[0] = MPUREG_BANK_SEL;
173 bank[1] = mem_addr >> 8;
174
175 addr[0] = MPUREG_MEM_START_ADDR;
176 addr[1] = mem_addr & 0xFF;
177
178 buf[0] = MPUREG_MEM_R_W;
179 memcpy(buf + 1, data, len);
180
181 /* write message */
182 msgs[0].addr = mpu_addr;
183 msgs[0].flags = 0;
184 msgs[0].buf = bank;
185 msgs[0].len = sizeof(bank);
186
187 msgs[1].addr = mpu_addr;
188 msgs[1].flags = 0;
189 msgs[1].buf = addr;
190 msgs[1].len = sizeof(addr);
191
192 msgs[2].addr = mpu_addr;
193 msgs[2].flags = 0;
194 msgs[2].buf = (unsigned char *)buf;
195 msgs[2].len = len + 1;
196
197 res = i2c_transfer(i2c_adap, msgs, 3);
198 if (res != 3) {
199 if (res >= 0)
200 res = -EIO;
201 LOG_RESULT_LOCATION(res);
202 return res;
203 } else
204 return 0;
205}
206
207int inv_serial_single_write(
208 void *sl_handle,
209 unsigned char slave_addr,
210 unsigned char register_addr,
211 unsigned char data)
212{
213 return inv_i2c_write_register((struct i2c_adapter *)sl_handle,
214 slave_addr, register_addr, data);
215}
216EXPORT_SYMBOL(inv_serial_single_write);
217
218int inv_serial_write(
219 void *sl_handle,
220 unsigned char slave_addr,
221 unsigned short length,
222 unsigned char const *data)
223{
224 int result;
225 const unsigned short data_length = length - 1;
226 const unsigned char start_reg_addr = data[0];
227 unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
228 unsigned short bytes_written = 0;
229
230 while (bytes_written < data_length) {
231 unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE,
232 data_length - bytes_written);
233 if (bytes_written == 0) {
234 result = inv_i2c_write((struct i2c_adapter *)
235 sl_handle, slave_addr,
236 1 + this_len, data);
237 } else {
238 /* manually increment register addr between chunks */
239 i2c_write[0] = start_reg_addr + bytes_written;
240 memcpy(&i2c_write[1], &data[1 + bytes_written],
241 this_len);
242 result = inv_i2c_write((struct i2c_adapter *)
243 sl_handle, slave_addr,
244 1 + this_len, i2c_write);
245 }
246 if (result) {
247 LOG_RESULT_LOCATION(result);
248 return result;
249 }
250 bytes_written += this_len;
251 }
252 return 0;
253}
254EXPORT_SYMBOL(inv_serial_write);
255
256int inv_serial_read(
257 void *sl_handle,
258 unsigned char slave_addr,
259 unsigned char register_addr,
260 unsigned short length,
261 unsigned char *data)
262{
263 int result;
264 unsigned short bytes_read = 0;
265
266 if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR
267 && (register_addr == MPUREG_FIFO_R_W ||
268 register_addr == MPUREG_MEM_R_W)) {
269 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
270 return INV_ERROR_INVALID_PARAMETER;
271 }
272
273 while (bytes_read < length) {
274 unsigned short this_len =
275 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
276 result = inv_i2c_read((struct i2c_adapter *)sl_handle,
277 slave_addr, register_addr + bytes_read,
278 this_len, &data[bytes_read]);
279 if (result) {
280 LOG_RESULT_LOCATION(result);
281 return result;
282 }
283 bytes_read += this_len;
284 }
285 return 0;
286}
287EXPORT_SYMBOL(inv_serial_read);
288
289int inv_serial_write_mem(
290 void *sl_handle,
291 unsigned char slave_addr,
292 unsigned short mem_addr,
293 unsigned short length,
294 unsigned char const *data)
295{
296 int result;
297 unsigned short bytes_written = 0;
298
299 if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
300 pr_err("memory read length (%d B) extends beyond its"
301 " limits (%d) if started at location %d\n", length,
302 MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
303 return INV_ERROR_INVALID_PARAMETER;
304 }
305 while (bytes_written < length) {
306 unsigned short this_len =
307 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
308 result = mpu_memory_write((struct i2c_adapter *)sl_handle,
309 slave_addr, mem_addr + bytes_written,
310 this_len, &data[bytes_written]);
311 if (result) {
312 LOG_RESULT_LOCATION(result);
313 return result;
314 }
315 bytes_written += this_len;
316 }
317 return 0;
318}
319EXPORT_SYMBOL(inv_serial_write_mem);
320
321int inv_serial_read_mem(
322 void *sl_handle,
323 unsigned char slave_addr,
324 unsigned short mem_addr,
325 unsigned short length,
326 unsigned char *data)
327{
328 int result;
329 unsigned short bytes_read = 0;
330
331 if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
332 printk
333 ("memory read length (%d B) extends beyond its limits (%d) "
334 "if started at location %d\n", length,
335 MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
336 return INV_ERROR_INVALID_PARAMETER;
337 }
338 while (bytes_read < length) {
339 unsigned short this_len =
340 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
341 result =
342 mpu_memory_read((struct i2c_adapter *)sl_handle,
343 slave_addr, mem_addr + bytes_read,
344 this_len, &data[bytes_read]);
345 if (result) {
346 LOG_RESULT_LOCATION(result);
347 return result;
348 }
349 bytes_read += this_len;
350 }
351 return 0;
352}
353EXPORT_SYMBOL(inv_serial_read_mem);
354
355int inv_serial_write_fifo(
356 void *sl_handle,
357 unsigned char slave_addr,
358 unsigned short length,
359 unsigned char const *data)
360{
361 int result;
362 unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
363 unsigned short bytes_written = 0;
364
365 if (length > FIFO_HW_SIZE) {
366 printk(KERN_ERR
367 "maximum fifo write length is %d\n", FIFO_HW_SIZE);
368 return INV_ERROR_INVALID_PARAMETER;
369 }
370 while (bytes_written < length) {
371 unsigned short this_len =
372 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
373 i2c_write[0] = MPUREG_FIFO_R_W;
374 memcpy(&i2c_write[1], &data[bytes_written], this_len);
375 result = inv_i2c_write((struct i2c_adapter *)sl_handle,
376 slave_addr, this_len + 1, i2c_write);
377 if (result) {
378 LOG_RESULT_LOCATION(result);
379 return result;
380 }
381 bytes_written += this_len;
382 }
383 return 0;
384}
385EXPORT_SYMBOL(inv_serial_write_fifo);
386
387int inv_serial_read_fifo(
388 void *sl_handle,
389 unsigned char slave_addr,
390 unsigned short length,
391 unsigned char *data)
392{
393 int result;
394 unsigned short bytes_read = 0;
395
396 if (length > FIFO_HW_SIZE) {
397 printk(KERN_ERR
398 "maximum fifo read length is %d\n", FIFO_HW_SIZE);
399 return INV_ERROR_INVALID_PARAMETER;
400 }
401 while (bytes_read < length) {
402 unsigned short this_len =
403 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
404 result = inv_i2c_read((struct i2c_adapter *)sl_handle,
405 slave_addr, MPUREG_FIFO_R_W, this_len,
406 &data[bytes_read]);
407 if (result) {
408 LOG_RESULT_LOCATION(result);
409 return result;
410 }
411 bytes_read += this_len;
412 }
413
414 return 0;
415}
416EXPORT_SYMBOL(inv_serial_read_fifo);
417
418/**
419 * @}
420 */
diff --git a/drivers/misc/inv_mpu/mpu3050/mpu-dev.c b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c
new file mode 100644
index 00000000000..c98ed3b4a80
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050/mpu-dev.c
@@ -0,0 +1,1250 @@
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#include <linux/i2c.h>
20#include <linux/i2c-dev.h>
21#include <linux/interrupt.h>
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/kernel.h>
25#include <linux/init.h>
26#include <linux/stat.h>
27#include <linux/irq.h>
28#include <linux/gpio.h>
29#include <linux/signal.h>
30#include <linux/miscdevice.h>
31#include <linux/slab.h>
32#include <linux/version.h>
33#include <linux/pm.h>
34#include <linux/mutex.h>
35#include <linux/suspend.h>
36#include <linux/poll.h>
37
38#include <linux/errno.h>
39#include <linux/fs.h>
40#include <linux/mm.h>
41#include <linux/sched.h>
42#include <linux/wait.h>
43#include <linux/uaccess.h>
44#include <linux/io.h>
45
46#include "mpuirq.h"
47#include "slaveirq.h"
48#include "mlsl.h"
49#include "mldl_cfg.h"
50#include <linux/mpu.h>
51
52
53/* Platform data for the MPU */
54struct mpu_private_data {
55 struct miscdevice dev;
56 struct i2c_client *client;
57
58 /* mldl_cfg data */
59 struct mldl_cfg mldl_cfg;
60 struct mpu_ram mpu_ram;
61 struct mpu_gyro_cfg mpu_gyro_cfg;
62 struct mpu_offsets mpu_offsets;
63 struct mpu_chip_info mpu_chip_info;
64 struct inv_mpu_cfg inv_mpu_cfg;
65 struct inv_mpu_state inv_mpu_state;
66
67 struct mutex mutex;
68 wait_queue_head_t mpu_event_wait;
69 struct completion completion;
70 struct timer_list timeout;
71 struct notifier_block nb;
72 struct mpuirq_data mpu_pm_event;
73 int response_timeout; /* In seconds */
74 unsigned long event;
75 int pid;
76 struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
77};
78
79struct mpu_private_data *mpu_private_data;
80
81static void mpu_pm_timeout(u_long data)
82{
83 struct mpu_private_data *mpu = (struct mpu_private_data *)data;
84 struct i2c_client *client = mpu->client;
85 dev_dbg(&client->adapter->dev, "%s\n", __func__);
86 complete(&mpu->completion);
87}
88
89static int mpu_pm_notifier_callback(struct notifier_block *nb,
90 unsigned long event, void *unused)
91{
92 struct mpu_private_data *mpu =
93 container_of(nb, struct mpu_private_data, nb);
94 struct i2c_client *client = mpu->client;
95 struct timeval event_time;
96 dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
97
98 /* Prevent the file handle from being closed before we initialize
99 the completion event */
100 mutex_lock(&mpu->mutex);
101 if (!(mpu->pid) ||
102 (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
103 mutex_unlock(&mpu->mutex);
104 return NOTIFY_OK;
105 }
106
107 if (event == PM_SUSPEND_PREPARE)
108 mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
109 if (event == PM_POST_SUSPEND)
110 mpu->event = MPU_PM_EVENT_POST_SUSPEND;
111
112 do_gettimeofday(&event_time);
113 mpu->mpu_pm_event.interruptcount++;
114 mpu->mpu_pm_event.irqtime =
115 (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
116 mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
117 mpu->mpu_pm_event.data = mpu->event;
118
119 if (mpu->response_timeout > 0) {
120 mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
121 add_timer(&mpu->timeout);
122 }
123 INIT_COMPLETION(mpu->completion);
124 mutex_unlock(&mpu->mutex);
125
126 wake_up_interruptible(&mpu->mpu_event_wait);
127 wait_for_completion(&mpu->completion);
128 del_timer_sync(&mpu->timeout);
129 dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
130 return NOTIFY_OK;
131}
132
133static int mpu_dev_open(struct inode *inode, struct file *file)
134{
135 struct mpu_private_data *mpu =
136 container_of(file->private_data, struct mpu_private_data, dev);
137 struct i2c_client *client = mpu->client;
138 int result;
139 int ii;
140 dev_dbg(&client->adapter->dev, "%s\n", __func__);
141 dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
142
143 result = mutex_lock_interruptible(&mpu->mutex);
144 if (mpu->pid) {
145 mutex_unlock(&mpu->mutex);
146 return -EBUSY;
147 }
148 mpu->pid = current->pid;
149
150 /* Reset the sensors to the default */
151 if (result) {
152 dev_err(&client->adapter->dev,
153 "%s: mutex_lock_interruptible returned %d\n",
154 __func__, result);
155 return result;
156 }
157
158 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
159 __module_get(mpu->slave_modules[ii]);
160
161 mutex_unlock(&mpu->mutex);
162 return 0;
163}
164
165/* close function - called when the "file" /dev/mpu is closed in userspace */
166static int mpu_release(struct inode *inode, struct file *file)
167{
168 struct mpu_private_data *mpu =
169 container_of(file->private_data, struct mpu_private_data, dev);
170 struct i2c_client *client = mpu->client;
171 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
172 int result = 0;
173 int ii;
174 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
175 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
176
177 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
178 if (!pdata_slave[ii])
179 slave_adapter[ii] = NULL;
180 else
181 slave_adapter[ii] =
182 i2c_get_adapter(pdata_slave[ii]->adapt_num);
183 }
184 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
185
186 mutex_lock(&mpu->mutex);
187 mldl_cfg->inv_mpu_cfg->requested_sensors = 0;
188 result = inv_mpu_suspend(mldl_cfg,
189 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
190 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
191 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
192 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
193 INV_ALL_SENSORS);
194 mpu->pid = 0;
195 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
196 module_put(mpu->slave_modules[ii]);
197
198 mutex_unlock(&mpu->mutex);
199 complete(&mpu->completion);
200 dev_dbg(&client->adapter->dev, "mpu_release\n");
201
202 return result;
203}
204
205/* read function called when from /dev/mpu is read. Read from the FIFO */
206static ssize_t mpu_read(struct file *file,
207 char __user *buf, size_t count, loff_t *offset)
208{
209 struct mpu_private_data *mpu =
210 container_of(file->private_data, struct mpu_private_data, dev);
211 struct i2c_client *client = mpu->client;
212 size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
213 int err;
214
215 if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
216 wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
217
218 if (!mpu->event || !buf
219 || count < sizeof(mpu->mpu_pm_event))
220 return 0;
221
222 err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
223 if (err) {
224 dev_err(&client->adapter->dev,
225 "Copy to user returned %d\n", err);
226 return -EFAULT;
227 }
228 mpu->event = 0;
229 return len;
230}
231
232static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
233{
234 struct mpu_private_data *mpu =
235 container_of(file->private_data, struct mpu_private_data, dev);
236 int mask = 0;
237
238 poll_wait(file, &mpu->mpu_event_wait, poll);
239 if (mpu->event)
240 mask |= POLLIN | POLLRDNORM;
241 return mask;
242}
243
244static int mpu_dev_ioctl_get_ext_slave_platform_data(
245 struct i2c_client *client,
246 struct ext_slave_platform_data __user *arg)
247{
248 struct mpu_private_data *mpu =
249 (struct mpu_private_data *)i2c_get_clientdata(client);
250 struct ext_slave_platform_data *pdata_slave;
251 struct ext_slave_platform_data local_pdata_slave;
252
253 if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave)))
254 return -EFAULT;
255
256 if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES)
257 return -EINVAL;
258
259 pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type];
260 /* All but private data and irq_data */
261 if (!pdata_slave)
262 return -ENODEV;
263 if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave)))
264 return -EFAULT;
265 return 0;
266}
267
268static int mpu_dev_ioctl_get_mpu_platform_data(
269 struct i2c_client *client,
270 struct mpu_platform_data __user *arg)
271{
272 struct mpu_private_data *mpu =
273 (struct mpu_private_data *)i2c_get_clientdata(client);
274 struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
275
276 if (copy_to_user(arg, pdata, sizeof(*pdata)))
277 return -EFAULT;
278 return 0;
279}
280
281static int mpu_dev_ioctl_get_ext_slave_descr(
282 struct i2c_client *client,
283 struct ext_slave_descr __user *arg)
284{
285 struct mpu_private_data *mpu =
286 (struct mpu_private_data *)i2c_get_clientdata(client);
287 struct ext_slave_descr *slave;
288 struct ext_slave_descr local_slave;
289
290 if (copy_from_user(&local_slave, arg, sizeof(local_slave)))
291 return -EFAULT;
292
293 if (local_slave.type >= EXT_SLAVE_NUM_TYPES)
294 return -EINVAL;
295
296 slave = mpu->mldl_cfg.slave[local_slave.type];
297 /* All but private data and irq_data */
298 if (!slave)
299 return -ENODEV;
300 if (copy_to_user(arg, slave, sizeof(*slave)))
301 return -EFAULT;
302 return 0;
303}
304
305
306/**
307 * slave_config() - Pass a requested slave configuration to the slave sensor
308 *
309 * @adapter the adaptor to use to communicate with the slave
310 * @mldl_cfg the mldl configuration structuer
311 * @slave pointer to the slave descriptor
312 * @usr_config The configuration to pass to the slave sensor
313 *
314 * returns 0 or non-zero error code
315 */
316static int inv_mpu_config(struct mldl_cfg *mldl_cfg,
317 void *gyro_adapter,
318 struct ext_slave_config __user *usr_config)
319{
320 int retval = 0;
321 struct ext_slave_config config;
322
323 retval = copy_from_user(&config, usr_config, sizeof(config));
324 if (retval)
325 return -EFAULT;
326
327 if (config.len && config.data) {
328 void *data;
329 data = kmalloc(config.len, GFP_KERNEL);
330 if (!data)
331 return -ENOMEM;
332
333 retval = copy_from_user(data,
334 (void __user *)config.data, config.len);
335 if (retval) {
336 retval = -EFAULT;
337 kfree(data);
338 return retval;
339 }
340 config.data = data;
341 }
342 retval = gyro_config(gyro_adapter, mldl_cfg, &config);
343 kfree(config.data);
344 return retval;
345}
346
347static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg,
348 void *gyro_adapter,
349 struct ext_slave_config __user *usr_config)
350{
351 int retval = 0;
352 struct ext_slave_config config;
353 void *user_data;
354
355 retval = copy_from_user(&config, usr_config, sizeof(config));
356 if (retval)
357 return -EFAULT;
358
359 user_data = config.data;
360 if (config.len && config.data) {
361 void *data;
362 data = kmalloc(config.len, GFP_KERNEL);
363 if (!data)
364 return -ENOMEM;
365
366 retval = copy_from_user(data,
367 (void __user *)config.data, config.len);
368 if (retval) {
369 retval = -EFAULT;
370 kfree(data);
371 return retval;
372 }
373 config.data = data;
374 }
375 retval = gyro_get_config(gyro_adapter, mldl_cfg, &config);
376 if (!retval)
377 retval = copy_to_user((unsigned char __user *)user_data,
378 config.data, config.len);
379 kfree(config.data);
380 return retval;
381}
382
383static int slave_config(struct mldl_cfg *mldl_cfg,
384 void *gyro_adapter,
385 void *slave_adapter,
386 struct ext_slave_descr *slave,
387 struct ext_slave_platform_data *pdata,
388 struct ext_slave_config __user *usr_config)
389{
390 int retval = 0;
391 struct ext_slave_config config;
392 if ((!slave) || (!slave->config))
393 return -ENODEV;
394
395 retval = copy_from_user(&config, usr_config, sizeof(config));
396 if (retval)
397 return -EFAULT;
398
399 if (config.len && config.data) {
400 void *data;
401 data = kmalloc(config.len, GFP_KERNEL);
402 if (!data)
403 return -ENOMEM;
404
405 retval = copy_from_user(data,
406 (void __user *)config.data, config.len);
407 if (retval) {
408 retval = -EFAULT;
409 kfree(data);
410 return retval;
411 }
412 config.data = data;
413 }
414 retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
415 &config, slave, pdata);
416 kfree(config.data);
417 return retval;
418}
419
420static int slave_get_config(struct mldl_cfg *mldl_cfg,
421 void *gyro_adapter,
422 void *slave_adapter,
423 struct ext_slave_descr *slave,
424 struct ext_slave_platform_data *pdata,
425 struct ext_slave_config __user *usr_config)
426{
427 int retval = 0;
428 struct ext_slave_config config;
429 void *user_data;
430 if (!(slave) || !(slave->get_config))
431 return -ENODEV;
432
433 retval = copy_from_user(&config, usr_config, sizeof(config));
434 if (retval)
435 return -EFAULT;
436
437 user_data = config.data;
438 if (config.len && config.data) {
439 void *data;
440 data = kmalloc(config.len, GFP_KERNEL);
441 if (!data)
442 return -ENOMEM;
443
444 retval = copy_from_user(data,
445 (void __user *)config.data, config.len);
446 if (retval) {
447 retval = -EFAULT;
448 kfree(data);
449 return retval;
450 }
451 config.data = data;
452 }
453 retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
454 slave_adapter, &config, slave, pdata);
455 if (retval) {
456 kfree(config.data);
457 return retval;
458 }
459 retval = copy_to_user((unsigned char __user *)user_data,
460 config.data, config.len);
461 kfree(config.data);
462 return retval;
463}
464
465static int inv_slave_read(struct mldl_cfg *mldl_cfg,
466 void *gyro_adapter,
467 void *slave_adapter,
468 struct ext_slave_descr *slave,
469 struct ext_slave_platform_data *pdata,
470 void __user *usr_data)
471{
472 int retval;
473 unsigned char *data;
474 data = kzalloc(slave->read_len, GFP_KERNEL);
475 if (!data)
476 return -EFAULT;
477
478 retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
479 slave, pdata, data);
480
481 if ((!retval) &&
482 (copy_to_user((unsigned char __user *)usr_data,
483 data, slave->read_len)))
484 retval = -EFAULT;
485
486 kfree(data);
487 return retval;
488}
489
490static int mpu_handle_mlsl(void *sl_handle,
491 unsigned char addr,
492 unsigned int cmd,
493 struct mpu_read_write __user *usr_msg)
494{
495 int retval = 0;
496 struct mpu_read_write msg;
497 unsigned char *user_data;
498 retval = copy_from_user(&msg, usr_msg, sizeof(msg));
499 if (retval)
500 return -EFAULT;
501
502 user_data = msg.data;
503 if (msg.length && msg.data) {
504 unsigned char *data;
505 data = kmalloc(msg.length, GFP_KERNEL);
506 if (!data)
507 return -ENOMEM;
508
509 retval = copy_from_user(data,
510 (void __user *)msg.data, msg.length);
511 if (retval) {
512 retval = -EFAULT;
513 kfree(data);
514 return retval;
515 }
516 msg.data = data;
517 } else {
518 return -EPERM;
519 }
520
521 switch (cmd) {
522 case MPU_READ:
523 retval = inv_serial_read(sl_handle, addr,
524 msg.address, msg.length, msg.data);
525 break;
526 case MPU_WRITE:
527 retval = inv_serial_write(sl_handle, addr,
528 msg.length, msg.data);
529 break;
530 case MPU_READ_MEM:
531 retval = inv_serial_read_mem(sl_handle, addr,
532 msg.address, msg.length, msg.data);
533 break;
534 case MPU_WRITE_MEM:
535 retval = inv_serial_write_mem(sl_handle, addr,
536 msg.address, msg.length,
537 msg.data);
538 break;
539 case MPU_READ_FIFO:
540 retval = inv_serial_read_fifo(sl_handle, addr,
541 msg.length, msg.data);
542 break;
543 case MPU_WRITE_FIFO:
544 retval = inv_serial_write_fifo(sl_handle, addr,
545 msg.length, msg.data);
546 break;
547
548 };
549 if (retval) {
550 dev_err(&((struct i2c_adapter *)sl_handle)->dev,
551 "%s: i2c %d error %d\n",
552 __func__, cmd, retval);
553 kfree(msg.data);
554 return retval;
555 }
556 retval = copy_to_user((unsigned char __user *)user_data,
557 msg.data, msg.length);
558 kfree(msg.data);
559 return retval;
560}
561
562/* ioctl - I/O control */
563static long mpu_dev_ioctl(struct file *file,
564 unsigned int cmd, unsigned long arg)
565{
566 struct mpu_private_data *mpu =
567 container_of(file->private_data, struct mpu_private_data, dev);
568 struct i2c_client *client = mpu->client;
569 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
570 int retval = 0;
571 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
572 struct ext_slave_descr **slave = mldl_cfg->slave;
573 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
574 int ii;
575
576 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
577 if (!pdata_slave[ii])
578 slave_adapter[ii] = NULL;
579 else
580 slave_adapter[ii] =
581 i2c_get_adapter(pdata_slave[ii]->adapt_num);
582 }
583 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
584
585 retval = mutex_lock_interruptible(&mpu->mutex);
586 if (retval) {
587 dev_err(&client->adapter->dev,
588 "%s: mutex_lock_interruptible returned %d\n",
589 __func__, retval);
590 return retval;
591 }
592
593 switch (cmd) {
594 case MPU_GET_EXT_SLAVE_PLATFORM_DATA:
595 retval = mpu_dev_ioctl_get_ext_slave_platform_data(
596 client,
597 (struct ext_slave_platform_data __user *)arg);
598 break;
599 case MPU_GET_MPU_PLATFORM_DATA:
600 retval = mpu_dev_ioctl_get_mpu_platform_data(
601 client,
602 (struct mpu_platform_data __user *)arg);
603 break;
604 case MPU_GET_EXT_SLAVE_DESCR:
605 retval = mpu_dev_ioctl_get_ext_slave_descr(
606 client,
607 (struct ext_slave_descr __user *)arg);
608 break;
609 case MPU_READ:
610 case MPU_WRITE:
611 case MPU_READ_MEM:
612 case MPU_WRITE_MEM:
613 case MPU_READ_FIFO:
614 case MPU_WRITE_FIFO:
615 retval = mpu_handle_mlsl(
616 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
617 mldl_cfg->mpu_chip_info->addr, cmd,
618 (struct mpu_read_write __user *)arg);
619 break;
620 case MPU_CONFIG_GYRO:
621 retval = inv_mpu_config(
622 mldl_cfg,
623 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
624 (struct ext_slave_config __user *)arg);
625 break;
626 case MPU_CONFIG_ACCEL:
627 retval = slave_config(
628 mldl_cfg,
629 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
630 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
631 slave[EXT_SLAVE_TYPE_ACCEL],
632 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
633 (struct ext_slave_config __user *)arg);
634 break;
635 case MPU_CONFIG_COMPASS:
636 retval = slave_config(
637 mldl_cfg,
638 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
639 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
640 slave[EXT_SLAVE_TYPE_COMPASS],
641 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
642 (struct ext_slave_config __user *)arg);
643 break;
644 case MPU_CONFIG_PRESSURE:
645 retval = slave_config(
646 mldl_cfg,
647 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
648 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
649 slave[EXT_SLAVE_TYPE_PRESSURE],
650 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
651 (struct ext_slave_config __user *)arg);
652 break;
653 case MPU_GET_CONFIG_GYRO:
654 retval = inv_mpu_get_config(
655 mldl_cfg,
656 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
657 (struct ext_slave_config __user *)arg);
658 break;
659 case MPU_GET_CONFIG_ACCEL:
660 retval = slave_get_config(
661 mldl_cfg,
662 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
663 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
664 slave[EXT_SLAVE_TYPE_ACCEL],
665 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
666 (struct ext_slave_config __user *)arg);
667 break;
668 case MPU_GET_CONFIG_COMPASS:
669 retval = slave_get_config(
670 mldl_cfg,
671 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
672 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
673 slave[EXT_SLAVE_TYPE_COMPASS],
674 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
675 (struct ext_slave_config __user *)arg);
676 break;
677 case MPU_GET_CONFIG_PRESSURE:
678 retval = slave_get_config(
679 mldl_cfg,
680 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
681 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
682 slave[EXT_SLAVE_TYPE_PRESSURE],
683 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
684 (struct ext_slave_config __user *)arg);
685 break;
686 case MPU_SUSPEND:
687 retval = inv_mpu_suspend(
688 mldl_cfg,
689 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
690 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
691 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
692 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
693 arg);
694 break;
695 case MPU_RESUME:
696 retval = inv_mpu_resume(
697 mldl_cfg,
698 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
699 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
700 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
701 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
702 arg);
703 break;
704 case MPU_PM_EVENT_HANDLED:
705 dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
706 complete(&mpu->completion);
707 break;
708 case MPU_READ_ACCEL:
709 retval = inv_slave_read(
710 mldl_cfg,
711 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
712 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
713 slave[EXT_SLAVE_TYPE_ACCEL],
714 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
715 (unsigned char __user *)arg);
716 break;
717 case MPU_READ_COMPASS:
718 retval = inv_slave_read(
719 mldl_cfg,
720 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
721 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
722 slave[EXT_SLAVE_TYPE_COMPASS],
723 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
724 (unsigned char __user *)arg);
725 break;
726 case MPU_READ_PRESSURE:
727 retval = inv_slave_read(
728 mldl_cfg,
729 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
730 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
731 slave[EXT_SLAVE_TYPE_PRESSURE],
732 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
733 (unsigned char __user *)arg);
734 break;
735 case MPU_GET_REQUESTED_SENSORS:
736 if (copy_to_user(
737 (__u32 __user *)arg,
738 &mldl_cfg->inv_mpu_cfg->requested_sensors,
739 sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors)))
740 retval = -EFAULT;
741 break;
742 case MPU_SET_REQUESTED_SENSORS:
743 mldl_cfg->inv_mpu_cfg->requested_sensors = arg;
744 break;
745 case MPU_GET_IGNORE_SYSTEM_SUSPEND:
746 if (copy_to_user(
747 (unsigned char __user *)arg,
748 &mldl_cfg->inv_mpu_cfg->ignore_system_suspend,
749 sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend)))
750 retval = -EFAULT;
751 break;
752 case MPU_SET_IGNORE_SYSTEM_SUSPEND:
753 mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg;
754 break;
755 case MPU_GET_MLDL_STATUS:
756 if (copy_to_user(
757 (unsigned char __user *)arg,
758 &mldl_cfg->inv_mpu_state->status,
759 sizeof(mldl_cfg->inv_mpu_state->status)))
760 retval = -EFAULT;
761 break;
762 case MPU_GET_I2C_SLAVES_ENABLED:
763 if (copy_to_user(
764 (unsigned char __user *)arg,
765 &mldl_cfg->inv_mpu_state->i2c_slaves_enabled,
766 sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)))
767 retval = -EFAULT;
768 break;
769 default:
770 dev_err(&client->adapter->dev,
771 "%s: Unknown cmd %x, arg %lu\n",
772 __func__, cmd, arg);
773 retval = -EINVAL;
774 };
775
776 mutex_unlock(&mpu->mutex);
777 dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n",
778 __func__, cmd, arg, retval);
779
780 if (retval > 0)
781 retval = -retval;
782
783 return retval;
784}
785
786void mpu_shutdown(struct i2c_client *client)
787{
788 struct mpu_private_data *mpu =
789 (struct mpu_private_data *)i2c_get_clientdata(client);
790 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
791 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
792 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
793 int ii;
794
795 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
796 if (!pdata_slave[ii])
797 slave_adapter[ii] = NULL;
798 else
799 slave_adapter[ii] =
800 i2c_get_adapter(pdata_slave[ii]->adapt_num);
801 }
802 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
803
804 mutex_lock(&mpu->mutex);
805 (void)inv_mpu_suspend(mldl_cfg,
806 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
807 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
808 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
809 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
810 INV_ALL_SENSORS);
811 mutex_unlock(&mpu->mutex);
812 dev_dbg(&client->adapter->dev, "%s\n", __func__);
813}
814
815int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg)
816{
817 struct mpu_private_data *mpu =
818 (struct mpu_private_data *)i2c_get_clientdata(client);
819 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
820 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
821 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
822 int ii;
823
824 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
825 if (!pdata_slave[ii])
826 slave_adapter[ii] = NULL;
827 else
828 slave_adapter[ii] =
829 i2c_get_adapter(pdata_slave[ii]->adapt_num);
830 }
831 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
832
833 mutex_lock(&mpu->mutex);
834 if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
835 dev_dbg(&client->adapter->dev,
836 "%s: suspending on event %d\n", __func__, mesg.event);
837 (void)inv_mpu_suspend(mldl_cfg,
838 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
839 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
840 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
841 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
842 INV_ALL_SENSORS);
843 } else {
844 dev_dbg(&client->adapter->dev,
845 "%s: Already suspended %d\n", __func__, mesg.event);
846 }
847 mutex_unlock(&mpu->mutex);
848 return 0;
849}
850
851int mpu_dev_resume(struct i2c_client *client)
852{
853 struct mpu_private_data *mpu =
854 (struct mpu_private_data *)i2c_get_clientdata(client);
855 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
856 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
857 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
858 int ii;
859
860 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
861 if (!pdata_slave[ii])
862 slave_adapter[ii] = NULL;
863 else
864 slave_adapter[ii] =
865 i2c_get_adapter(pdata_slave[ii]->adapt_num);
866 }
867 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
868
869 mutex_lock(&mpu->mutex);
870 if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
871 (void)inv_mpu_resume(mldl_cfg,
872 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
873 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
874 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
875 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
876 mldl_cfg->inv_mpu_cfg->requested_sensors);
877 dev_dbg(&client->adapter->dev,
878 "%s for pid %d\n", __func__, mpu->pid);
879 }
880 mutex_unlock(&mpu->mutex);
881 return 0;
882}
883
884/* define which file operations are supported */
885static const struct file_operations mpu_fops = {
886 .owner = THIS_MODULE,
887 .read = mpu_read,
888 .poll = mpu_poll,
889 .unlocked_ioctl = mpu_dev_ioctl,
890 .open = mpu_dev_open,
891 .release = mpu_release,
892};
893
894int inv_mpu_register_slave(struct module *slave_module,
895 struct i2c_client *slave_client,
896 struct ext_slave_platform_data *slave_pdata,
897 struct ext_slave_descr *(*get_slave_descr)(void))
898{
899 struct mpu_private_data *mpu = mpu_private_data;
900 struct mldl_cfg *mldl_cfg;
901 struct ext_slave_descr *slave_descr;
902 struct ext_slave_platform_data **pdata_slave;
903 char *irq_name = NULL;
904 int result = 0;
905
906 if (!slave_client || !slave_pdata || !get_slave_descr)
907 return -EINVAL;
908
909 if (!mpu) {
910 dev_err(&slave_client->adapter->dev,
911 "%s: Null mpu_private_data\n", __func__);
912 return -EINVAL;
913 }
914 mldl_cfg = &mpu->mldl_cfg;
915 pdata_slave = mldl_cfg->pdata_slave;
916 slave_descr = get_slave_descr();
917
918 if (!slave_descr) {
919 dev_err(&slave_client->adapter->dev,
920 "%s: Null ext_slave_descr\n", __func__);
921 return -EINVAL;
922 }
923
924 mutex_lock(&mpu->mutex);
925 if (mpu->pid) {
926 mutex_unlock(&mpu->mutex);
927 return -EBUSY;
928 }
929
930 if (pdata_slave[slave_descr->type]) {
931 result = -EBUSY;
932 goto out_unlock_mutex;
933 }
934
935 slave_pdata->address = slave_client->addr;
936 slave_pdata->irq = slave_client->irq;
937 slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter);
938
939 dev_info(&slave_client->adapter->dev,
940 "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n",
941 __func__,
942 slave_descr->name,
943 slave_descr->type,
944 slave_pdata->address,
945 slave_pdata->irq,
946 slave_pdata->adapt_num);
947
948 switch (slave_descr->type) {
949 case EXT_SLAVE_TYPE_ACCEL:
950 irq_name = "accelirq";
951 break;
952 case EXT_SLAVE_TYPE_COMPASS:
953 irq_name = "compassirq";
954 break;
955 case EXT_SLAVE_TYPE_PRESSURE:
956 irq_name = "pressureirq";
957 break;
958 default:
959 irq_name = "none";
960 };
961 if (slave_descr->init) {
962 result = slave_descr->init(slave_client->adapter,
963 slave_descr,
964 slave_pdata);
965 if (result) {
966 dev_err(&slave_client->adapter->dev,
967 "%s init failed %d\n",
968 slave_descr->name, result);
969 goto out_unlock_mutex;
970 }
971 }
972
973 pdata_slave[slave_descr->type] = slave_pdata;
974 mpu->slave_modules[slave_descr->type] = slave_module;
975 mldl_cfg->slave[slave_descr->type] = slave_descr;
976
977 goto out_unlock_mutex;
978
979out_unlock_mutex:
980 mutex_unlock(&mpu->mutex);
981
982 if (!result && irq_name && (slave_pdata->irq > 0)) {
983 int warn_result;
984 dev_info(&slave_client->adapter->dev,
985 "Installing %s irq using %d\n",
986 irq_name,
987 slave_pdata->irq);
988 warn_result = slaveirq_init(slave_client->adapter,
989 slave_pdata, irq_name);
990 if (result)
991 dev_WARN(&slave_client->adapter->dev,
992 "%s irq assigned error: %d\n",
993 slave_descr->name, warn_result);
994 } else {
995 dev_info(&slave_client->adapter->dev,
996 "%s irq not assigned: %d %d %d\n",
997 slave_descr->name,
998 result, (int)irq_name, slave_pdata->irq);
999 }
1000
1001 return result;
1002}
1003EXPORT_SYMBOL(inv_mpu_register_slave);
1004
1005void inv_mpu_unregister_slave(struct i2c_client *slave_client,
1006 struct ext_slave_platform_data *slave_pdata,
1007 struct ext_slave_descr *(*get_slave_descr)(void))
1008{
1009 struct mpu_private_data *mpu = mpu_private_data;
1010 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
1011 struct ext_slave_descr *slave_descr;
1012 int result;
1013
1014 dev_info(&slave_client->adapter->dev, "%s\n", __func__);
1015
1016 if (!slave_client || !slave_pdata || !get_slave_descr)
1017 return;
1018
1019 if (slave_pdata->irq)
1020 slaveirq_exit(slave_pdata);
1021
1022 slave_descr = get_slave_descr();
1023 if (!slave_descr)
1024 return;
1025
1026 mutex_lock(&mpu->mutex);
1027
1028 if (slave_descr->exit) {
1029 result = slave_descr->exit(slave_client->adapter,
1030 slave_descr,
1031 slave_pdata);
1032 if (result)
1033 dev_err(&slave_client->adapter->dev,
1034 "Accel exit failed %d\n", result);
1035 }
1036 mldl_cfg->slave[slave_descr->type] = NULL;
1037 mldl_cfg->pdata_slave[slave_descr->type] = NULL;
1038 mpu->slave_modules[slave_descr->type] = NULL;
1039
1040 mutex_unlock(&mpu->mutex);
1041
1042}
1043EXPORT_SYMBOL(inv_mpu_unregister_slave);
1044
1045static unsigned short normal_i2c[] = { I2C_CLIENT_END };
1046
1047static const struct i2c_device_id mpu_id[] = {
1048 {"mpu3050", 0},
1049 {"mpu6050", 0},
1050 {"mpu6050_no_accel", 0},
1051 {}
1052};
1053MODULE_DEVICE_TABLE(i2c, mpu_id);
1054
1055int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
1056{
1057 struct mpu_platform_data *pdata;
1058 struct mpu_private_data *mpu;
1059 struct mldl_cfg *mldl_cfg;
1060 int res = 0;
1061 int ii = 0;
1062 unsigned long irq_flags;
1063
1064 dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
1065
1066 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
1067 res = -ENODEV;
1068 goto out_check_functionality_failed;
1069 }
1070
1071 mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
1072 if (!mpu) {
1073 res = -ENOMEM;
1074 goto out_alloc_data_failed;
1075 }
1076 mldl_cfg = &mpu->mldl_cfg;
1077 mldl_cfg->mpu_ram = &mpu->mpu_ram;
1078 mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg;
1079 mldl_cfg->mpu_offsets = &mpu->mpu_offsets;
1080 mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info;
1081 mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg;
1082 mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state;
1083
1084 mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE;
1085 mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL);
1086 if (!mldl_cfg->mpu_ram->ram) {
1087 res = -ENOMEM;
1088 goto out_alloc_ram_failed;
1089 }
1090 mpu_private_data = mpu;
1091 i2c_set_clientdata(client, mpu);
1092 mpu->client = client;
1093
1094 init_waitqueue_head(&mpu->mpu_event_wait);
1095 mutex_init(&mpu->mutex);
1096 init_completion(&mpu->completion);
1097
1098 mpu->response_timeout = 60; /* Seconds */
1099 mpu->timeout.function = mpu_pm_timeout;
1100 mpu->timeout.data = (u_long) mpu;
1101 init_timer(&mpu->timeout);
1102
1103 mpu->nb.notifier_call = mpu_pm_notifier_callback;
1104 mpu->nb.priority = 0;
1105 res = register_pm_notifier(&mpu->nb);
1106 if (res) {
1107 dev_err(&client->adapter->dev,
1108 "Unable to register pm_notifier %d\n", res);
1109 goto out_register_pm_notifier_failed;
1110 }
1111
1112 pdata = (struct mpu_platform_data *)client->dev.platform_data;
1113 if (!pdata) {
1114 dev_WARN(&client->adapter->dev,
1115 "Missing platform data for mpu\n");
1116 }
1117 mldl_cfg->pdata = pdata;
1118
1119 mldl_cfg->mpu_chip_info->addr = client->addr;
1120 res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
1121
1122 if (res) {
1123 dev_err(&client->adapter->dev,
1124 "Unable to open %s %d\n", MPU_NAME, res);
1125 res = -ENODEV;
1126 goto out_whoami_failed;
1127 }
1128
1129 mpu->dev.minor = MISC_DYNAMIC_MINOR;
1130 mpu->dev.name = "mpu";
1131 mpu->dev.fops = &mpu_fops;
1132 res = misc_register(&mpu->dev);
1133 if (res < 0) {
1134 dev_err(&client->adapter->dev,
1135 "ERROR: misc_register returned %d\n", res);
1136 goto out_misc_register_failed;
1137 }
1138
1139 if (client->irq) {
1140 dev_info(&client->adapter->dev,
1141 "Installing irq using %d\n", client->irq);
1142 if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
1143 irq_flags = IRQF_TRIGGER_FALLING;
1144 else
1145 irq_flags = IRQF_TRIGGER_RISING;
1146 res = mpuirq_init(client, mldl_cfg, irq_flags);
1147
1148 if (res)
1149 goto out_mpuirq_failed;
1150 } else {
1151 dev_WARN(&client->adapter->dev,
1152 "Missing %s IRQ\n", MPU_NAME);
1153 }
1154 return res;
1155
1156out_mpuirq_failed:
1157 misc_deregister(&mpu->dev);
1158out_misc_register_failed:
1159 inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
1160out_whoami_failed:
1161 unregister_pm_notifier(&mpu->nb);
1162out_register_pm_notifier_failed:
1163 kfree(mldl_cfg->mpu_ram->ram);
1164 mpu_private_data = NULL;
1165out_alloc_ram_failed:
1166 kfree(mpu);
1167out_alloc_data_failed:
1168out_check_functionality_failed:
1169 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
1170 return res;
1171
1172}
1173
1174static int mpu_remove(struct i2c_client *client)
1175{
1176 struct mpu_private_data *mpu = i2c_get_clientdata(client);
1177 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
1178 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
1179 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
1180 int ii;
1181
1182 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1183 if (!pdata_slave[ii])
1184 slave_adapter[ii] = NULL;
1185 else
1186 slave_adapter[ii] =
1187 i2c_get_adapter(pdata_slave[ii]->adapt_num);
1188 }
1189
1190 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
1191 dev_dbg(&client->adapter->dev, "%s\n", __func__);
1192
1193 inv_mpu_close(mldl_cfg,
1194 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
1195 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
1196 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
1197 slave_adapter[EXT_SLAVE_TYPE_PRESSURE]);
1198
1199
1200 if (client->irq)
1201 mpuirq_exit();
1202
1203 misc_deregister(&mpu->dev);
1204
1205 unregister_pm_notifier(&mpu->nb);
1206
1207 kfree(mpu->mldl_cfg.mpu_ram->ram);
1208 kfree(mpu);
1209
1210 return 0;
1211}
1212
1213static struct i2c_driver mpu_driver = {
1214 .class = I2C_CLASS_HWMON,
1215 .probe = mpu_probe,
1216 .remove = mpu_remove,
1217 .id_table = mpu_id,
1218 .driver = {
1219 .owner = THIS_MODULE,
1220 .name = MPU_NAME,
1221 },
1222 .address_list = normal_i2c,
1223 .shutdown = mpu_shutdown, /* optional */
1224 .suspend = mpu_dev_suspend, /* optional */
1225 .resume = mpu_dev_resume, /* optional */
1226
1227};
1228
1229static int __init mpu_init(void)
1230{
1231 int res = i2c_add_driver(&mpu_driver);
1232 pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
1233 if (res)
1234 pr_err("%s failed\n", __func__);
1235 return res;
1236}
1237
1238static void __exit mpu_exit(void)
1239{
1240 pr_info("%s\n", __func__);
1241 i2c_del_driver(&mpu_driver);
1242}
1243
1244module_init(mpu_init);
1245module_exit(mpu_exit);
1246
1247MODULE_AUTHOR("Invensense Corporation");
1248MODULE_DESCRIPTION("User space character device interface for MPU");
1249MODULE_LICENSE("GPL");
1250MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu6050/Makefile b/drivers/misc/inv_mpu/mpu6050/Makefile
new file mode 100644
index 00000000000..a93aa97a699
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/Makefile
@@ -0,0 +1,18 @@
1
2# Kernel makefile for motions sensors
3#
4#
5
6obj-$(CONFIG_MPU_SENSORS_MPU6050B1) += mpu6050b1.o
7
8ccflags-y := -DMPU_CURRENT_BUILD_MPU6050B1
9
10mpu6050b1-objs += mldl_cfg.o
11mpu6050b1-objs += mldl_print_cfg.o
12mpu6050b1-objs += mlsl-kernel.o
13mpu6050b1-objs += mpu-dev.o
14mpu6050b1-objs += ../accel/mpu6050.o
15
16EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
17EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
18EXTRA_CFLAGS += -DINV_CACHE_DMP=1
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c
new file mode 100644
index 00000000000..22af0c20098
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mldl_cfg.c
@@ -0,0 +1,1916 @@
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 MLDL
22 *
23 * @{
24 * @file mldl_cfg.c
25 * @brief The Motion Library Driver Layer.
26 */
27
28/* -------------------------------------------------------------------------- */
29#include <linux/delay.h>
30#include <linux/slab.h>
31
32#include <stddef.h>
33
34#include "mldl_cfg.h"
35#include <linux/mpu.h>
36#include "mpu6050b1.h"
37
38#include "mlsl.h"
39#include "mldl_print_cfg.h"
40#include "log.h"
41#undef MPL_LOG_TAG
42#define MPL_LOG_TAG "mldl_cfg:"
43
44/* -------------------------------------------------------------------------- */
45
46#define SLEEP 0
47#define WAKE_UP 7
48#define RESET 1
49#define STANDBY 1
50
51/* -------------------------------------------------------------------------- */
52
53/**
54 * @brief Stop the DMP running
55 *
56 * @return INV_SUCCESS or non-zero error code
57 */
58static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle)
59{
60 unsigned char user_ctrl_reg;
61 int result;
62
63 if (mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)
64 return INV_SUCCESS;
65
66 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
67 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
68 if (result) {
69 LOG_RESULT_LOCATION(result);
70 return result;
71 }
72 user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST;
73 user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST;
74
75 result = inv_serial_single_write(gyro_handle,
76 mldl_cfg->mpu_chip_info->addr,
77 MPUREG_USER_CTRL, user_ctrl_reg);
78 if (result) {
79 LOG_RESULT_LOCATION(result);
80 return result;
81 }
82 mldl_cfg->inv_mpu_state->status |= MPU_DMP_IS_SUSPENDED;
83
84 return result;
85}
86
87/**
88 * @brief Starts the DMP running
89 *
90 * @return INV_SUCCESS or non-zero error code
91 */
92static int dmp_start(struct mldl_cfg *mldl_cfg, void *mlsl_handle)
93{
94 unsigned char user_ctrl_reg;
95 int result;
96
97 if ((!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
98 mldl_cfg->mpu_gyro_cfg->dmp_enable)
99 ||
100 ((mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED) &&
101 !mldl_cfg->mpu_gyro_cfg->dmp_enable))
102 return INV_SUCCESS;
103
104 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
105 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
106 if (result) {
107 LOG_RESULT_LOCATION(result);
108 return result;
109 }
110
111 result = inv_serial_single_write(
112 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
113 MPUREG_USER_CTRL,
114 ((user_ctrl_reg & (~BIT_FIFO_EN))
115 | BIT_FIFO_RST));
116 if (result) {
117 LOG_RESULT_LOCATION(result);
118 return result;
119 }
120
121 result = inv_serial_single_write(
122 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
123 MPUREG_USER_CTRL, user_ctrl_reg);
124 if (result) {
125 LOG_RESULT_LOCATION(result);
126 return result;
127 }
128
129 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
130 MPUREG_USER_CTRL, 1, &user_ctrl_reg);
131 if (result) {
132 LOG_RESULT_LOCATION(result);
133 return result;
134 }
135
136 user_ctrl_reg |= BIT_DMP_EN;
137
138 if (mldl_cfg->mpu_gyro_cfg->fifo_enable)
139 user_ctrl_reg |= BIT_FIFO_EN;
140 else
141 user_ctrl_reg &= ~BIT_FIFO_EN;
142
143 user_ctrl_reg |= BIT_DMP_RST;
144
145 result = inv_serial_single_write(
146 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
147 MPUREG_USER_CTRL, user_ctrl_reg);
148 if (result) {
149 LOG_RESULT_LOCATION(result);
150 return result;
151 }
152 mldl_cfg->inv_mpu_state->status &= ~MPU_DMP_IS_SUSPENDED;
153
154 return result;
155}
156
157/**
158 * @brief enables/disables the I2C bypass to an external device
159 * connected to MPU's secondary I2C bus.
160 * @param enable
161 * Non-zero to enable pass through.
162 * @return INV_SUCCESS if successful, a non-zero error code otherwise.
163 */
164static int mpu6050b1_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
165 void *mlsl_handle, unsigned char enable)
166{
167 unsigned char reg;
168 int result;
169 unsigned char status = mldl_cfg->inv_mpu_state->status;
170 if ((status & MPU_GYRO_IS_BYPASSED && enable) ||
171 (!(status & MPU_GYRO_IS_BYPASSED) && !enable))
172 return INV_SUCCESS;
173
174 /*---- get current 'USER_CTRL' into b ----*/
175 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
176 MPUREG_USER_CTRL, 1, &reg);
177 if (result) {
178 LOG_RESULT_LOCATION(result);
179 return result;
180 }
181
182 if (!enable) {
183 /* setting int_config with the property flag BIT_BYPASS_EN
184 should be done by the setup functions */
185 result = inv_serial_single_write(
186 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
187 MPUREG_INT_PIN_CFG,
188 (mldl_cfg->pdata->int_config & ~(BIT_BYPASS_EN)));
189 if (!(reg & BIT_I2C_MST_EN)) {
190 result =
191 inv_serial_single_write(
192 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
193 MPUREG_USER_CTRL,
194 (reg | BIT_I2C_MST_EN));
195 if (result) {
196 LOG_RESULT_LOCATION(result);
197 return result;
198 }
199 }
200 } else if (enable) {
201 if (reg & BIT_AUX_IF_EN) {
202 result =
203 inv_serial_single_write(
204 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
205 MPUREG_USER_CTRL,
206 (reg & (~BIT_I2C_MST_EN)));
207 if (result) {
208 LOG_RESULT_LOCATION(result);
209 return result;
210 }
211 /*****************************************
212 * To avoid hanging the bus we must sleep until all
213 * slave transactions have been completed.
214 * 24 bytes max slave reads
215 * +1 byte possible extra write
216 * +4 max slave address
217 * ---
218 * 33 Maximum bytes
219 * x9 Approximate bits per byte
220 * ---
221 * 297 bits.
222 * 2.97 ms minimum @ 100kbps
223 * 0.75 ms minimum @ 400kbps.
224 *****************************************/
225 msleep(3);
226 }
227 result = inv_serial_single_write(
228 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
229 MPUREG_INT_PIN_CFG,
230 (mldl_cfg->pdata->int_config | BIT_BYPASS_EN));
231 if (result) {
232 LOG_RESULT_LOCATION(result);
233 return result;
234 }
235 }
236 if (enable)
237 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_BYPASSED;
238 else
239 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
240
241 return result;
242}
243
244
245
246
247/**
248 * @brief enables/disables the I2C bypass to an external device
249 * connected to MPU's secondary I2C bus.
250 * @param enable
251 * Non-zero to enable pass through.
252 * @return INV_SUCCESS if successful, a non-zero error code otherwise.
253 */
254static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
255 unsigned char enable)
256{
257 return mpu6050b1_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
258}
259
260
261#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
262
263/* NOTE : when not indicated, product revision
264 is considered an 'npp'; non production part */
265
266/* produces an unique identifier for each device based on the
267 combination of product version and product revision */
268struct prod_rev_map_t {
269 unsigned short mpl_product_key;
270 unsigned char silicon_rev;
271 unsigned short gyro_trim;
272 unsigned short accel_trim;
273};
274
275/* NOTE: product entries are in chronological order */
276static struct prod_rev_map_t prod_rev_map[] = {
277 /* prod_ver = 0 */
278 {MPL_PROD_KEY(0, 1), MPU_SILICON_REV_A2, 131, 16384},
279 {MPL_PROD_KEY(0, 2), MPU_SILICON_REV_A2, 131, 16384},
280 {MPL_PROD_KEY(0, 3), MPU_SILICON_REV_A2, 131, 16384},
281 {MPL_PROD_KEY(0, 4), MPU_SILICON_REV_A2, 131, 16384},
282 {MPL_PROD_KEY(0, 5), MPU_SILICON_REV_A2, 131, 16384},
283 {MPL_PROD_KEY(0, 6), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/C2-1) */
284 /* prod_ver = 1, forced to 0 for MPU6050 A2 */
285 {MPL_PROD_KEY(0, 7), MPU_SILICON_REV_A2, 131, 16384},
286 {MPL_PROD_KEY(0, 8), MPU_SILICON_REV_A2, 131, 16384},
287 {MPL_PROD_KEY(0, 9), MPU_SILICON_REV_A2, 131, 16384},
288 {MPL_PROD_KEY(0, 10), MPU_SILICON_REV_A2, 131, 16384},
289 {MPL_PROD_KEY(0, 11), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D2-1) */
290 {MPL_PROD_KEY(0, 12), MPU_SILICON_REV_A2, 131, 16384},
291 {MPL_PROD_KEY(0, 13), MPU_SILICON_REV_A2, 131, 16384},
292 {MPL_PROD_KEY(0, 14), MPU_SILICON_REV_A2, 131, 16384},
293 {MPL_PROD_KEY(0, 15), MPU_SILICON_REV_A2, 131, 16384},
294 {MPL_PROD_KEY(0, 27), MPU_SILICON_REV_A2, 131, 16384}, /* (A2/D4) */
295 /* prod_ver = 1 */
296 {MPL_PROD_KEY(1, 16), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-1) */
297 {MPL_PROD_KEY(1, 17), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-2) */
298 {MPL_PROD_KEY(1, 18), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-3) */
299 {MPL_PROD_KEY(1, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-4) */
300 {MPL_PROD_KEY(1, 20), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D2-5) */
301 {MPL_PROD_KEY(1, 28), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/D4) */
302 {MPL_PROD_KEY(1, 1), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-1) */
303 {MPL_PROD_KEY(1, 2), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-2) */
304 {MPL_PROD_KEY(1, 3), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-3) */
305 {MPL_PROD_KEY(1, 4), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-4) */
306 {MPL_PROD_KEY(1, 5), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-5) */
307 {MPL_PROD_KEY(1, 6), MPU_SILICON_REV_B1, 131, 16384}, /* (B1/E1-6) */
308 /* prod_ver = 2 */
309 {MPL_PROD_KEY(2, 7), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-1) */
310 {MPL_PROD_KEY(2, 8), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-2) */
311 {MPL_PROD_KEY(2, 9), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-3) */
312 {MPL_PROD_KEY(2, 10), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-4) */
313 {MPL_PROD_KEY(2, 11), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-5) */
314 {MPL_PROD_KEY(2, 12), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E1-6) */
315 {MPL_PROD_KEY(2, 29), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/D4) */
316 /* prod_ver = 3 */
317 {MPL_PROD_KEY(3, 30), MPU_SILICON_REV_B1, 131, 16384}, /* (B2/E2) */
318 /* prod_ver = 4 */
319 {MPL_PROD_KEY(4, 31), MPU_SILICON_REV_B1, 131, 8192}, /* (B2/F1) */
320 {MPL_PROD_KEY(4, 1), MPU_SILICON_REV_B1, 131, 8192}, /* (B3/F1) */
321 {MPL_PROD_KEY(4, 3), MPU_SILICON_REV_B1, 131, 8192}, /* (B4/F1) */
322 /* prod_ver = 5 */
323 {MPL_PROD_KEY(6, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */
324 /* prod_ver = 7 */
325 {MPL_PROD_KEY(7, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */
326 /* prod_ver = 8 */
327 {MPL_PROD_KEY(8, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */
328 /* prod_ver = 9 */
329 {MPL_PROD_KEY(9, 19), MPU_SILICON_REV_B1, 131, 16384}, /* (B5/E2) */
330 /* prod_ver = 10 */
331 {MPL_PROD_KEY(10, 19), MPU_SILICON_REV_B1, 131, 16384} /* (B5/E2) */
332};
333
334/**
335 * @internal
336 * @brief Inverse lookup of the index of an MPL product key .
337 * @param key
338 * the MPL product indentifier also referred to as 'key'.
339 * @return the index position of the key in the array, -1 if not found.
340 */
341short index_of_key(unsigned short key)
342{
343 int i;
344 for (i = 0; i < NUM_OF_PROD_REVS; i++)
345 if (prod_rev_map[i].mpl_product_key == key)
346 return (short)i;
347 return -1;
348}
349
350/**
351 * @internal
352 * @brief Get the product revision and version for MPU6050 and
353 * extract all per-part specific information.
354 * The product version number is read from the PRODUCT_ID register in
355 * user space register map.
356 * The product revision number is in read from OTP bank 0, ADDR6[7:2].
357 * These 2 numbers, combined, provide an unique key to be used to
358 * retrieve some per-device information such as the silicon revision
359 * and the gyro and accel sensitivity trim values.
360 *
361 * @param mldl_cfg
362 * a pointer to the mldl config data structure.
363 * @param mlsl_handle
364 * an file handle to the serial communication device the
365 * device is connected to.
366 *
367 * @return 0 on success, a non-zero error code otherwise.
368 */
369static int inv_get_silicon_rev_mpu6050(
370 struct mldl_cfg *mldl_cfg, void *mlsl_handle)
371{
372 int result;
373 unsigned char prod_ver = 0x00, prod_rev = 0x00;
374 unsigned char bank =
375 (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
376 unsigned short memAddr = ((bank << 8) | 0x06);
377 unsigned short key;
378 short index;
379 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
380
381 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
382 MPUREG_PRODUCT_ID, 1, &prod_ver);
383 if (result) {
384 LOG_RESULT_LOCATION(result);
385 return result;
386 }
387 prod_ver &= 0xF;
388
389 result = inv_serial_read_mem(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
390 memAddr, 1, &prod_rev);
391 if (result) {
392 LOG_RESULT_LOCATION(result);
393 return result;
394 }
395 prod_rev >>= 2;
396
397 /* clean the prefetch and cfg user bank bits */
398 result = inv_serial_single_write(
399 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
400 MPUREG_BANK_SEL, 0);
401 if (result) {
402 LOG_RESULT_LOCATION(result);
403 return result;
404 }
405
406 key = MPL_PROD_KEY(prod_ver, prod_rev);
407 if (key == 0) {
408 MPL_LOGE("Product id read as 0 "
409 "indicates device is either "
410 "incompatible or an MPU3050\n");
411 return INV_ERROR_INVALID_MODULE;
412 }
413 index = index_of_key(key);
414 if (index == -1 || index >= NUM_OF_PROD_REVS) {
415 MPL_LOGE("Unsupported product key %d in MPL\n", key);
416 return INV_ERROR_INVALID_MODULE;
417 }
418 /* check MPL is compiled for this device */
419 if (prod_rev_map[index].silicon_rev != MPU_SILICON_REV_B1) {
420 MPL_LOGE("MPL compiled for MPU6050B1 support "
421 "but device is not MPU6050B1 (%d)\n", key);
422 return INV_ERROR_INVALID_MODULE;
423 }
424
425 mpu_chip_info->product_id = prod_ver;
426 mpu_chip_info->product_revision = prod_rev;
427 mpu_chip_info->silicon_revision = prod_rev_map[index].silicon_rev;
428 mpu_chip_info->gyro_sens_trim = prod_rev_map[index].gyro_trim;
429 mpu_chip_info->accel_sens_trim = prod_rev_map[index].accel_trim;
430
431 return result;
432}
433#define inv_get_silicon_rev inv_get_silicon_rev_mpu6050
434
435
436/**
437 * @brief Enable / Disable the use MPU's secondary I2C interface level
438 * shifters.
439 * When enabled the secondary I2C interface to which the external
440 * device is connected runs at VDD voltage (main supply).
441 * When disabled the 2nd interface runs at VDDIO voltage.
442 * See the device specification for more details.
443 *
444 * @note using this API may produce unpredictable results, depending on how
445 * the MPU and slave device are setup on the target platform.
446 * Use of this API should entirely be restricted to system
447 * integrators. Once the correct value is found, there should be no
448 * need to change the level shifter at runtime.
449 *
450 * @pre Must be called after inv_serial_start().
451 * @note Typically called before inv_dmp_open().
452 *
453 * @param[in] enable:
454 * 0 to run at VDDIO (default),
455 * 1 to run at VDD.
456 *
457 * @return INV_SUCCESS if successfull, a non-zero error code otherwise.
458 */
459static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *mldl_cfg,
460 void *mlsl_handle, unsigned char enable)
461{
462 int result;
463 unsigned char regval;
464
465 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
466 MPUREG_YG_OFFS_TC, 1, &regval);
467 if (result) {
468 LOG_RESULT_LOCATION(result);
469 return result;
470 }
471 if (enable)
472 regval |= BIT_I2C_MST_VDDIO;
473
474 result = inv_serial_single_write(
475 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
476 MPUREG_YG_OFFS_TC, regval);
477 if (result) {
478 LOG_RESULT_LOCATION(result);
479 return result;
480 }
481 return INV_SUCCESS;
482}
483
484
485/**
486 * @internal
487 * @brief MPU6050 B1 power management functions.
488 * @param mldl_cfg
489 * a pointer to the internal mldl_cfg data structure.
490 * @param mlsl_handle
491 * a file handle to the serial device used to communicate
492 * with the MPU6050 B1 device.
493 * @param reset
494 * 1 to reset hardware.
495 * @param sensors
496 * Bitfield of sensors to leave on
497 *
498 * @return 0 on success, a non-zero error code on error.
499 */
500static int mpu60xx_pwr_mgmt(struct mldl_cfg *mldl_cfg,
501 void *mlsl_handle,
502 unsigned int reset, unsigned long sensors)
503{
504 unsigned char pwr_mgmt[2];
505 unsigned char pwr_mgmt_prev[2];
506 int result;
507 int sleep = !(sensors & (INV_THREE_AXIS_GYRO | INV_THREE_AXIS_ACCEL
508 | INV_DMP_PROCESSOR));
509
510 if (reset) {
511 MPL_LOGI("Reset MPU6050 B1\n");
512 result = inv_serial_single_write(
513 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
514 MPUREG_PWR_MGMT_1, BIT_H_RESET);
515 if (result) {
516 LOG_RESULT_LOCATION(result);
517 return result;
518 }
519 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
520 msleep(15);
521 }
522
523 /* NOTE : reading both PWR_MGMT_1 and PWR_MGMT_2 for efficiency because
524 they are accessible even when the device is powered off */
525 result = inv_serial_read(mlsl_handle, mldl_cfg->mpu_chip_info->addr,
526 MPUREG_PWR_MGMT_1, 2, pwr_mgmt_prev);
527 if (result) {
528 LOG_RESULT_LOCATION(result);
529 return result;
530 }
531
532 pwr_mgmt[0] = pwr_mgmt_prev[0];
533 pwr_mgmt[1] = pwr_mgmt_prev[1];
534
535 if (sleep) {
536 mldl_cfg->inv_mpu_state->status |= MPU_DEVICE_IS_SUSPENDED;
537 pwr_mgmt[0] |= BIT_SLEEP;
538 } else {
539 mldl_cfg->inv_mpu_state->status &= ~MPU_DEVICE_IS_SUSPENDED;
540 pwr_mgmt[0] &= ~BIT_SLEEP;
541 }
542 if (pwr_mgmt[0] != pwr_mgmt_prev[0]) {
543 result = inv_serial_single_write(
544 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
545 MPUREG_PWR_MGMT_1, pwr_mgmt[0]);
546 if (result) {
547 LOG_RESULT_LOCATION(result);
548 return result;
549 }
550 }
551
552 pwr_mgmt[1] &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
553 if (!(sensors & INV_X_GYRO))
554 pwr_mgmt[1] |= BIT_STBY_XG;
555 if (!(sensors & INV_Y_GYRO))
556 pwr_mgmt[1] |= BIT_STBY_YG;
557 if (!(sensors & INV_Z_GYRO))
558 pwr_mgmt[1] |= BIT_STBY_ZG;
559
560 if (pwr_mgmt[1] != pwr_mgmt_prev[1]) {
561 result = inv_serial_single_write(
562 mlsl_handle, mldl_cfg->mpu_chip_info->addr,
563 MPUREG_PWR_MGMT_2, pwr_mgmt[1]);
564 if (result) {
565 LOG_RESULT_LOCATION(result);
566 return result;
567 }
568 }
569
570 if ((pwr_mgmt[1] & (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
571 (BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) {
572 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_IS_SUSPENDED;
573 } else {
574 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_SUSPENDED;
575 }
576
577 return INV_SUCCESS;
578}
579
580
581/**
582 * @brief sets the clock source for the gyros.
583 * @param mldl_cfg
584 * a pointer to the struct mldl_cfg data structure.
585 * @param gyro_handle
586 * an handle to the serial device the gyro is assigned to.
587 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
588 */
589static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
590{
591 int result;
592 unsigned char cur_clk_src;
593 unsigned char reg;
594
595 /* clock source selection */
596 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
597 MPUREG_PWR_MGM, 1, &reg);
598 if (result) {
599 LOG_RESULT_LOCATION(result);
600 return result;
601 }
602 cur_clk_src = reg & BITS_CLKSEL;
603 reg &= ~BITS_CLKSEL;
604
605
606 result = inv_serial_single_write(
607 gyro_handle, mldl_cfg->mpu_chip_info->addr,
608 MPUREG_PWR_MGM, mldl_cfg->mpu_gyro_cfg->clk_src | reg);
609 if (result) {
610 LOG_RESULT_LOCATION(result);
611 return result;
612 }
613
614 /* ERRATA:
615 workaroud to switch from any MPU_CLK_SEL_PLLGYROx to
616 MPU_CLK_SEL_INTERNAL and XGyro is powered up:
617 1) Select INT_OSC
618 2) PD XGyro
619 3) PU XGyro
620 */
621 if ((cur_clk_src == MPU_CLK_SEL_PLLGYROX
622 || cur_clk_src == MPU_CLK_SEL_PLLGYROY
623 || cur_clk_src == MPU_CLK_SEL_PLLGYROZ)
624 && mldl_cfg->mpu_gyro_cfg->clk_src == MPU_CLK_SEL_INTERNAL
625 && mldl_cfg->inv_mpu_cfg->requested_sensors & INV_X_GYRO) {
626 unsigned char first_result = INV_SUCCESS;
627 mldl_cfg->inv_mpu_cfg->requested_sensors &= ~INV_X_GYRO;
628 result = mpu60xx_pwr_mgmt(
629 mldl_cfg, gyro_handle,
630 false, mldl_cfg->inv_mpu_cfg->requested_sensors);
631 ERROR_CHECK_FIRST(first_result, result);
632 mldl_cfg->inv_mpu_cfg->requested_sensors |= INV_X_GYRO;
633 result = mpu60xx_pwr_mgmt(
634 mldl_cfg, gyro_handle,
635 false, mldl_cfg->inv_mpu_cfg->requested_sensors);
636 ERROR_CHECK_FIRST(first_result, result);
637 result = first_result;
638 }
639 return result;
640}
641
642/**
643 * Configures the MPU I2C Master
644 *
645 * @mldl_cfg Handle to the configuration data
646 * @gyro_handle handle to the gyro communictation interface
647 * @slave Can be Null if turning off the slave
648 * @slave_pdata Can be null if turning off the slave
649 * @slave_id enum ext_slave_type to determine which index to use
650 *
651 *
652 * This fucntion configures the slaves by:
653 * 1) Setting up the read
654 * a) Read Register
655 * b) Read Length
656 * 2) Set up the data trigger (MPU6050 only)
657 * a) Set trigger write register
658 * b) Set Trigger write value
659 * 3) Set up the divider (MPU6050 only)
660 * 4) Set the slave bypass mode depending on slave
661 *
662 * returns INV_SUCCESS or non-zero error code
663 */
664
665static int mpu_set_slave_mpu60xx(struct mldl_cfg *mldl_cfg,
666 void *gyro_handle,
667 struct ext_slave_descr *slave,
668 struct ext_slave_platform_data *slave_pdata,
669 int slave_id)
670{
671 int result;
672 unsigned char reg;
673 /* Slave values */
674 unsigned char slave_reg;
675 unsigned char slave_len;
676 unsigned char slave_endian;
677 unsigned char slave_address;
678 /* Which MPU6050 registers to use */
679 unsigned char addr_reg;
680 unsigned char reg_reg;
681 unsigned char ctrl_reg;
682 /* Which MPU6050 registers to use for the trigger */
683 unsigned char addr_trig_reg;
684 unsigned char reg_trig_reg;
685 unsigned char ctrl_trig_reg;
686
687 unsigned char bits_slave_delay = 0;
688 /* Divide down rate for the Slave, from the mpu rate */
689 unsigned char d0_trig_reg;
690 unsigned char delay_ctrl_orig;
691 unsigned char delay_ctrl;
692 long divider;
693
694 if (NULL == slave || NULL == slave_pdata) {
695 slave_reg = 0;
696 slave_len = 0;
697 slave_endian = 0;
698 slave_address = 0;
699 } else {
700 slave_reg = slave->read_reg;
701 slave_len = slave->read_len;
702 slave_endian = slave->endian;
703 slave_address = slave_pdata->address;
704 slave_address |= BIT_I2C_READ;
705 }
706
707 switch (slave_id) {
708 case EXT_SLAVE_TYPE_ACCEL:
709 addr_reg = MPUREG_I2C_SLV1_ADDR;
710 reg_reg = MPUREG_I2C_SLV1_REG;
711 ctrl_reg = MPUREG_I2C_SLV1_CTRL;
712 addr_trig_reg = 0;
713 reg_trig_reg = 0;
714 ctrl_trig_reg = 0;
715 bits_slave_delay = BIT_SLV1_DLY_EN;
716 break;
717 case EXT_SLAVE_TYPE_COMPASS:
718 addr_reg = MPUREG_I2C_SLV0_ADDR;
719 reg_reg = MPUREG_I2C_SLV0_REG;
720 ctrl_reg = MPUREG_I2C_SLV0_CTRL;
721 addr_trig_reg = MPUREG_I2C_SLV2_ADDR;
722 reg_trig_reg = MPUREG_I2C_SLV2_REG;
723 ctrl_trig_reg = MPUREG_I2C_SLV2_CTRL;
724 d0_trig_reg = MPUREG_I2C_SLV2_DO;
725 bits_slave_delay = BIT_SLV2_DLY_EN | BIT_SLV0_DLY_EN;
726 break;
727 case EXT_SLAVE_TYPE_PRESSURE:
728 addr_reg = MPUREG_I2C_SLV3_ADDR;
729 reg_reg = MPUREG_I2C_SLV3_REG;
730 ctrl_reg = MPUREG_I2C_SLV3_CTRL;
731 addr_trig_reg = MPUREG_I2C_SLV4_ADDR;
732 reg_trig_reg = MPUREG_I2C_SLV4_REG;
733 ctrl_trig_reg = MPUREG_I2C_SLV4_CTRL;
734 bits_slave_delay = BIT_SLV4_DLY_EN | BIT_SLV3_DLY_EN;
735 break;
736 default:
737 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
738 return INV_ERROR_INVALID_PARAMETER;
739 };
740
741 /* return if this slave has already been set */
742 if ((slave_address &&
743 ((mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay)
744 == bits_slave_delay)) ||
745 (!slave_address &&
746 (mldl_cfg->inv_mpu_state->i2c_slaves_enabled & bits_slave_delay) ==
747 0))
748 return 0;
749
750 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
751
752 /* Address */
753 result = inv_serial_single_write(gyro_handle,
754 mldl_cfg->mpu_chip_info->addr,
755 addr_reg, slave_address);
756 if (result) {
757 LOG_RESULT_LOCATION(result);
758 return result;
759 }
760 /* Register */
761 result = inv_serial_single_write(gyro_handle,
762 mldl_cfg->mpu_chip_info->addr,
763 reg_reg, slave_reg);
764 if (result) {
765 LOG_RESULT_LOCATION(result);
766 return result;
767 }
768
769 /* Length, byte swapping, grouping & enable */
770 if (slave_len > BITS_SLV_LENG) {
771 MPL_LOGW("Limiting slave burst read length to "
772 "the allowed maximum (15B, req. %d)\n", slave_len);
773 slave_len = BITS_SLV_LENG;
774 return INV_ERROR_INVALID_CONFIGURATION;
775 }
776 reg = slave_len;
777 if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN) {
778 reg |= BIT_SLV_BYTE_SW;
779 if (slave_reg & 1)
780 reg |= BIT_SLV_GRP;
781 }
782 if (slave_address)
783 reg |= BIT_SLV_ENABLE;
784
785 result = inv_serial_single_write(gyro_handle,
786 mldl_cfg->mpu_chip_info->addr,
787 ctrl_reg, reg);
788 if (result) {
789 LOG_RESULT_LOCATION(result);
790 return result;
791 }
792
793 /* Trigger */
794 if (addr_trig_reg) {
795 /* If slave address is 0 this clears the trigger */
796 result = inv_serial_single_write(gyro_handle,
797 mldl_cfg->mpu_chip_info->addr,
798 addr_trig_reg,
799 slave_address & ~BIT_I2C_READ);
800 if (result) {
801 LOG_RESULT_LOCATION(result);
802 return result;
803 }
804 }
805
806 if (slave && slave->trigger && reg_trig_reg) {
807 result = inv_serial_single_write(gyro_handle,
808 mldl_cfg->mpu_chip_info->addr,
809 reg_trig_reg,
810 slave->trigger->reg);
811 if (result) {
812 LOG_RESULT_LOCATION(result);
813 return result;
814 }
815 result = inv_serial_single_write(gyro_handle,
816 mldl_cfg->mpu_chip_info->addr,
817 ctrl_trig_reg,
818 BIT_SLV_ENABLE | 0x01);
819 if (result) {
820 LOG_RESULT_LOCATION(result);
821 return result;
822 }
823 result = inv_serial_single_write(gyro_handle,
824 mldl_cfg->mpu_chip_info->addr,
825 d0_trig_reg,
826 slave->trigger->value);
827 if (result) {
828 LOG_RESULT_LOCATION(result);
829 return result;
830 }
831 } else if (ctrl_trig_reg) {
832 result = inv_serial_single_write(gyro_handle,
833 mldl_cfg->mpu_chip_info->addr,
834 ctrl_trig_reg, 0x00);
835 if (result) {
836 LOG_RESULT_LOCATION(result);
837 return result;
838 }
839 }
840
841 /* Data rate */
842 if (slave) {
843 struct ext_slave_config config;
844 long data;
845 config.key = MPU_SLAVE_CONFIG_ODR_RESUME;
846 config.len = sizeof(long);
847 config.apply = false;
848 config.data = &data;
849 if (!(slave->get_config))
850 return INV_ERROR_INVALID_CONFIGURATION;
851
852 result = slave->get_config(NULL, slave, slave_pdata, &config);
853 if (result) {
854 LOG_RESULT_LOCATION(result);
855 return result;
856 }
857 MPL_LOGI("Slave %d ODR: %ld Hz\n", slave_id, data / 1000);
858 divider = ((1000 * inv_mpu_get_sampling_rate_hz(
859 mldl_cfg->mpu_gyro_cfg))
860 / data) - 1;
861 } else {
862 divider = 0;
863 }
864
865 result = inv_serial_read(gyro_handle,
866 mldl_cfg->mpu_chip_info->addr,
867 MPUREG_I2C_MST_DELAY_CTRL,
868 1, &delay_ctrl_orig);
869 delay_ctrl = delay_ctrl_orig;
870 if (result) {
871 LOG_RESULT_LOCATION(result);
872 return result;
873 }
874
875 if (divider > 0 && divider <= MASK_I2C_MST_DLY) {
876 result = inv_serial_read(gyro_handle,
877 mldl_cfg->mpu_chip_info->addr,
878 MPUREG_I2C_SLV4_CTRL, 1, &reg);
879 if (result) {
880 LOG_RESULT_LOCATION(result);
881 return result;
882 }
883 if ((reg & MASK_I2C_MST_DLY) &&
884 ((long)(reg & MASK_I2C_MST_DLY) !=
885 (divider & MASK_I2C_MST_DLY))) {
886 MPL_LOGW("Changing slave divider: %ld to %ld\n",
887 (long)(reg & MASK_I2C_MST_DLY),
888 (divider & MASK_I2C_MST_DLY));
889
890 }
891 reg |= (unsigned char)(divider & MASK_I2C_MST_DLY);
892 result = inv_serial_single_write(gyro_handle,
893 mldl_cfg->mpu_chip_info->addr,
894 MPUREG_I2C_SLV4_CTRL,
895 reg);
896 if (result) {
897 LOG_RESULT_LOCATION(result);
898 return result;
899 }
900
901 delay_ctrl |= bits_slave_delay;
902 } else {
903 delay_ctrl &= ~(bits_slave_delay);
904 }
905 if (delay_ctrl != delay_ctrl_orig) {
906 result = inv_serial_single_write(
907 gyro_handle, mldl_cfg->mpu_chip_info->addr,
908 MPUREG_I2C_MST_DELAY_CTRL,
909 delay_ctrl);
910 if (result) {
911 LOG_RESULT_LOCATION(result);
912 return result;
913 }
914 }
915
916 if (slave_address)
917 mldl_cfg->inv_mpu_state->i2c_slaves_enabled |=
918 bits_slave_delay;
919 else
920 mldl_cfg->inv_mpu_state->i2c_slaves_enabled &=
921 ~bits_slave_delay;
922
923 return result;
924}
925
926static int mpu_set_slave(struct mldl_cfg *mldl_cfg,
927 void *gyro_handle,
928 struct ext_slave_descr *slave,
929 struct ext_slave_platform_data *slave_pdata,
930 int slave_id)
931{
932 return mpu_set_slave_mpu60xx(mldl_cfg, gyro_handle, slave,
933 slave_pdata, slave_id);
934}
935/**
936 * Check to see if the gyro was reset by testing a couple of registers known
937 * to change on reset.
938 *
939 * @mldl_cfg mldl configuration structure
940 * @gyro_handle handle used to communicate with the gyro
941 *
942 * @return INV_SUCCESS or non-zero error code
943 */
944static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle)
945{
946 int result = INV_SUCCESS;
947 unsigned char reg;
948
949 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
950 MPUREG_DMP_CFG_2, 1, &reg);
951 if (result) {
952 LOG_RESULT_LOCATION(result);
953 return result;
954 }
955
956 if (mldl_cfg->mpu_gyro_cfg->dmp_cfg2 != reg)
957 return true;
958
959 if (0 != mldl_cfg->mpu_gyro_cfg->dmp_cfg1)
960 return false;
961
962 /* Inconclusive assume it was reset */
963 return true;
964}
965
966
967int inv_mpu_set_firmware(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
968 const unsigned char *data, int size)
969{
970 int bank, offset, write_size;
971 int result;
972 unsigned char read[MPU_MEM_BANK_SIZE];
973
974 if (mldl_cfg->inv_mpu_state->status & MPU_DEVICE_IS_SUSPENDED) {
975#if INV_CACHE_DMP == 1
976 memcpy(mldl_cfg->mpu_ram->ram, data, size);
977 return INV_SUCCESS;
978#else
979 LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
980 return INV_ERROR_MEMORY_SET;
981#endif
982 }
983
984 if (!(mldl_cfg->inv_mpu_state->status & MPU_DMP_IS_SUSPENDED)) {
985 LOG_RESULT_LOCATION(INV_ERROR_MEMORY_SET);
986 return INV_ERROR_MEMORY_SET;
987 }
988 /* Write and verify memory */
989 for (bank = 0; size > 0; bank++,
990 size -= write_size,
991 data += write_size) {
992 if (size > MPU_MEM_BANK_SIZE)
993 write_size = MPU_MEM_BANK_SIZE;
994 else
995 write_size = size;
996
997 result = inv_serial_write_mem(mlsl_handle,
998 mldl_cfg->mpu_chip_info->addr,
999 ((bank << 8) | 0x00),
1000 write_size,
1001 data);
1002 if (result) {
1003 LOG_RESULT_LOCATION(result);
1004 MPL_LOGE("Write mem error in bank %d\n", bank);
1005 return result;
1006 }
1007 result = inv_serial_read_mem(mlsl_handle,
1008 mldl_cfg->mpu_chip_info->addr,
1009 ((bank << 8) | 0x00),
1010 write_size,
1011 read);
1012 if (result) {
1013 LOG_RESULT_LOCATION(result);
1014 MPL_LOGE("Read mem error in bank %d\n", bank);
1015 return result;
1016 }
1017
1018#define ML_SKIP_CHECK 38
1019 for (offset = 0; offset < write_size; offset++) {
1020 /* skip the register memory locations */
1021 if (bank == 0 && offset < ML_SKIP_CHECK)
1022 continue;
1023 if (data[offset] != read[offset]) {
1024 result = INV_ERROR_SERIAL_WRITE;
1025 break;
1026 }
1027 }
1028 if (result != INV_SUCCESS) {
1029 LOG_RESULT_LOCATION(result);
1030 MPL_LOGE("Read data mismatch at bank %d, offset %d\n",
1031 bank, offset);
1032 return result;
1033 }
1034 }
1035 return INV_SUCCESS;
1036}
1037
1038static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
1039 unsigned long sensors)
1040{
1041 int result;
1042 int ii;
1043 unsigned char reg;
1044 unsigned char regs[7];
1045
1046 /* Wake up the part */
1047 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, sensors);
1048 if (result) {
1049 LOG_RESULT_LOCATION(result);
1050 return result;
1051 }
1052
1053 /* Always set the INT_ENABLE and DIVIDER as the Accel Only mode for 6050
1054 can set these too */
1055 result = inv_serial_single_write(
1056 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1057 MPUREG_INT_ENABLE, (mldl_cfg->mpu_gyro_cfg->int_config));
1058 if (result) {
1059 LOG_RESULT_LOCATION(result);
1060 return result;
1061 }
1062 result = inv_serial_single_write(
1063 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1064 MPUREG_SMPLRT_DIV, mldl_cfg->mpu_gyro_cfg->divider);
1065 if (result) {
1066 LOG_RESULT_LOCATION(result);
1067 return result;
1068 }
1069
1070 if (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG) &&
1071 !mpu_was_reset(mldl_cfg, gyro_handle)) {
1072 return INV_SUCCESS;
1073 }
1074
1075 /* Configure the MPU */
1076 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1077 if (result) {
1078 LOG_RESULT_LOCATION(result);
1079 return result;
1080 }
1081 result = mpu_set_clock_source(gyro_handle, mldl_cfg);
1082 if (result) {
1083 LOG_RESULT_LOCATION(result);
1084 return result;
1085 }
1086
1087 reg = MPUREG_GYRO_CONFIG_VALUE(0, 0, 0,
1088 mldl_cfg->mpu_gyro_cfg->full_scale);
1089 result = inv_serial_single_write(
1090 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1091 MPUREG_GYRO_CONFIG, reg);
1092 reg = MPUREG_CONFIG_VALUE(mldl_cfg->mpu_gyro_cfg->ext_sync,
1093 mldl_cfg->mpu_gyro_cfg->lpf);
1094 result = inv_serial_single_write(
1095 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1096 MPUREG_CONFIG, reg);
1097 if (result) {
1098 LOG_RESULT_LOCATION(result);
1099 return result;
1100 }
1101 result = inv_serial_single_write(
1102 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1103 MPUREG_DMP_CFG_1, mldl_cfg->mpu_gyro_cfg->dmp_cfg1);
1104 if (result) {
1105 LOG_RESULT_LOCATION(result);
1106 return result;
1107 }
1108 result = inv_serial_single_write(
1109 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1110 MPUREG_DMP_CFG_2, mldl_cfg->mpu_gyro_cfg->dmp_cfg2);
1111 if (result) {
1112 LOG_RESULT_LOCATION(result);
1113 return result;
1114 }
1115
1116 /* Write and verify memory */
1117#if INV_CACHE_DMP != 0
1118 inv_mpu_set_firmware(mldl_cfg, gyro_handle,
1119 mldl_cfg->mpu_ram->ram, mldl_cfg->mpu_ram->length);
1120#endif
1121
1122 result = inv_serial_single_write(
1123 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1124 MPUREG_XG_OFFS_TC,
1125 ((mldl_cfg->mpu_offsets->tc[0] << 1) & BITS_XG_OFFS_TC));
1126 if (result) {
1127 LOG_RESULT_LOCATION(result);
1128 return result;
1129 }
1130 regs[0] = ((mldl_cfg->mpu_offsets->tc[1] << 1) & BITS_YG_OFFS_TC);
1131 result = inv_serial_single_write(
1132 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1133 MPUREG_YG_OFFS_TC, regs[0]);
1134 if (result) {
1135 LOG_RESULT_LOCATION(result);
1136 return result;
1137 }
1138 result = inv_serial_single_write(
1139 gyro_handle, mldl_cfg->mpu_chip_info->addr,
1140 MPUREG_ZG_OFFS_TC,
1141 ((mldl_cfg->mpu_offsets->tc[2] << 1) & BITS_ZG_OFFS_TC));
1142 if (result) {
1143 LOG_RESULT_LOCATION(result);
1144 return result;
1145 }
1146 regs[0] = MPUREG_X_OFFS_USRH;
1147 for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->mpu_offsets->gyro); ii++) {
1148 regs[1 + ii * 2] =
1149 (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] >> 8)
1150 & 0xff;
1151 regs[1 + ii * 2 + 1] =
1152 (unsigned char)(mldl_cfg->mpu_offsets->gyro[ii] & 0xff);
1153 }
1154 result = inv_serial_write(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1155 7, regs);
1156 if (result) {
1157 LOG_RESULT_LOCATION(result);
1158 return result;
1159 }
1160
1161 /* Configure slaves */
1162 result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
1163 mldl_cfg->pdata->level_shifter);
1164 if (result) {
1165 LOG_RESULT_LOCATION(result);
1166 return result;
1167 }
1168 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_NEEDS_CONFIG;
1169
1170 return result;
1171}
1172
1173int gyro_config(void *mlsl_handle,
1174 struct mldl_cfg *mldl_cfg,
1175 struct ext_slave_config *data)
1176{
1177 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
1178 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
1179 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
1180 int ii;
1181
1182 if (!data->data)
1183 return INV_ERROR_INVALID_PARAMETER;
1184
1185 switch (data->key) {
1186 case MPU_SLAVE_INT_CONFIG:
1187 mpu_gyro_cfg->int_config = *((__u8 *)data->data);
1188 break;
1189 case MPU_SLAVE_EXT_SYNC:
1190 mpu_gyro_cfg->ext_sync = *((__u8 *)data->data);
1191 break;
1192 case MPU_SLAVE_FULL_SCALE:
1193 mpu_gyro_cfg->full_scale = *((__u8 *)data->data);
1194 break;
1195 case MPU_SLAVE_LPF:
1196 mpu_gyro_cfg->lpf = *((__u8 *)data->data);
1197 break;
1198 case MPU_SLAVE_CLK_SRC:
1199 mpu_gyro_cfg->clk_src = *((__u8 *)data->data);
1200 break;
1201 case MPU_SLAVE_DIVIDER:
1202 mpu_gyro_cfg->divider = *((__u8 *)data->data);
1203 break;
1204 case MPU_SLAVE_DMP_ENABLE:
1205 mpu_gyro_cfg->dmp_enable = *((__u8 *)data->data);
1206 break;
1207 case MPU_SLAVE_FIFO_ENABLE:
1208 mpu_gyro_cfg->fifo_enable = *((__u8 *)data->data);
1209 break;
1210 case MPU_SLAVE_DMP_CFG1:
1211 mpu_gyro_cfg->dmp_cfg1 = *((__u8 *)data->data);
1212 break;
1213 case MPU_SLAVE_DMP_CFG2:
1214 mpu_gyro_cfg->dmp_cfg2 = *((__u8 *)data->data);
1215 break;
1216 case MPU_SLAVE_TC:
1217 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1218 mpu_offsets->tc[ii] = ((__u8 *)data->data)[ii];
1219 break;
1220 case MPU_SLAVE_GYRO:
1221 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1222 mpu_offsets->gyro[ii] = ((__u16 *)data->data)[ii];
1223 break;
1224 case MPU_SLAVE_ADDR:
1225 mpu_chip_info->addr = *((__u8 *)data->data);
1226 break;
1227 case MPU_SLAVE_PRODUCT_REVISION:
1228 mpu_chip_info->product_revision = *((__u8 *)data->data);
1229 break;
1230 case MPU_SLAVE_SILICON_REVISION:
1231 mpu_chip_info->silicon_revision = *((__u8 *)data->data);
1232 break;
1233 case MPU_SLAVE_PRODUCT_ID:
1234 mpu_chip_info->product_id = *((__u8 *)data->data);
1235 break;
1236 case MPU_SLAVE_GYRO_SENS_TRIM:
1237 mpu_chip_info->gyro_sens_trim = *((__u16 *)data->data);
1238 break;
1239 case MPU_SLAVE_ACCEL_SENS_TRIM:
1240 mpu_chip_info->accel_sens_trim = *((__u16 *)data->data);
1241 break;
1242 case MPU_SLAVE_RAM:
1243 if (data->len != mldl_cfg->mpu_ram->length)
1244 return INV_ERROR_INVALID_PARAMETER;
1245
1246 memcpy(mldl_cfg->mpu_ram->ram, data->data, data->len);
1247 break;
1248 default:
1249 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
1250 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1251 };
1252 mldl_cfg->inv_mpu_state->status |= MPU_GYRO_NEEDS_CONFIG;
1253 return INV_SUCCESS;
1254}
1255
1256int gyro_get_config(void *mlsl_handle,
1257 struct mldl_cfg *mldl_cfg,
1258 struct ext_slave_config *data)
1259{
1260 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
1261 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
1262 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
1263 int ii;
1264
1265 if (!data->data)
1266 return INV_ERROR_INVALID_PARAMETER;
1267
1268 switch (data->key) {
1269 case MPU_SLAVE_INT_CONFIG:
1270 *((__u8 *)data->data) = mpu_gyro_cfg->int_config;
1271 break;
1272 case MPU_SLAVE_EXT_SYNC:
1273 *((__u8 *)data->data) = mpu_gyro_cfg->ext_sync;
1274 break;
1275 case MPU_SLAVE_FULL_SCALE:
1276 *((__u8 *)data->data) = mpu_gyro_cfg->full_scale;
1277 break;
1278 case MPU_SLAVE_LPF:
1279 *((__u8 *)data->data) = mpu_gyro_cfg->lpf;
1280 break;
1281 case MPU_SLAVE_CLK_SRC:
1282 *((__u8 *)data->data) = mpu_gyro_cfg->clk_src;
1283 break;
1284 case MPU_SLAVE_DIVIDER:
1285 *((__u8 *)data->data) = mpu_gyro_cfg->divider;
1286 break;
1287 case MPU_SLAVE_DMP_ENABLE:
1288 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_enable;
1289 break;
1290 case MPU_SLAVE_FIFO_ENABLE:
1291 *((__u8 *)data->data) = mpu_gyro_cfg->fifo_enable;
1292 break;
1293 case MPU_SLAVE_DMP_CFG1:
1294 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg1;
1295 break;
1296 case MPU_SLAVE_DMP_CFG2:
1297 *((__u8 *)data->data) = mpu_gyro_cfg->dmp_cfg2;
1298 break;
1299 case MPU_SLAVE_TC:
1300 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1301 ((__u8 *)data->data)[ii] = mpu_offsets->tc[ii];
1302 break;
1303 case MPU_SLAVE_GYRO:
1304 for (ii = 0; ii < GYRO_NUM_AXES; ii++)
1305 ((__u16 *)data->data)[ii] = mpu_offsets->gyro[ii];
1306 break;
1307 case MPU_SLAVE_ADDR:
1308 *((__u8 *)data->data) = mpu_chip_info->addr;
1309 break;
1310 case MPU_SLAVE_PRODUCT_REVISION:
1311 *((__u8 *)data->data) = mpu_chip_info->product_revision;
1312 break;
1313 case MPU_SLAVE_SILICON_REVISION:
1314 *((__u8 *)data->data) = mpu_chip_info->silicon_revision;
1315 break;
1316 case MPU_SLAVE_PRODUCT_ID:
1317 *((__u8 *)data->data) = mpu_chip_info->product_id;
1318 break;
1319 case MPU_SLAVE_GYRO_SENS_TRIM:
1320 *((__u16 *)data->data) = mpu_chip_info->gyro_sens_trim;
1321 break;
1322 case MPU_SLAVE_ACCEL_SENS_TRIM:
1323 *((__u16 *)data->data) = mpu_chip_info->accel_sens_trim;
1324 break;
1325 case MPU_SLAVE_RAM:
1326 if (data->len != mldl_cfg->mpu_ram->length)
1327 return INV_ERROR_INVALID_PARAMETER;
1328
1329 memcpy(data->data, mldl_cfg->mpu_ram->ram, data->len);
1330 break;
1331 default:
1332 LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
1333 return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
1334 };
1335
1336 return INV_SUCCESS;
1337}
1338
1339
1340/*******************************************************************************
1341 *******************************************************************************
1342 * Exported functions
1343 *******************************************************************************
1344 ******************************************************************************/
1345
1346/**
1347 * Initializes the pdata structure to defaults.
1348 *
1349 * Opens the device to read silicon revision, product id and whoami.
1350 *
1351 * @mldl_cfg
1352 * The internal device configuration data structure.
1353 * @mlsl_handle
1354 * The serial communication handle.
1355 *
1356 * @return INV_SUCCESS if silicon revision, product id and woami are supported
1357 * by this software.
1358 */
1359int inv_mpu_open(struct mldl_cfg *mldl_cfg,
1360 void *gyro_handle,
1361 void *accel_handle,
1362 void *compass_handle, void *pressure_handle)
1363{
1364 int result;
1365 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1366 int ii;
1367
1368 /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
1369 ii = 0;
1370 mldl_cfg->inv_mpu_cfg->ignore_system_suspend = false;
1371 mldl_cfg->mpu_gyro_cfg->int_config = BIT_DMP_INT_EN;
1372 mldl_cfg->mpu_gyro_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
1373 mldl_cfg->mpu_gyro_cfg->lpf = MPU_FILTER_42HZ;
1374 mldl_cfg->mpu_gyro_cfg->full_scale = MPU_FS_2000DPS;
1375 mldl_cfg->mpu_gyro_cfg->divider = 4;
1376 mldl_cfg->mpu_gyro_cfg->dmp_enable = 1;
1377 mldl_cfg->mpu_gyro_cfg->fifo_enable = 1;
1378 mldl_cfg->mpu_gyro_cfg->ext_sync = 0;
1379 mldl_cfg->mpu_gyro_cfg->dmp_cfg1 = 0;
1380 mldl_cfg->mpu_gyro_cfg->dmp_cfg2 = 0;
1381 mldl_cfg->inv_mpu_state->status =
1382 MPU_DMP_IS_SUSPENDED |
1383 MPU_GYRO_IS_SUSPENDED |
1384 MPU_ACCEL_IS_SUSPENDED |
1385 MPU_COMPASS_IS_SUSPENDED |
1386 MPU_PRESSURE_IS_SUSPENDED |
1387 MPU_DEVICE_IS_SUSPENDED;
1388 mldl_cfg->inv_mpu_state->i2c_slaves_enabled = 0;
1389
1390 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1391 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1392 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1393 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1394
1395 if (mldl_cfg->mpu_chip_info->addr == 0) {
1396 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
1397 return INV_ERROR_INVALID_PARAMETER;
1398 }
1399
1400 /*
1401 * Reset,
1402 * Take the DMP out of sleep, and
1403 * read the product_id, sillicon rev and whoami
1404 */
1405 mldl_cfg->inv_mpu_state->status &= ~MPU_GYRO_IS_BYPASSED;
1406 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, true,
1407 INV_THREE_AXIS_GYRO);
1408 if (result) {
1409 LOG_RESULT_LOCATION(result);
1410 return result;
1411 }
1412
1413 result = inv_get_silicon_rev(mldl_cfg, gyro_handle);
1414 if (result) {
1415 LOG_RESULT_LOCATION(result);
1416 return result;
1417 }
1418
1419 /* Get the factory temperature compensation offsets */
1420 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1421 MPUREG_XG_OFFS_TC, 1,
1422 &mldl_cfg->mpu_offsets->tc[0]);
1423 if (result) {
1424 LOG_RESULT_LOCATION(result);
1425 return result;
1426 }
1427 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1428 MPUREG_YG_OFFS_TC, 1,
1429 &mldl_cfg->mpu_offsets->tc[1]);
1430 if (result) {
1431 LOG_RESULT_LOCATION(result);
1432 return result;
1433 }
1434 result = inv_serial_read(gyro_handle, mldl_cfg->mpu_chip_info->addr,
1435 MPUREG_ZG_OFFS_TC, 1,
1436 &mldl_cfg->mpu_offsets->tc[2]);
1437 if (result) {
1438 LOG_RESULT_LOCATION(result);
1439 return result;
1440 }
1441
1442 /* Into bypass mode before sleeping and calling the slaves init */
1443 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, true);
1444 if (result) {
1445 LOG_RESULT_LOCATION(result);
1446 return result;
1447 }
1448 result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
1449 mldl_cfg->pdata->level_shifter);
1450 if (result) {
1451 LOG_RESULT_LOCATION(result);
1452 return result;
1453 }
1454
1455 for (ii = 0; ii < GYRO_NUM_AXES; ii++) {
1456 mldl_cfg->mpu_offsets->tc[ii] =
1457 (mldl_cfg->mpu_offsets->tc[ii] & BITS_XG_OFFS_TC) >> 1;
1458 }
1459
1460#if INV_CACHE_DMP != 0
1461 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false, 0);
1462#endif
1463 if (result) {
1464 LOG_RESULT_LOCATION(result);
1465 return result;
1466 }
1467
1468
1469 return result;
1470
1471}
1472
1473/**
1474 * Close the mpu interface
1475 *
1476 * @mldl_cfg pointer to the configuration structure
1477 * @mlsl_handle pointer to the serial layer handle
1478 *
1479 * @return INV_SUCCESS or non-zero error code
1480 */
1481int inv_mpu_close(struct mldl_cfg *mldl_cfg,
1482 void *gyro_handle,
1483 void *accel_handle,
1484 void *compass_handle,
1485 void *pressure_handle)
1486{
1487 return 0;
1488}
1489
1490/**
1491 * @brief resume the MPU device and all the other sensor
1492 * devices from their low power state.
1493 *
1494 * @mldl_cfg
1495 * pointer to the configuration structure
1496 * @gyro_handle
1497 * the main file handle to the MPU device.
1498 * @accel_handle
1499 * an handle to the accelerometer device, if sitting
1500 * onto a separate bus. Can match mlsl_handle if
1501 * the accelerometer device operates on the same
1502 * primary bus of MPU.
1503 * @compass_handle
1504 * an handle to the compass device, if sitting
1505 * onto a separate bus. Can match mlsl_handle if
1506 * the compass device operates on the same
1507 * primary bus of MPU.
1508 * @pressure_handle
1509 * an handle to the pressure sensor device, if sitting
1510 * onto a separate bus. Can match mlsl_handle if
1511 * the pressure sensor device operates on the same
1512 * primary bus of MPU.
1513 * @resume_gyro
1514 * whether resuming the gyroscope device is
1515 * actually needed (if the device supports low power
1516 * mode of some sort).
1517 * @resume_accel
1518 * whether resuming the accelerometer device is
1519 * actually needed (if the device supports low power
1520 * mode of some sort).
1521 * @resume_compass
1522 * whether resuming the compass device is
1523 * actually needed (if the device supports low power
1524 * mode of some sort).
1525 * @resume_pressure
1526 * whether resuming the pressure sensor device is
1527 * actually needed (if the device supports low power
1528 * mode of some sort).
1529 * @return INV_SUCCESS or a non-zero error code.
1530 */
1531int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
1532 void *gyro_handle,
1533 void *accel_handle,
1534 void *compass_handle,
1535 void *pressure_handle,
1536 unsigned long sensors)
1537{
1538 int result = INV_SUCCESS;
1539 int ii;
1540 bool resume_slave[EXT_SLAVE_NUM_TYPES];
1541 bool resume_dmp = sensors & INV_DMP_PROCESSOR;
1542 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1543 resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
1544 (sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
1545 resume_slave[EXT_SLAVE_TYPE_ACCEL] =
1546 sensors & INV_THREE_AXIS_ACCEL;
1547 resume_slave[EXT_SLAVE_TYPE_COMPASS] =
1548 sensors & INV_THREE_AXIS_COMPASS;
1549 resume_slave[EXT_SLAVE_TYPE_PRESSURE] =
1550 sensors & INV_THREE_AXIS_PRESSURE;
1551
1552 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1553 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1554 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1555 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1556
1557
1558 mldl_print_cfg(mldl_cfg);
1559
1560 /* Skip the Gyro since slave[EXT_SLAVE_TYPE_GYROSCOPE] is NULL */
1561 for (ii = EXT_SLAVE_TYPE_ACCEL; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1562 if (resume_slave[ii] &&
1563 ((!mldl_cfg->slave[ii]) ||
1564 (!mldl_cfg->slave[ii]->resume))) {
1565 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
1566 return INV_ERROR_INVALID_PARAMETER;
1567 }
1568 }
1569
1570 if ((resume_slave[EXT_SLAVE_TYPE_GYROSCOPE] || resume_dmp)
1571 && ((mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED) ||
1572 (mldl_cfg->inv_mpu_state->status & MPU_GYRO_NEEDS_CONFIG))) {
1573 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1574 if (result) {
1575 LOG_RESULT_LOCATION(result);
1576 return result;
1577 }
1578 result = dmp_stop(mldl_cfg, gyro_handle);
1579 if (result) {
1580 LOG_RESULT_LOCATION(result);
1581 return result;
1582 }
1583 result = gyro_resume(mldl_cfg, gyro_handle, sensors);
1584 if (result) {
1585 LOG_RESULT_LOCATION(result);
1586 return result;
1587 }
1588 }
1589
1590 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1591 if (!mldl_cfg->slave[ii] ||
1592 !mldl_cfg->pdata_slave[ii] ||
1593 !resume_slave[ii] ||
1594 !(mldl_cfg->inv_mpu_state->status & (1 << ii)))
1595 continue;
1596
1597 if (EXT_SLAVE_BUS_SECONDARY ==
1598 mldl_cfg->pdata_slave[ii]->bus) {
1599 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
1600 true);
1601 if (result) {
1602 LOG_RESULT_LOCATION(result);
1603 return result;
1604 }
1605 }
1606 result = mldl_cfg->slave[ii]->resume(slave_handle[ii],
1607 mldl_cfg->slave[ii],
1608 mldl_cfg->pdata_slave[ii]);
1609 if (result) {
1610 LOG_RESULT_LOCATION(result);
1611 return result;
1612 }
1613 mldl_cfg->inv_mpu_state->status &= ~(1 << ii);
1614 }
1615
1616 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1617 if (resume_dmp &&
1618 !(mldl_cfg->inv_mpu_state->status & (1 << ii)) &&
1619 mldl_cfg->pdata_slave[ii] &&
1620 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata_slave[ii]->bus) {
1621 result = mpu_set_slave(mldl_cfg,
1622 gyro_handle,
1623 mldl_cfg->slave[ii],
1624 mldl_cfg->pdata_slave[ii],
1625 mldl_cfg->slave[ii]->type);
1626 if (result) {
1627 LOG_RESULT_LOCATION(result);
1628 return result;
1629 }
1630 }
1631 }
1632
1633 /* Turn on the master i2c iterface if necessary */
1634 if (resume_dmp) {
1635 result = mpu_set_i2c_bypass(
1636 mldl_cfg, gyro_handle,
1637 !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
1638 if (result) {
1639 LOG_RESULT_LOCATION(result);
1640 return result;
1641 }
1642
1643 /* Now start */
1644 result = dmp_start(mldl_cfg, gyro_handle);
1645 if (result) {
1646 LOG_RESULT_LOCATION(result);
1647 return result;
1648 }
1649 }
1650 mldl_cfg->inv_mpu_cfg->requested_sensors = sensors;
1651
1652 return result;
1653}
1654
1655/**
1656 * @brief suspend the MPU device and all the other sensor
1657 * devices into their low power state.
1658 * @mldl_cfg
1659 * a pointer to the struct mldl_cfg internal data
1660 * structure.
1661 * @gyro_handle
1662 * the main file handle to the MPU device.
1663 * @accel_handle
1664 * an handle to the accelerometer device, if sitting
1665 * onto a separate bus. Can match gyro_handle if
1666 * the accelerometer device operates on the same
1667 * primary bus of MPU.
1668 * @compass_handle
1669 * an handle to the compass device, if sitting
1670 * onto a separate bus. Can match gyro_handle if
1671 * the compass device operates on the same
1672 * primary bus of MPU.
1673 * @pressure_handle
1674 * an handle to the pressure sensor device, if sitting
1675 * onto a separate bus. Can match gyro_handle if
1676 * the pressure sensor device operates on the same
1677 * primary bus of MPU.
1678 * @accel
1679 * whether suspending the accelerometer device is
1680 * actually needed (if the device supports low power
1681 * mode of some sort).
1682 * @compass
1683 * whether suspending the compass device is
1684 * actually needed (if the device supports low power
1685 * mode of some sort).
1686 * @pressure
1687 * whether suspending the pressure sensor device is
1688 * actually needed (if the device supports low power
1689 * mode of some sort).
1690 * @return INV_SUCCESS or a non-zero error code.
1691 */
1692int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
1693 void *gyro_handle,
1694 void *accel_handle,
1695 void *compass_handle,
1696 void *pressure_handle,
1697 unsigned long sensors)
1698{
1699 int result = INV_SUCCESS;
1700 int ii;
1701 struct ext_slave_descr **slave = mldl_cfg->slave;
1702 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
1703 bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR);
1704 bool suspend_slave[EXT_SLAVE_NUM_TYPES];
1705 void *slave_handle[EXT_SLAVE_NUM_TYPES];
1706
1707 suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] =
1708 ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO))
1709 == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
1710 suspend_slave[EXT_SLAVE_TYPE_ACCEL] =
1711 ((sensors & INV_THREE_AXIS_ACCEL) == INV_THREE_AXIS_ACCEL);
1712 suspend_slave[EXT_SLAVE_TYPE_COMPASS] =
1713 ((sensors & INV_THREE_AXIS_COMPASS) == INV_THREE_AXIS_COMPASS);
1714 suspend_slave[EXT_SLAVE_TYPE_PRESSURE] =
1715 ((sensors & INV_THREE_AXIS_PRESSURE) ==
1716 INV_THREE_AXIS_PRESSURE);
1717
1718 slave_handle[EXT_SLAVE_TYPE_GYROSCOPE] = gyro_handle;
1719 slave_handle[EXT_SLAVE_TYPE_ACCEL] = accel_handle;
1720 slave_handle[EXT_SLAVE_TYPE_COMPASS] = compass_handle;
1721 slave_handle[EXT_SLAVE_TYPE_PRESSURE] = pressure_handle;
1722
1723 if (suspend_dmp) {
1724 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1725 if (result) {
1726 LOG_RESULT_LOCATION(result);
1727 return result;
1728 }
1729 result = dmp_stop(mldl_cfg, gyro_handle);
1730 if (result) {
1731 LOG_RESULT_LOCATION(result);
1732 return result;
1733 }
1734 }
1735
1736 /* Gyro */
1737 if (suspend_slave[EXT_SLAVE_TYPE_GYROSCOPE] &&
1738 !(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_SUSPENDED)) {
1739 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, false,
1740 ((~sensors) & INV_ALL_SENSORS));
1741 if (result) {
1742 LOG_RESULT_LOCATION(result);
1743 return result;
1744 }
1745 }
1746
1747 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1748 bool is_suspended = mldl_cfg->inv_mpu_state->status & (1 << ii);
1749 if (!slave[ii] || !pdata_slave[ii] ||
1750 is_suspended || !suspend_slave[ii])
1751 continue;
1752
1753 if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
1754 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1755 if (result) {
1756 LOG_RESULT_LOCATION(result);
1757 return result;
1758 }
1759 }
1760 result = slave[ii]->suspend(slave_handle[ii],
1761 slave[ii],
1762 pdata_slave[ii]);
1763 if (result) {
1764 LOG_RESULT_LOCATION(result);
1765 return result;
1766 }
1767 if (EXT_SLAVE_BUS_SECONDARY == pdata_slave[ii]->bus) {
1768 result = mpu_set_slave(mldl_cfg, gyro_handle,
1769 NULL, NULL,
1770 slave[ii]->type);
1771 if (result) {
1772 LOG_RESULT_LOCATION(result);
1773 return result;
1774 }
1775 }
1776 mldl_cfg->inv_mpu_state->status |= (1 << ii);
1777 }
1778
1779 /* Re-enable the i2c master if there are configured slaves and DMP */
1780 if (!suspend_dmp) {
1781 result = mpu_set_i2c_bypass(
1782 mldl_cfg, gyro_handle,
1783 !(mldl_cfg->inv_mpu_state->i2c_slaves_enabled));
1784 if (result) {
1785 LOG_RESULT_LOCATION(result);
1786 return result;
1787 }
1788 }
1789 mldl_cfg->inv_mpu_cfg->requested_sensors = (~sensors) & INV_ALL_SENSORS;
1790
1791 return result;
1792}
1793
1794int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
1795 void *gyro_handle,
1796 void *slave_handle,
1797 struct ext_slave_descr *slave,
1798 struct ext_slave_platform_data *pdata,
1799 unsigned char *data)
1800{
1801 int result;
1802 int bypass_result;
1803 int remain_bypassed = true;
1804
1805 if (NULL == slave || NULL == slave->read) {
1806 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1807 return INV_ERROR_INVALID_CONFIGURATION;
1808 }
1809
1810 if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1811 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1812 remain_bypassed = false;
1813 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1814 if (result) {
1815 LOG_RESULT_LOCATION(result);
1816 return result;
1817 }
1818 }
1819
1820 result = slave->read(slave_handle, slave, pdata, data);
1821
1822 if (!remain_bypassed) {
1823 bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1824 if (bypass_result) {
1825 LOG_RESULT_LOCATION(bypass_result);
1826 return bypass_result;
1827 }
1828 }
1829 return result;
1830}
1831
1832int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
1833 void *gyro_handle,
1834 void *slave_handle,
1835 struct ext_slave_config *data,
1836 struct ext_slave_descr *slave,
1837 struct ext_slave_platform_data *pdata)
1838{
1839 int result;
1840 int remain_bypassed = true;
1841
1842 if (NULL == slave || NULL == slave->config) {
1843 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1844 return INV_ERROR_INVALID_CONFIGURATION;
1845 }
1846
1847 if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1848 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1849 remain_bypassed = false;
1850 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1851 if (result) {
1852 LOG_RESULT_LOCATION(result);
1853 return result;
1854 }
1855 }
1856
1857 result = slave->config(slave_handle, slave, pdata, data);
1858 if (result) {
1859 LOG_RESULT_LOCATION(result);
1860 return result;
1861 }
1862
1863 if (!remain_bypassed) {
1864 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1865 if (result) {
1866 LOG_RESULT_LOCATION(result);
1867 return result;
1868 }
1869 }
1870 return result;
1871}
1872
1873int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
1874 void *gyro_handle,
1875 void *slave_handle,
1876 struct ext_slave_config *data,
1877 struct ext_slave_descr *slave,
1878 struct ext_slave_platform_data *pdata)
1879{
1880 int result;
1881 int remain_bypassed = true;
1882
1883 if (NULL == slave || NULL == slave->get_config) {
1884 LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
1885 return INV_ERROR_INVALID_CONFIGURATION;
1886 }
1887
1888 if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
1889 && (!(mldl_cfg->inv_mpu_state->status & MPU_GYRO_IS_BYPASSED))) {
1890 remain_bypassed = false;
1891 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
1892 if (result) {
1893 LOG_RESULT_LOCATION(result);
1894 return result;
1895 }
1896 }
1897
1898 result = slave->get_config(slave_handle, slave, pdata, data);
1899 if (result) {
1900 LOG_RESULT_LOCATION(result);
1901 return result;
1902 }
1903
1904 if (!remain_bypassed) {
1905 result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
1906 if (result) {
1907 LOG_RESULT_LOCATION(result);
1908 return result;
1909 }
1910 }
1911 return result;
1912}
1913
1914/**
1915 * @}
1916 */
diff --git a/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c
new file mode 100644
index 00000000000..7379a170761
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mldl_print_cfg.c
@@ -0,0 +1,138 @@
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 MLDL
22 *
23 * @{
24 * @file mldl_print_cfg.c
25 * @brief The Motion Library Driver Layer.
26 */
27
28#include <stddef.h>
29#include "mldl_cfg.h"
30#include "mlsl.h"
31#include "linux/mpu.h"
32
33#include "log.h"
34#undef MPL_LOG_TAG
35#define MPL_LOG_TAG "mldl_print_cfg:"
36
37#undef MPL_LOG_NDEBUG
38#define MPL_LOG_NDEBUG 1
39
40void mldl_print_cfg(struct mldl_cfg *mldl_cfg)
41{
42 struct mpu_gyro_cfg *mpu_gyro_cfg = mldl_cfg->mpu_gyro_cfg;
43 struct mpu_offsets *mpu_offsets = mldl_cfg->mpu_offsets;
44 struct mpu_chip_info *mpu_chip_info = mldl_cfg->mpu_chip_info;
45 struct inv_mpu_cfg *inv_mpu_cfg = mldl_cfg->inv_mpu_cfg;
46 struct inv_mpu_state *inv_mpu_state = mldl_cfg->inv_mpu_state;
47 struct ext_slave_descr **slave = mldl_cfg->slave;
48 struct mpu_platform_data *pdata = mldl_cfg->pdata;
49 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
50 int ii;
51
52 /* mpu_gyro_cfg */
53 MPL_LOGV("int_config = %02x\n", mpu_gyro_cfg->int_config);
54 MPL_LOGV("ext_sync = %02x\n", mpu_gyro_cfg->ext_sync);
55 MPL_LOGV("full_scale = %02x\n", mpu_gyro_cfg->full_scale);
56 MPL_LOGV("lpf = %02x\n", mpu_gyro_cfg->lpf);
57 MPL_LOGV("clk_src = %02x\n", mpu_gyro_cfg->clk_src);
58 MPL_LOGV("divider = %02x\n", mpu_gyro_cfg->divider);
59 MPL_LOGV("dmp_enable = %02x\n", mpu_gyro_cfg->dmp_enable);
60 MPL_LOGV("fifo_enable = %02x\n", mpu_gyro_cfg->fifo_enable);
61 MPL_LOGV("dmp_cfg1 = %02x\n", mpu_gyro_cfg->dmp_cfg1);
62 MPL_LOGV("dmp_cfg2 = %02x\n", mpu_gyro_cfg->dmp_cfg2);
63 /* mpu_offsets */
64 MPL_LOGV("tc[0] = %02x\n", mpu_offsets->tc[0]);
65 MPL_LOGV("tc[1] = %02x\n", mpu_offsets->tc[1]);
66 MPL_LOGV("tc[2] = %02x\n", mpu_offsets->tc[2]);
67 MPL_LOGV("gyro[0] = %04x\n", mpu_offsets->gyro[0]);
68 MPL_LOGV("gyro[1] = %04x\n", mpu_offsets->gyro[1]);
69 MPL_LOGV("gyro[2] = %04x\n", mpu_offsets->gyro[2]);
70
71 /* mpu_chip_info */
72 MPL_LOGV("addr = %02x\n", mldl_cfg->mpu_chip_info->addr);
73
74 MPL_LOGV("silicon_revision = %02x\n", mpu_chip_info->silicon_revision);
75 MPL_LOGV("product_revision = %02x\n", mpu_chip_info->product_revision);
76 MPL_LOGV("product_id = %02x\n", mpu_chip_info->product_id);
77 MPL_LOGV("gyro_sens_trim = %02x\n", mpu_chip_info->gyro_sens_trim);
78 MPL_LOGV("accel_sens_trim = %02x\n", mpu_chip_info->accel_sens_trim);
79
80 MPL_LOGV("requested_sensors = %04x\n", inv_mpu_cfg->requested_sensors);
81 MPL_LOGV("ignore_system_suspend= %04x\n",
82 inv_mpu_cfg->ignore_system_suspend);
83 MPL_LOGV("status = %04x\n", inv_mpu_state->status);
84 MPL_LOGV("i2c_slaves_enabled= %04x\n",
85 inv_mpu_state->i2c_slaves_enabled);
86
87 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
88 if (!slave[ii])
89 continue;
90 MPL_LOGV("SLAVE %d:\n", ii);
91 MPL_LOGV(" suspend = %02x\n", (int)slave[ii]->suspend);
92 MPL_LOGV(" resume = %02x\n", (int)slave[ii]->resume);
93 MPL_LOGV(" read = %02x\n", (int)slave[ii]->read);
94 MPL_LOGV(" type = %02x\n", slave[ii]->type);
95 MPL_LOGV(" reg = %02x\n", slave[ii]->read_reg);
96 MPL_LOGV(" len = %02x\n", slave[ii]->read_len);
97 MPL_LOGV(" endian = %02x\n", slave[ii]->endian);
98 MPL_LOGV(" range.mantissa= %02x\n",
99 slave[ii]->range.mantissa);
100 MPL_LOGV(" range.fraction= %02x\n",
101 slave[ii]->range.fraction);
102 }
103
104 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
105 if (!pdata_slave[ii])
106 continue;
107 MPL_LOGV("PDATA_SLAVE[%d]\n", ii);
108 MPL_LOGV(" irq = %02x\n", pdata_slave[ii]->irq);
109 MPL_LOGV(" adapt_num = %02x\n", pdata_slave[ii]->adapt_num);
110 MPL_LOGV(" bus = %02x\n", pdata_slave[ii]->bus);
111 MPL_LOGV(" address = %02x\n", pdata_slave[ii]->address);
112 MPL_LOGV(" orientation=\n"
113 " %2d %2d %2d\n"
114 " %2d %2d %2d\n"
115 " %2d %2d %2d\n",
116 pdata_slave[ii]->orientation[0],
117 pdata_slave[ii]->orientation[1],
118 pdata_slave[ii]->orientation[2],
119 pdata_slave[ii]->orientation[3],
120 pdata_slave[ii]->orientation[4],
121 pdata_slave[ii]->orientation[5],
122 pdata_slave[ii]->orientation[6],
123 pdata_slave[ii]->orientation[7],
124 pdata_slave[ii]->orientation[8]);
125 }
126
127 MPL_LOGV("pdata->int_config = %02x\n", pdata->int_config);
128 MPL_LOGV("pdata->level_shifter = %02x\n", pdata->level_shifter);
129 MPL_LOGV("pdata->orientation =\n"
130 " %2d %2d %2d\n"
131 " %2d %2d %2d\n"
132 " %2d %2d %2d\n",
133 pdata->orientation[0], pdata->orientation[1],
134 pdata->orientation[2], pdata->orientation[3],
135 pdata->orientation[4], pdata->orientation[5],
136 pdata->orientation[6], pdata->orientation[7],
137 pdata->orientation[8]);
138}
diff --git a/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c
new file mode 100644
index 00000000000..f1c228f48fe
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mlsl-kernel.c
@@ -0,0 +1,420 @@
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#include "mlsl.h"
21#include <linux/i2c.h>
22#include "log.h"
23#include "mpu6050b1.h"
24
25static int inv_i2c_write(struct i2c_adapter *i2c_adap,
26 unsigned char address,
27 unsigned int len, unsigned char const *data)
28{
29 struct i2c_msg msgs[1];
30 int res;
31
32 if (!data || !i2c_adap) {
33 LOG_RESULT_LOCATION(-EINVAL);
34 return -EINVAL;
35 }
36
37 msgs[0].addr = address;
38 msgs[0].flags = 0; /* write */
39 msgs[0].buf = (unsigned char *)data;
40 msgs[0].len = len;
41
42 res = i2c_transfer(i2c_adap, msgs, 1);
43 if (res < 1) {
44 if (res == 0)
45 res = -EIO;
46 LOG_RESULT_LOCATION(res);
47 return res;
48 } else
49 return 0;
50}
51
52static int inv_i2c_write_register(struct i2c_adapter *i2c_adap,
53 unsigned char address,
54 unsigned char reg, unsigned char value)
55{
56 unsigned char data[2];
57
58 data[0] = reg;
59 data[1] = value;
60 return inv_i2c_write(i2c_adap, address, 2, data);
61}
62
63static int inv_i2c_read(struct i2c_adapter *i2c_adap,
64 unsigned char address, unsigned char reg,
65 unsigned int len, unsigned char *data)
66{
67 struct i2c_msg msgs[2];
68 int res;
69
70 if (!data || !i2c_adap) {
71 LOG_RESULT_LOCATION(-EINVAL);
72 return -EINVAL;
73 }
74
75 msgs[0].addr = address;
76 msgs[0].flags = 0; /* write */
77 msgs[0].buf = &reg;
78 msgs[0].len = 1;
79
80 msgs[1].addr = address;
81 msgs[1].flags = I2C_M_RD;
82 msgs[1].buf = data;
83 msgs[1].len = len;
84
85 res = i2c_transfer(i2c_adap, msgs, 2);
86 if (res < 2) {
87 if (res >= 0)
88 res = -EIO;
89 LOG_RESULT_LOCATION(res);
90 return res;
91 } else
92 return 0;
93}
94
95static int mpu_memory_read(struct i2c_adapter *i2c_adap,
96 unsigned char mpu_addr,
97 unsigned short mem_addr,
98 unsigned int len, unsigned char *data)
99{
100 unsigned char bank[2];
101 unsigned char addr[2];
102 unsigned char buf;
103
104 struct i2c_msg msgs[4];
105 int res;
106
107 if (!data || !i2c_adap) {
108 LOG_RESULT_LOCATION(-EINVAL);
109 return -EINVAL;
110 }
111
112 bank[0] = MPUREG_BANK_SEL;
113 bank[1] = mem_addr >> 8;
114
115 addr[0] = MPUREG_MEM_START_ADDR;
116 addr[1] = mem_addr & 0xFF;
117
118 buf = MPUREG_MEM_R_W;
119
120 /* write message */
121 msgs[0].addr = mpu_addr;
122 msgs[0].flags = 0;
123 msgs[0].buf = bank;
124 msgs[0].len = sizeof(bank);
125
126 msgs[1].addr = mpu_addr;
127 msgs[1].flags = 0;
128 msgs[1].buf = addr;
129 msgs[1].len = sizeof(addr);
130
131 msgs[2].addr = mpu_addr;
132 msgs[2].flags = 0;
133 msgs[2].buf = &buf;
134 msgs[2].len = 1;
135
136 msgs[3].addr = mpu_addr;
137 msgs[3].flags = I2C_M_RD;
138 msgs[3].buf = data;
139 msgs[3].len = len;
140
141 res = i2c_transfer(i2c_adap, msgs, 4);
142 if (res != 4) {
143 if (res >= 0)
144 res = -EIO;
145 LOG_RESULT_LOCATION(res);
146 return res;
147 } else
148 return 0;
149}
150
151static int mpu_memory_write(struct i2c_adapter *i2c_adap,
152 unsigned char mpu_addr,
153 unsigned short mem_addr,
154 unsigned int len, unsigned char const *data)
155{
156 unsigned char bank[2];
157 unsigned char addr[2];
158 unsigned char buf[513];
159
160 struct i2c_msg msgs[3];
161 int res;
162
163 if (!data || !i2c_adap) {
164 LOG_RESULT_LOCATION(-EINVAL);
165 return -EINVAL;
166 }
167 if (len >= (sizeof(buf) - 1)) {
168 LOG_RESULT_LOCATION(-ENOMEM);
169 return -ENOMEM;
170 }
171
172 bank[0] = MPUREG_BANK_SEL;
173 bank[1] = mem_addr >> 8;
174
175 addr[0] = MPUREG_MEM_START_ADDR;
176 addr[1] = mem_addr & 0xFF;
177
178 buf[0] = MPUREG_MEM_R_W;
179 memcpy(buf + 1, data, len);
180
181 /* write message */
182 msgs[0].addr = mpu_addr;
183 msgs[0].flags = 0;
184 msgs[0].buf = bank;
185 msgs[0].len = sizeof(bank);
186
187 msgs[1].addr = mpu_addr;
188 msgs[1].flags = 0;
189 msgs[1].buf = addr;
190 msgs[1].len = sizeof(addr);
191
192 msgs[2].addr = mpu_addr;
193 msgs[2].flags = 0;
194 msgs[2].buf = (unsigned char *)buf;
195 msgs[2].len = len + 1;
196
197 res = i2c_transfer(i2c_adap, msgs, 3);
198 if (res != 3) {
199 if (res >= 0)
200 res = -EIO;
201 LOG_RESULT_LOCATION(res);
202 return res;
203 } else
204 return 0;
205}
206
207int inv_serial_single_write(
208 void *sl_handle,
209 unsigned char slave_addr,
210 unsigned char register_addr,
211 unsigned char data)
212{
213 return inv_i2c_write_register((struct i2c_adapter *)sl_handle,
214 slave_addr, register_addr, data);
215}
216EXPORT_SYMBOL(inv_serial_single_write);
217
218int inv_serial_write(
219 void *sl_handle,
220 unsigned char slave_addr,
221 unsigned short length,
222 unsigned char const *data)
223{
224 int result;
225 const unsigned short data_length = length - 1;
226 const unsigned char start_reg_addr = data[0];
227 unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
228 unsigned short bytes_written = 0;
229
230 while (bytes_written < data_length) {
231 unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE,
232 data_length - bytes_written);
233 if (bytes_written == 0) {
234 result = inv_i2c_write((struct i2c_adapter *)
235 sl_handle, slave_addr,
236 1 + this_len, data);
237 } else {
238 /* manually increment register addr between chunks */
239 i2c_write[0] = start_reg_addr + bytes_written;
240 memcpy(&i2c_write[1], &data[1 + bytes_written],
241 this_len);
242 result = inv_i2c_write((struct i2c_adapter *)
243 sl_handle, slave_addr,
244 1 + this_len, i2c_write);
245 }
246 if (result) {
247 LOG_RESULT_LOCATION(result);
248 return result;
249 }
250 bytes_written += this_len;
251 }
252 return 0;
253}
254EXPORT_SYMBOL(inv_serial_write);
255
256int inv_serial_read(
257 void *sl_handle,
258 unsigned char slave_addr,
259 unsigned char register_addr,
260 unsigned short length,
261 unsigned char *data)
262{
263 int result;
264 unsigned short bytes_read = 0;
265
266 if ((slave_addr & 0x7E) == DEFAULT_MPU_SLAVEADDR
267 && (register_addr == MPUREG_FIFO_R_W ||
268 register_addr == MPUREG_MEM_R_W)) {
269 LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
270 return INV_ERROR_INVALID_PARAMETER;
271 }
272
273 while (bytes_read < length) {
274 unsigned short this_len =
275 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
276 result = inv_i2c_read((struct i2c_adapter *)sl_handle,
277 slave_addr, register_addr + bytes_read,
278 this_len, &data[bytes_read]);
279 if (result) {
280 LOG_RESULT_LOCATION(result);
281 return result;
282 }
283 bytes_read += this_len;
284 }
285 return 0;
286}
287EXPORT_SYMBOL(inv_serial_read);
288
289int inv_serial_write_mem(
290 void *sl_handle,
291 unsigned char slave_addr,
292 unsigned short mem_addr,
293 unsigned short length,
294 unsigned char const *data)
295{
296 int result;
297 unsigned short bytes_written = 0;
298
299 if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
300 pr_err("memory read length (%d B) extends beyond its"
301 " limits (%d) if started at location %d\n", length,
302 MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
303 return INV_ERROR_INVALID_PARAMETER;
304 }
305 while (bytes_written < length) {
306 unsigned short this_len =
307 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
308 result = mpu_memory_write((struct i2c_adapter *)sl_handle,
309 slave_addr, mem_addr + bytes_written,
310 this_len, &data[bytes_written]);
311 if (result) {
312 LOG_RESULT_LOCATION(result);
313 return result;
314 }
315 bytes_written += this_len;
316 }
317 return 0;
318}
319EXPORT_SYMBOL(inv_serial_write_mem);
320
321int inv_serial_read_mem(
322 void *sl_handle,
323 unsigned char slave_addr,
324 unsigned short mem_addr,
325 unsigned short length,
326 unsigned char *data)
327{
328 int result;
329 unsigned short bytes_read = 0;
330
331 if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
332 printk
333 ("memory read length (%d B) extends beyond its limits (%d) "
334 "if started at location %d\n", length,
335 MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
336 return INV_ERROR_INVALID_PARAMETER;
337 }
338 while (bytes_read < length) {
339 unsigned short this_len =
340 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
341 result =
342 mpu_memory_read((struct i2c_adapter *)sl_handle,
343 slave_addr, mem_addr + bytes_read,
344 this_len, &data[bytes_read]);
345 if (result) {
346 LOG_RESULT_LOCATION(result);
347 return result;
348 }
349 bytes_read += this_len;
350 }
351 return 0;
352}
353EXPORT_SYMBOL(inv_serial_read_mem);
354
355int inv_serial_write_fifo(
356 void *sl_handle,
357 unsigned char slave_addr,
358 unsigned short length,
359 unsigned char const *data)
360{
361 int result;
362 unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
363 unsigned short bytes_written = 0;
364
365 if (length > FIFO_HW_SIZE) {
366 printk(KERN_ERR
367 "maximum fifo write length is %d\n", FIFO_HW_SIZE);
368 return INV_ERROR_INVALID_PARAMETER;
369 }
370 while (bytes_written < length) {
371 unsigned short this_len =
372 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
373 i2c_write[0] = MPUREG_FIFO_R_W;
374 memcpy(&i2c_write[1], &data[bytes_written], this_len);
375 result = inv_i2c_write((struct i2c_adapter *)sl_handle,
376 slave_addr, this_len + 1, i2c_write);
377 if (result) {
378 LOG_RESULT_LOCATION(result);
379 return result;
380 }
381 bytes_written += this_len;
382 }
383 return 0;
384}
385EXPORT_SYMBOL(inv_serial_write_fifo);
386
387int inv_serial_read_fifo(
388 void *sl_handle,
389 unsigned char slave_addr,
390 unsigned short length,
391 unsigned char *data)
392{
393 int result;
394 unsigned short bytes_read = 0;
395
396 if (length > FIFO_HW_SIZE) {
397 printk(KERN_ERR
398 "maximum fifo read length is %d\n", FIFO_HW_SIZE);
399 return INV_ERROR_INVALID_PARAMETER;
400 }
401 while (bytes_read < length) {
402 unsigned short this_len =
403 min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
404 result = inv_i2c_read((struct i2c_adapter *)sl_handle,
405 slave_addr, MPUREG_FIFO_R_W, this_len,
406 &data[bytes_read]);
407 if (result) {
408 LOG_RESULT_LOCATION(result);
409 return result;
410 }
411 bytes_read += this_len;
412 }
413
414 return 0;
415}
416EXPORT_SYMBOL(inv_serial_read_fifo);
417
418/**
419 * @}
420 */
diff --git a/drivers/misc/inv_mpu/mpu6050/mpu-dev.c b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c
new file mode 100644
index 00000000000..e171dc2a7ef
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050/mpu-dev.c
@@ -0,0 +1,1309 @@
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#include <linux/i2c.h>
20#include <linux/i2c-dev.h>
21#include <linux/interrupt.h>
22#include <linux/module.h>
23#include <linux/moduleparam.h>
24#include <linux/kernel.h>
25#include <linux/init.h>
26#include <linux/stat.h>
27#include <linux/irq.h>
28#include <linux/gpio.h>
29#include <linux/signal.h>
30#include <linux/miscdevice.h>
31#include <linux/slab.h>
32#include <linux/version.h>
33#include <linux/pm.h>
34#include <linux/mutex.h>
35#include <linux/suspend.h>
36#include <linux/poll.h>
37
38#include <linux/errno.h>
39#include <linux/fs.h>
40#include <linux/mm.h>
41#include <linux/sched.h>
42#include <linux/wait.h>
43#include <linux/uaccess.h>
44#include <linux/io.h>
45
46#include "mpuirq.h"
47#include "slaveirq.h"
48#include "mlsl.h"
49#include "mldl_cfg.h"
50#include <linux/mpu.h>
51
52#include "accel/mpu6050.h"
53
54/* Platform data for the MPU */
55struct mpu_private_data {
56 struct miscdevice dev;
57 struct i2c_client *client;
58
59 /* mldl_cfg data */
60 struct mldl_cfg mldl_cfg;
61 struct mpu_ram mpu_ram;
62 struct mpu_gyro_cfg mpu_gyro_cfg;
63 struct mpu_offsets mpu_offsets;
64 struct mpu_chip_info mpu_chip_info;
65 struct inv_mpu_cfg inv_mpu_cfg;
66 struct inv_mpu_state inv_mpu_state;
67
68 struct mutex mutex;
69 wait_queue_head_t mpu_event_wait;
70 struct completion completion;
71 struct timer_list timeout;
72 struct notifier_block nb;
73 struct mpuirq_data mpu_pm_event;
74 int response_timeout; /* In seconds */
75 unsigned long event;
76 int pid;
77 struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
78};
79
80struct mpu_private_data *mpu_private_data;
81
82static void mpu_pm_timeout(u_long data)
83{
84 struct mpu_private_data *mpu = (struct mpu_private_data *)data;
85 struct i2c_client *client = mpu->client;
86 dev_dbg(&client->adapter->dev, "%s\n", __func__);
87 complete(&mpu->completion);
88}
89
90static int mpu_pm_notifier_callback(struct notifier_block *nb,
91 unsigned long event, void *unused)
92{
93 struct mpu_private_data *mpu =
94 container_of(nb, struct mpu_private_data, nb);
95 struct i2c_client *client = mpu->client;
96 struct timeval event_time;
97 dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
98
99 /* Prevent the file handle from being closed before we initialize
100 the completion event */
101 mutex_lock(&mpu->mutex);
102 if (!(mpu->pid) ||
103 (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
104 mutex_unlock(&mpu->mutex);
105 return NOTIFY_OK;
106 }
107
108 if (event == PM_SUSPEND_PREPARE)
109 mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
110 if (event == PM_POST_SUSPEND)
111 mpu->event = MPU_PM_EVENT_POST_SUSPEND;
112
113 do_gettimeofday(&event_time);
114 mpu->mpu_pm_event.interruptcount++;
115 mpu->mpu_pm_event.irqtime =
116 (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
117 mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
118 mpu->mpu_pm_event.data = mpu->event;
119
120 if (mpu->response_timeout > 0) {
121 mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
122 add_timer(&mpu->timeout);
123 }
124 INIT_COMPLETION(mpu->completion);
125 mutex_unlock(&mpu->mutex);
126
127 wake_up_interruptible(&mpu->mpu_event_wait);
128 wait_for_completion(&mpu->completion);
129 del_timer_sync(&mpu->timeout);
130 dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
131 return NOTIFY_OK;
132}
133
134static int mpu_dev_open(struct inode *inode, struct file *file)
135{
136 struct mpu_private_data *mpu =
137 container_of(file->private_data, struct mpu_private_data, dev);
138 struct i2c_client *client = mpu->client;
139 int result;
140 int ii;
141 dev_dbg(&client->adapter->dev, "%s\n", __func__);
142 dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
143
144 result = mutex_lock_interruptible(&mpu->mutex);
145 if (mpu->pid) {
146 mutex_unlock(&mpu->mutex);
147 return -EBUSY;
148 }
149 mpu->pid = current->pid;
150
151 /* Reset the sensors to the default */
152 if (result) {
153 dev_err(&client->adapter->dev,
154 "%s: mutex_lock_interruptible returned %d\n",
155 __func__, result);
156 return result;
157 }
158
159 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
160 __module_get(mpu->slave_modules[ii]);
161
162 mutex_unlock(&mpu->mutex);
163 return 0;
164}
165
166/* close function - called when the "file" /dev/mpu is closed in userspace */
167static int mpu_release(struct inode *inode, struct file *file)
168{
169 struct mpu_private_data *mpu =
170 container_of(file->private_data, struct mpu_private_data, dev);
171 struct i2c_client *client = mpu->client;
172 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
173 int result = 0;
174 int ii;
175 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
176 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
177
178 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
179 if (!pdata_slave[ii])
180 slave_adapter[ii] = NULL;
181 else
182 slave_adapter[ii] =
183 i2c_get_adapter(pdata_slave[ii]->adapt_num);
184 }
185 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
186
187 mutex_lock(&mpu->mutex);
188 mldl_cfg->inv_mpu_cfg->requested_sensors = 0;
189 result = inv_mpu_suspend(mldl_cfg,
190 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
191 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
192 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
193 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
194 INV_ALL_SENSORS);
195 mpu->pid = 0;
196 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
197 module_put(mpu->slave_modules[ii]);
198
199 mutex_unlock(&mpu->mutex);
200 complete(&mpu->completion);
201 dev_dbg(&client->adapter->dev, "mpu_release\n");
202
203 return result;
204}
205
206/* read function called when from /dev/mpu is read. Read from the FIFO */
207static ssize_t mpu_read(struct file *file,
208 char __user *buf, size_t count, loff_t *offset)
209{
210 struct mpu_private_data *mpu =
211 container_of(file->private_data, struct mpu_private_data, dev);
212 struct i2c_client *client = mpu->client;
213 size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
214 int err;
215
216 if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
217 wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
218
219 if (!mpu->event || !buf
220 || count < sizeof(mpu->mpu_pm_event))
221 return 0;
222
223 err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
224 if (err) {
225 dev_err(&client->adapter->dev,
226 "Copy to user returned %d\n", err);
227 return -EFAULT;
228 }
229 mpu->event = 0;
230 return len;
231}
232
233static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
234{
235 struct mpu_private_data *mpu =
236 container_of(file->private_data, struct mpu_private_data, dev);
237 int mask = 0;
238
239 poll_wait(file, &mpu->mpu_event_wait, poll);
240 if (mpu->event)
241 mask |= POLLIN | POLLRDNORM;
242 return mask;
243}
244
245static int mpu_dev_ioctl_get_ext_slave_platform_data(
246 struct i2c_client *client,
247 struct ext_slave_platform_data __user *arg)
248{
249 struct mpu_private_data *mpu =
250 (struct mpu_private_data *)i2c_get_clientdata(client);
251 struct ext_slave_platform_data *pdata_slave;
252 struct ext_slave_platform_data local_pdata_slave;
253
254 if (copy_from_user(&local_pdata_slave, arg, sizeof(local_pdata_slave)))
255 return -EFAULT;
256
257 if (local_pdata_slave.type >= EXT_SLAVE_NUM_TYPES)
258 return -EINVAL;
259
260 pdata_slave = mpu->mldl_cfg.pdata_slave[local_pdata_slave.type];
261 /* All but private data and irq_data */
262 if (!pdata_slave)
263 return -ENODEV;
264 if (copy_to_user(arg, pdata_slave, sizeof(*pdata_slave)))
265 return -EFAULT;
266 return 0;
267}
268
269static int mpu_dev_ioctl_get_mpu_platform_data(
270 struct i2c_client *client,
271 struct mpu_platform_data __user *arg)
272{
273 struct mpu_private_data *mpu =
274 (struct mpu_private_data *)i2c_get_clientdata(client);
275 struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
276
277 if (copy_to_user(arg, pdata, sizeof(*pdata)))
278 return -EFAULT;
279 return 0;
280}
281
282static int mpu_dev_ioctl_get_ext_slave_descr(
283 struct i2c_client *client,
284 struct ext_slave_descr __user *arg)
285{
286 struct mpu_private_data *mpu =
287 (struct mpu_private_data *)i2c_get_clientdata(client);
288 struct ext_slave_descr *slave;
289 struct ext_slave_descr local_slave;
290
291 if (copy_from_user(&local_slave, arg, sizeof(local_slave)))
292 return -EFAULT;
293
294 if (local_slave.type >= EXT_SLAVE_NUM_TYPES)
295 return -EINVAL;
296
297 slave = mpu->mldl_cfg.slave[local_slave.type];
298 /* All but private data and irq_data */
299 if (!slave)
300 return -ENODEV;
301 if (copy_to_user(arg, slave, sizeof(*slave)))
302 return -EFAULT;
303 return 0;
304}
305
306
307/**
308 * slave_config() - Pass a requested slave configuration to the slave sensor
309 *
310 * @adapter the adaptor to use to communicate with the slave
311 * @mldl_cfg the mldl configuration structuer
312 * @slave pointer to the slave descriptor
313 * @usr_config The configuration to pass to the slave sensor
314 *
315 * returns 0 or non-zero error code
316 */
317static int inv_mpu_config(struct mldl_cfg *mldl_cfg,
318 void *gyro_adapter,
319 struct ext_slave_config __user *usr_config)
320{
321 int retval = 0;
322 struct ext_slave_config config;
323
324 retval = copy_from_user(&config, usr_config, sizeof(config));
325 if (retval)
326 return -EFAULT;
327
328 if (config.len && config.data) {
329 void *data;
330 data = kmalloc(config.len, GFP_KERNEL);
331 if (!data)
332 return -ENOMEM;
333
334 retval = copy_from_user(data,
335 (void __user *)config.data, config.len);
336 if (retval) {
337 retval = -EFAULT;
338 kfree(data);
339 return retval;
340 }
341 config.data = data;
342 }
343 retval = gyro_config(gyro_adapter, mldl_cfg, &config);
344 kfree(config.data);
345 return retval;
346}
347
348static int inv_mpu_get_config(struct mldl_cfg *mldl_cfg,
349 void *gyro_adapter,
350 struct ext_slave_config __user *usr_config)
351{
352 int retval = 0;
353 struct ext_slave_config config;
354 void *user_data;
355
356 retval = copy_from_user(&config, usr_config, sizeof(config));
357 if (retval)
358 return -EFAULT;
359
360 user_data = config.data;
361 if (config.len && config.data) {
362 void *data;
363 data = kmalloc(config.len, GFP_KERNEL);
364 if (!data)
365 return -ENOMEM;
366
367 retval = copy_from_user(data,
368 (void __user *)config.data, config.len);
369 if (retval) {
370 retval = -EFAULT;
371 kfree(data);
372 return retval;
373 }
374 config.data = data;
375 }
376 retval = gyro_get_config(gyro_adapter, mldl_cfg, &config);
377 if (!retval)
378 retval = copy_to_user((unsigned char __user *)user_data,
379 config.data, config.len);
380 kfree(config.data);
381 return retval;
382}
383
384static int slave_config(struct mldl_cfg *mldl_cfg,
385 void *gyro_adapter,
386 void *slave_adapter,
387 struct ext_slave_descr *slave,
388 struct ext_slave_platform_data *pdata,
389 struct ext_slave_config __user *usr_config)
390{
391 int retval = 0;
392 struct ext_slave_config config;
393 if ((!slave) || (!slave->config))
394 return -ENODEV;
395
396 retval = copy_from_user(&config, usr_config, sizeof(config));
397 if (retval)
398 return -EFAULT;
399
400 if (config.len && config.data) {
401 void *data;
402 data = kmalloc(config.len, GFP_KERNEL);
403 if (!data)
404 return -ENOMEM;
405
406 retval = copy_from_user(data,
407 (void __user *)config.data, config.len);
408 if (retval) {
409 retval = -EFAULT;
410 kfree(data);
411 return retval;
412 }
413 config.data = data;
414 }
415 retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
416 &config, slave, pdata);
417 kfree(config.data);
418 return retval;
419}
420
421static int slave_get_config(struct mldl_cfg *mldl_cfg,
422 void *gyro_adapter,
423 void *slave_adapter,
424 struct ext_slave_descr *slave,
425 struct ext_slave_platform_data *pdata,
426 struct ext_slave_config __user *usr_config)
427{
428 int retval = 0;
429 struct ext_slave_config config;
430 void *user_data;
431 if (!(slave) || !(slave->get_config))
432 return -ENODEV;
433
434 retval = copy_from_user(&config, usr_config, sizeof(config));
435 if (retval)
436 return -EFAULT;
437
438 user_data = config.data;
439 if (config.len && config.data) {
440 void *data;
441 data = kmalloc(config.len, GFP_KERNEL);
442 if (!data)
443 return -ENOMEM;
444
445 retval = copy_from_user(data,
446 (void __user *)config.data, config.len);
447 if (retval) {
448 retval = -EFAULT;
449 kfree(data);
450 return retval;
451 }
452 config.data = data;
453 }
454 retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
455 slave_adapter, &config, slave, pdata);
456 if (retval) {
457 kfree(config.data);
458 return retval;
459 }
460 retval = copy_to_user((unsigned char __user *)user_data,
461 config.data, config.len);
462 kfree(config.data);
463 return retval;
464}
465
466static int inv_slave_read(struct mldl_cfg *mldl_cfg,
467 void *gyro_adapter,
468 void *slave_adapter,
469 struct ext_slave_descr *slave,
470 struct ext_slave_platform_data *pdata,
471 void __user *usr_data)
472{
473 int retval;
474 unsigned char *data;
475 data = kzalloc(slave->read_len, GFP_KERNEL);
476 if (!data)
477 return -EFAULT;
478
479 retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
480 slave, pdata, data);
481
482 if ((!retval) &&
483 (copy_to_user((unsigned char __user *)usr_data,
484 data, slave->read_len)))
485 retval = -EFAULT;
486
487 kfree(data);
488 return retval;
489}
490
491static int mpu_handle_mlsl(void *sl_handle,
492 unsigned char addr,
493 unsigned int cmd,
494 struct mpu_read_write __user *usr_msg)
495{
496 int retval = 0;
497 struct mpu_read_write msg;
498 unsigned char *user_data;
499 retval = copy_from_user(&msg, usr_msg, sizeof(msg));
500 if (retval)
501 return -EFAULT;
502
503 user_data = msg.data;
504 if (msg.length && msg.data) {
505 unsigned char *data;
506 data = kmalloc(msg.length, GFP_KERNEL);
507 if (!data)
508 return -ENOMEM;
509
510 retval = copy_from_user(data,
511 (void __user *)msg.data, msg.length);
512 if (retval) {
513 retval = -EFAULT;
514 kfree(data);
515 return retval;
516 }
517 msg.data = data;
518 } else {
519 return -EPERM;
520 }
521
522 switch (cmd) {
523 case MPU_READ:
524 retval = inv_serial_read(sl_handle, addr,
525 msg.address, msg.length, msg.data);
526 break;
527 case MPU_WRITE:
528 retval = inv_serial_write(sl_handle, addr,
529 msg.length, msg.data);
530 break;
531 case MPU_READ_MEM:
532 retval = inv_serial_read_mem(sl_handle, addr,
533 msg.address, msg.length, msg.data);
534 break;
535 case MPU_WRITE_MEM:
536 retval = inv_serial_write_mem(sl_handle, addr,
537 msg.address, msg.length,
538 msg.data);
539 break;
540 case MPU_READ_FIFO:
541 retval = inv_serial_read_fifo(sl_handle, addr,
542 msg.length, msg.data);
543 break;
544 case MPU_WRITE_FIFO:
545 retval = inv_serial_write_fifo(sl_handle, addr,
546 msg.length, msg.data);
547 break;
548
549 };
550 if (retval) {
551 dev_err(&((struct i2c_adapter *)sl_handle)->dev,
552 "%s: i2c %d error %d\n",
553 __func__, cmd, retval);
554 kfree(msg.data);
555 return retval;
556 }
557 retval = copy_to_user((unsigned char __user *)user_data,
558 msg.data, msg.length);
559 kfree(msg.data);
560 return retval;
561}
562
563/* ioctl - I/O control */
564static long mpu_dev_ioctl(struct file *file,
565 unsigned int cmd, unsigned long arg)
566{
567 struct mpu_private_data *mpu =
568 container_of(file->private_data, struct mpu_private_data, dev);
569 struct i2c_client *client = mpu->client;
570 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
571 int retval = 0;
572 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
573 struct ext_slave_descr **slave = mldl_cfg->slave;
574 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
575 int ii;
576
577 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
578 if (!pdata_slave[ii])
579 slave_adapter[ii] = NULL;
580 else
581 slave_adapter[ii] =
582 i2c_get_adapter(pdata_slave[ii]->adapt_num);
583 }
584 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
585
586 retval = mutex_lock_interruptible(&mpu->mutex);
587 if (retval) {
588 dev_err(&client->adapter->dev,
589 "%s: mutex_lock_interruptible returned %d\n",
590 __func__, retval);
591 return retval;
592 }
593
594 switch (cmd) {
595 case MPU_GET_EXT_SLAVE_PLATFORM_DATA:
596 retval = mpu_dev_ioctl_get_ext_slave_platform_data(
597 client,
598 (struct ext_slave_platform_data __user *)arg);
599 break;
600 case MPU_GET_MPU_PLATFORM_DATA:
601 retval = mpu_dev_ioctl_get_mpu_platform_data(
602 client,
603 (struct mpu_platform_data __user *)arg);
604 break;
605 case MPU_GET_EXT_SLAVE_DESCR:
606 retval = mpu_dev_ioctl_get_ext_slave_descr(
607 client,
608 (struct ext_slave_descr __user *)arg);
609 break;
610 case MPU_READ:
611 case MPU_WRITE:
612 case MPU_READ_MEM:
613 case MPU_WRITE_MEM:
614 case MPU_READ_FIFO:
615 case MPU_WRITE_FIFO:
616 retval = mpu_handle_mlsl(
617 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
618 mldl_cfg->mpu_chip_info->addr, cmd,
619 (struct mpu_read_write __user *)arg);
620 break;
621 case MPU_CONFIG_GYRO:
622 retval = inv_mpu_config(
623 mldl_cfg,
624 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
625 (struct ext_slave_config __user *)arg);
626 break;
627 case MPU_CONFIG_ACCEL:
628 retval = slave_config(
629 mldl_cfg,
630 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
631 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
632 slave[EXT_SLAVE_TYPE_ACCEL],
633 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
634 (struct ext_slave_config __user *)arg);
635 break;
636 case MPU_CONFIG_COMPASS:
637 retval = slave_config(
638 mldl_cfg,
639 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
640 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
641 slave[EXT_SLAVE_TYPE_COMPASS],
642 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
643 (struct ext_slave_config __user *)arg);
644 break;
645 case MPU_CONFIG_PRESSURE:
646 retval = slave_config(
647 mldl_cfg,
648 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
649 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
650 slave[EXT_SLAVE_TYPE_PRESSURE],
651 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
652 (struct ext_slave_config __user *)arg);
653 break;
654 case MPU_GET_CONFIG_GYRO:
655 retval = inv_mpu_get_config(
656 mldl_cfg,
657 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
658 (struct ext_slave_config __user *)arg);
659 break;
660 case MPU_GET_CONFIG_ACCEL:
661 retval = slave_get_config(
662 mldl_cfg,
663 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
664 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
665 slave[EXT_SLAVE_TYPE_ACCEL],
666 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
667 (struct ext_slave_config __user *)arg);
668 break;
669 case MPU_GET_CONFIG_COMPASS:
670 retval = slave_get_config(
671 mldl_cfg,
672 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
673 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
674 slave[EXT_SLAVE_TYPE_COMPASS],
675 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
676 (struct ext_slave_config __user *)arg);
677 break;
678 case MPU_GET_CONFIG_PRESSURE:
679 retval = slave_get_config(
680 mldl_cfg,
681 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
682 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
683 slave[EXT_SLAVE_TYPE_PRESSURE],
684 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
685 (struct ext_slave_config __user *)arg);
686 break;
687 case MPU_SUSPEND:
688 retval = inv_mpu_suspend(
689 mldl_cfg,
690 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
691 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
692 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
693 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
694 arg);
695 break;
696 case MPU_RESUME:
697 retval = inv_mpu_resume(
698 mldl_cfg,
699 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
700 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
701 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
702 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
703 arg);
704 break;
705 case MPU_PM_EVENT_HANDLED:
706 dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
707 complete(&mpu->completion);
708 break;
709 case MPU_READ_ACCEL:
710 retval = inv_slave_read(
711 mldl_cfg,
712 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
713 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
714 slave[EXT_SLAVE_TYPE_ACCEL],
715 pdata_slave[EXT_SLAVE_TYPE_ACCEL],
716 (unsigned char __user *)arg);
717 break;
718 case MPU_READ_COMPASS:
719 retval = inv_slave_read(
720 mldl_cfg,
721 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
722 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
723 slave[EXT_SLAVE_TYPE_COMPASS],
724 pdata_slave[EXT_SLAVE_TYPE_COMPASS],
725 (unsigned char __user *)arg);
726 break;
727 case MPU_READ_PRESSURE:
728 retval = inv_slave_read(
729 mldl_cfg,
730 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
731 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
732 slave[EXT_SLAVE_TYPE_PRESSURE],
733 pdata_slave[EXT_SLAVE_TYPE_PRESSURE],
734 (unsigned char __user *)arg);
735 break;
736 case MPU_GET_REQUESTED_SENSORS:
737 if (copy_to_user(
738 (__u32 __user *)arg,
739 &mldl_cfg->inv_mpu_cfg->requested_sensors,
740 sizeof(mldl_cfg->inv_mpu_cfg->requested_sensors)))
741 retval = -EFAULT;
742 break;
743 case MPU_SET_REQUESTED_SENSORS:
744 mldl_cfg->inv_mpu_cfg->requested_sensors = arg;
745 break;
746 case MPU_GET_IGNORE_SYSTEM_SUSPEND:
747 if (copy_to_user(
748 (unsigned char __user *)arg,
749 &mldl_cfg->inv_mpu_cfg->ignore_system_suspend,
750 sizeof(mldl_cfg->inv_mpu_cfg->ignore_system_suspend)))
751 retval = -EFAULT;
752 break;
753 case MPU_SET_IGNORE_SYSTEM_SUSPEND:
754 mldl_cfg->inv_mpu_cfg->ignore_system_suspend = arg;
755 break;
756 case MPU_GET_MLDL_STATUS:
757 if (copy_to_user(
758 (unsigned char __user *)arg,
759 &mldl_cfg->inv_mpu_state->status,
760 sizeof(mldl_cfg->inv_mpu_state->status)))
761 retval = -EFAULT;
762 break;
763 case MPU_GET_I2C_SLAVES_ENABLED:
764 if (copy_to_user(
765 (unsigned char __user *)arg,
766 &mldl_cfg->inv_mpu_state->i2c_slaves_enabled,
767 sizeof(mldl_cfg->inv_mpu_state->i2c_slaves_enabled)))
768 retval = -EFAULT;
769 break;
770 default:
771 dev_err(&client->adapter->dev,
772 "%s: Unknown cmd %x, arg %lu\n",
773 __func__, cmd, arg);
774 retval = -EINVAL;
775 };
776
777 mutex_unlock(&mpu->mutex);
778 dev_dbg(&client->adapter->dev, "%s: %08x, %08lx, %d\n",
779 __func__, cmd, arg, retval);
780
781 if (retval > 0)
782 retval = -retval;
783
784 return retval;
785}
786
787void mpu_shutdown(struct i2c_client *client)
788{
789 struct mpu_private_data *mpu =
790 (struct mpu_private_data *)i2c_get_clientdata(client);
791 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
792 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
793 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
794 int ii;
795
796 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
797 if (!pdata_slave[ii])
798 slave_adapter[ii] = NULL;
799 else
800 slave_adapter[ii] =
801 i2c_get_adapter(pdata_slave[ii]->adapt_num);
802 }
803 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
804
805 mutex_lock(&mpu->mutex);
806 (void)inv_mpu_suspend(mldl_cfg,
807 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
808 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
809 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
810 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
811 INV_ALL_SENSORS);
812 mutex_unlock(&mpu->mutex);
813 dev_dbg(&client->adapter->dev, "%s\n", __func__);
814}
815
816int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg)
817{
818 struct mpu_private_data *mpu =
819 (struct mpu_private_data *)i2c_get_clientdata(client);
820 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
821 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
822 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
823 int ii;
824
825 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
826 if (!pdata_slave[ii])
827 slave_adapter[ii] = NULL;
828 else
829 slave_adapter[ii] =
830 i2c_get_adapter(pdata_slave[ii]->adapt_num);
831 }
832 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
833
834 mutex_lock(&mpu->mutex);
835 if (!mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
836 dev_dbg(&client->adapter->dev,
837 "%s: suspending on event %d\n", __func__, mesg.event);
838 (void)inv_mpu_suspend(mldl_cfg,
839 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
840 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
841 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
842 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
843 INV_ALL_SENSORS);
844 } else {
845 dev_dbg(&client->adapter->dev,
846 "%s: Already suspended %d\n", __func__, mesg.event);
847 }
848 mutex_unlock(&mpu->mutex);
849 return 0;
850}
851
852int mpu_dev_resume(struct i2c_client *client)
853{
854 struct mpu_private_data *mpu =
855 (struct mpu_private_data *)i2c_get_clientdata(client);
856 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
857 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
858 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
859 int ii;
860
861 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
862 if (!pdata_slave[ii])
863 slave_adapter[ii] = NULL;
864 else
865 slave_adapter[ii] =
866 i2c_get_adapter(pdata_slave[ii]->adapt_num);
867 }
868 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
869
870 mutex_lock(&mpu->mutex);
871 if (mpu->pid && !mldl_cfg->inv_mpu_cfg->ignore_system_suspend) {
872 (void)inv_mpu_resume(mldl_cfg,
873 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
874 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
875 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
876 slave_adapter[EXT_SLAVE_TYPE_PRESSURE],
877 mldl_cfg->inv_mpu_cfg->requested_sensors);
878 dev_dbg(&client->adapter->dev,
879 "%s for pid %d\n", __func__, mpu->pid);
880 }
881 mutex_unlock(&mpu->mutex);
882 return 0;
883}
884
885/* define which file operations are supported */
886static const struct file_operations mpu_fops = {
887 .owner = THIS_MODULE,
888 .read = mpu_read,
889 .poll = mpu_poll,
890 .unlocked_ioctl = mpu_dev_ioctl,
891 .open = mpu_dev_open,
892 .release = mpu_release,
893};
894
895int inv_mpu_register_slave(struct module *slave_module,
896 struct i2c_client *slave_client,
897 struct ext_slave_platform_data *slave_pdata,
898 struct ext_slave_descr *(*get_slave_descr)(void))
899{
900 struct mpu_private_data *mpu = mpu_private_data;
901 struct mldl_cfg *mldl_cfg;
902 struct ext_slave_descr *slave_descr;
903 struct ext_slave_platform_data **pdata_slave;
904 char *irq_name = NULL;
905 int result = 0;
906
907 if (!slave_client || !slave_pdata || !get_slave_descr)
908 return -EINVAL;
909
910 if (!mpu) {
911 dev_err(&slave_client->adapter->dev,
912 "%s: Null mpu_private_data\n", __func__);
913 return -EINVAL;
914 }
915 mldl_cfg = &mpu->mldl_cfg;
916 pdata_slave = mldl_cfg->pdata_slave;
917 slave_descr = get_slave_descr();
918
919 if (!slave_descr) {
920 dev_err(&slave_client->adapter->dev,
921 "%s: Null ext_slave_descr\n", __func__);
922 return -EINVAL;
923 }
924
925 mutex_lock(&mpu->mutex);
926 if (mpu->pid) {
927 mutex_unlock(&mpu->mutex);
928 return -EBUSY;
929 }
930
931 if (pdata_slave[slave_descr->type]) {
932 result = -EBUSY;
933 goto out_unlock_mutex;
934 }
935
936 slave_pdata->address = slave_client->addr;
937 slave_pdata->irq = slave_client->irq;
938 slave_pdata->adapt_num = i2c_adapter_id(slave_client->adapter);
939
940 dev_info(&slave_client->adapter->dev,
941 "%s: +%s Type %d: Addr: %2x IRQ: %2d, Adapt: %2d\n",
942 __func__,
943 slave_descr->name,
944 slave_descr->type,
945 slave_pdata->address,
946 slave_pdata->irq,
947 slave_pdata->adapt_num);
948
949 switch (slave_descr->type) {
950 case EXT_SLAVE_TYPE_ACCEL:
951 irq_name = "accelirq";
952 break;
953 case EXT_SLAVE_TYPE_COMPASS:
954 irq_name = "compassirq";
955 break;
956 case EXT_SLAVE_TYPE_PRESSURE:
957 irq_name = "pressureirq";
958 break;
959 default:
960 irq_name = "none";
961 };
962 if (slave_descr->init) {
963 result = slave_descr->init(slave_client->adapter,
964 slave_descr,
965 slave_pdata);
966 if (result) {
967 dev_err(&slave_client->adapter->dev,
968 "%s init failed %d\n",
969 slave_descr->name, result);
970 goto out_unlock_mutex;
971 }
972 }
973
974 if (slave_descr->type == EXT_SLAVE_TYPE_ACCEL &&
975 slave_descr->id == ACCEL_ID_MPU6050 &&
976 slave_descr->config) {
977 /* pass a reference to the mldl_cfg data
978 structure to the mpu6050 accel "class" */
979 struct ext_slave_config config;
980 config.key = MPU_SLAVE_CONFIG_INTERNAL_REFERENCE;
981 config.len = sizeof(struct mldl_cfg *);
982 config.apply = true;
983 config.data = mldl_cfg;
984 result = slave_descr->config(
985 slave_client->adapter, slave_descr,
986 slave_pdata, &config);
987 if (result) {
988 LOG_RESULT_LOCATION(result);
989 goto out_slavedescr_exit;
990 }
991 }
992 pdata_slave[slave_descr->type] = slave_pdata;
993 mpu->slave_modules[slave_descr->type] = slave_module;
994 mldl_cfg->slave[slave_descr->type] = slave_descr;
995
996 goto out_unlock_mutex;
997
998out_slavedescr_exit:
999 if (slave_descr->exit)
1000 slave_descr->exit(slave_client->adapter,
1001 slave_descr, slave_pdata);
1002out_unlock_mutex:
1003 mutex_unlock(&mpu->mutex);
1004
1005 if (!result && irq_name && (slave_pdata->irq > 0)) {
1006 int warn_result;
1007 dev_info(&slave_client->adapter->dev,
1008 "Installing %s irq using %d\n",
1009 irq_name,
1010 slave_pdata->irq);
1011 warn_result = slaveirq_init(slave_client->adapter,
1012 slave_pdata, irq_name);
1013 if (result)
1014 dev_WARN(&slave_client->adapter->dev,
1015 "%s irq assigned error: %d\n",
1016 slave_descr->name, warn_result);
1017 } else {
1018 dev_WARN(&slave_client->adapter->dev,
1019 "%s irq not assigned: %d %d %d\n",
1020 slave_descr->name,
1021 result, (int)irq_name, slave_pdata->irq);
1022 }
1023
1024 return result;
1025}
1026EXPORT_SYMBOL(inv_mpu_register_slave);
1027
1028void inv_mpu_unregister_slave(struct i2c_client *slave_client,
1029 struct ext_slave_platform_data *slave_pdata,
1030 struct ext_slave_descr *(*get_slave_descr)(void))
1031{
1032 struct mpu_private_data *mpu = mpu_private_data;
1033 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
1034 struct ext_slave_descr *slave_descr;
1035 int result;
1036
1037 dev_info(&slave_client->adapter->dev, "%s\n", __func__);
1038
1039 if (!slave_client || !slave_pdata || !get_slave_descr)
1040 return;
1041
1042 if (slave_pdata->irq)
1043 slaveirq_exit(slave_pdata);
1044
1045 slave_descr = get_slave_descr();
1046 if (!slave_descr)
1047 return;
1048
1049 mutex_lock(&mpu->mutex);
1050
1051 if (slave_descr->exit) {
1052 result = slave_descr->exit(slave_client->adapter,
1053 slave_descr,
1054 slave_pdata);
1055 if (result)
1056 dev_err(&slave_client->adapter->dev,
1057 "Accel exit failed %d\n", result);
1058 }
1059 mldl_cfg->slave[slave_descr->type] = NULL;
1060 mldl_cfg->pdata_slave[slave_descr->type] = NULL;
1061 mpu->slave_modules[slave_descr->type] = NULL;
1062
1063 mutex_unlock(&mpu->mutex);
1064
1065}
1066EXPORT_SYMBOL(inv_mpu_unregister_slave);
1067
1068static unsigned short normal_i2c[] = { I2C_CLIENT_END };
1069
1070static const struct i2c_device_id mpu_id[] = {
1071 {"mpu3050", 0},
1072 {"mpu6050", 0},
1073 {"mpu6050_no_accel", 0},
1074 {}
1075};
1076MODULE_DEVICE_TABLE(i2c, mpu_id);
1077
1078int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
1079{
1080 struct mpu_platform_data *pdata;
1081 struct mpu_private_data *mpu;
1082 struct mldl_cfg *mldl_cfg;
1083 int res = 0;
1084 int ii = 0;
1085 unsigned long irq_flags;
1086
1087 dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
1088
1089 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
1090 res = -ENODEV;
1091 goto out_check_functionality_failed;
1092 }
1093
1094 mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
1095 if (!mpu) {
1096 res = -ENOMEM;
1097 goto out_alloc_data_failed;
1098 }
1099 mldl_cfg = &mpu->mldl_cfg;
1100 mldl_cfg->mpu_ram = &mpu->mpu_ram;
1101 mldl_cfg->mpu_gyro_cfg = &mpu->mpu_gyro_cfg;
1102 mldl_cfg->mpu_offsets = &mpu->mpu_offsets;
1103 mldl_cfg->mpu_chip_info = &mpu->mpu_chip_info;
1104 mldl_cfg->inv_mpu_cfg = &mpu->inv_mpu_cfg;
1105 mldl_cfg->inv_mpu_state = &mpu->inv_mpu_state;
1106
1107 mldl_cfg->mpu_ram->length = MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE;
1108 mldl_cfg->mpu_ram->ram = kzalloc(mldl_cfg->mpu_ram->length, GFP_KERNEL);
1109 if (!mldl_cfg->mpu_ram->ram) {
1110 res = -ENOMEM;
1111 goto out_alloc_ram_failed;
1112 }
1113 mpu_private_data = mpu;
1114 i2c_set_clientdata(client, mpu);
1115 mpu->client = client;
1116
1117 init_waitqueue_head(&mpu->mpu_event_wait);
1118 mutex_init(&mpu->mutex);
1119 init_completion(&mpu->completion);
1120
1121 mpu->response_timeout = 60; /* Seconds */
1122 mpu->timeout.function = mpu_pm_timeout;
1123 mpu->timeout.data = (u_long) mpu;
1124 init_timer(&mpu->timeout);
1125
1126 mpu->nb.notifier_call = mpu_pm_notifier_callback;
1127 mpu->nb.priority = 0;
1128 res = register_pm_notifier(&mpu->nb);
1129 if (res) {
1130 dev_err(&client->adapter->dev,
1131 "Unable to register pm_notifier %d\n", res);
1132 goto out_register_pm_notifier_failed;
1133 }
1134
1135 pdata = (struct mpu_platform_data *)client->dev.platform_data;
1136 if (!pdata) {
1137 dev_WARN(&client->adapter->dev,
1138 "Missing platform data for mpu\n");
1139 }
1140 mldl_cfg->pdata = pdata;
1141
1142 mldl_cfg->mpu_chip_info->addr = client->addr;
1143 res = inv_mpu_open(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
1144
1145 if (res) {
1146 dev_err(&client->adapter->dev,
1147 "Unable to open %s %d\n", MPU_NAME, res);
1148 res = -ENODEV;
1149 goto out_whoami_failed;
1150 }
1151
1152 mpu->dev.minor = MISC_DYNAMIC_MINOR;
1153 mpu->dev.name = "mpu";
1154 mpu->dev.fops = &mpu_fops;
1155 res = misc_register(&mpu->dev);
1156 if (res < 0) {
1157 dev_err(&client->adapter->dev,
1158 "ERROR: misc_register returned %d\n", res);
1159 goto out_misc_register_failed;
1160 }
1161
1162 if (client->irq) {
1163 dev_info(&client->adapter->dev,
1164 "Installing irq using %d\n", client->irq);
1165 if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
1166 irq_flags = IRQF_TRIGGER_FALLING;
1167 else
1168 irq_flags = IRQF_TRIGGER_RISING;
1169 res = mpuirq_init(client, mldl_cfg, irq_flags);
1170
1171 if (res)
1172 goto out_mpuirq_failed;
1173 } else {
1174 dev_WARN(&client->adapter->dev,
1175 "Missing %s IRQ\n", MPU_NAME);
1176 }
1177 if (!strcmp(mpu_id[1].name, devid->name)) {
1178 /* Special case to re-use the inv_mpu_register_slave */
1179 struct ext_slave_platform_data *slave_pdata;
1180 slave_pdata = kzalloc(sizeof(*slave_pdata), GFP_KERNEL);
1181 if (!slave_pdata) {
1182 res = -ENOMEM;
1183 goto out_slave_pdata_kzalloc_failed;
1184 }
1185 slave_pdata->bus = EXT_SLAVE_BUS_PRIMARY;
1186 for (ii = 0; ii < 9; ii++)
1187 slave_pdata->orientation[ii] = pdata->orientation[ii];
1188 res = inv_mpu_register_slave(
1189 NULL, client,
1190 slave_pdata,
1191 mpu6050_get_slave_descr);
1192 if (res) {
1193 /* if inv_mpu_register_slave fails there are no pointer
1194 references to the memory allocated to slave_pdata */
1195 kfree(slave_pdata);
1196 goto out_slave_pdata_kzalloc_failed;
1197 }
1198 }
1199 return res;
1200
1201out_slave_pdata_kzalloc_failed:
1202 if (client->irq)
1203 mpuirq_exit();
1204out_mpuirq_failed:
1205 misc_deregister(&mpu->dev);
1206out_misc_register_failed:
1207 inv_mpu_close(&mpu->mldl_cfg, client->adapter, NULL, NULL, NULL);
1208out_whoami_failed:
1209 unregister_pm_notifier(&mpu->nb);
1210out_register_pm_notifier_failed:
1211 kfree(mldl_cfg->mpu_ram->ram);
1212 mpu_private_data = NULL;
1213out_alloc_ram_failed:
1214 kfree(mpu);
1215out_alloc_data_failed:
1216out_check_functionality_failed:
1217 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
1218 return res;
1219
1220}
1221
1222static int mpu_remove(struct i2c_client *client)
1223{
1224 struct mpu_private_data *mpu = i2c_get_clientdata(client);
1225 struct i2c_adapter *slave_adapter[EXT_SLAVE_NUM_TYPES];
1226 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
1227 struct ext_slave_platform_data **pdata_slave = mldl_cfg->pdata_slave;
1228 int ii;
1229
1230 for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++) {
1231 if (!pdata_slave[ii])
1232 slave_adapter[ii] = NULL;
1233 else
1234 slave_adapter[ii] =
1235 i2c_get_adapter(pdata_slave[ii]->adapt_num);
1236 }
1237
1238 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE] = client->adapter;
1239 dev_dbg(&client->adapter->dev, "%s\n", __func__);
1240
1241 inv_mpu_close(mldl_cfg,
1242 slave_adapter[EXT_SLAVE_TYPE_GYROSCOPE],
1243 slave_adapter[EXT_SLAVE_TYPE_ACCEL],
1244 slave_adapter[EXT_SLAVE_TYPE_COMPASS],
1245 slave_adapter[EXT_SLAVE_TYPE_PRESSURE]);
1246
1247 if (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL] &&
1248 (mldl_cfg->slave[EXT_SLAVE_TYPE_ACCEL]->id ==
1249 ACCEL_ID_MPU6050)) {
1250 struct ext_slave_platform_data *slave_pdata =
1251 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL];
1252 inv_mpu_unregister_slave(
1253 client,
1254 mldl_cfg->pdata_slave[EXT_SLAVE_TYPE_ACCEL],
1255 mpu6050_get_slave_descr);
1256 kfree(slave_pdata);
1257 }
1258
1259 if (client->irq)
1260 mpuirq_exit();
1261
1262 misc_deregister(&mpu->dev);
1263
1264 unregister_pm_notifier(&mpu->nb);
1265
1266 kfree(mpu->mldl_cfg.mpu_ram->ram);
1267 kfree(mpu);
1268
1269 return 0;
1270}
1271
1272static struct i2c_driver mpu_driver = {
1273 .class = I2C_CLASS_HWMON,
1274 .probe = mpu_probe,
1275 .remove = mpu_remove,
1276 .id_table = mpu_id,
1277 .driver = {
1278 .owner = THIS_MODULE,
1279 .name = MPU_NAME,
1280 },
1281 .address_list = normal_i2c,
1282 .shutdown = mpu_shutdown, /* optional */
1283 .suspend = mpu_dev_suspend, /* optional */
1284 .resume = mpu_dev_resume, /* optional */
1285
1286};
1287
1288static int __init mpu_init(void)
1289{
1290 int res = i2c_add_driver(&mpu_driver);
1291 pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
1292 if (res)
1293 pr_err("%s failed\n", __func__);
1294 return res;
1295}
1296
1297static void __exit mpu_exit(void)
1298{
1299 pr_info("%s\n", __func__);
1300 i2c_del_driver(&mpu_driver);
1301}
1302
1303module_init(mpu_init);
1304module_exit(mpu_exit);
1305
1306MODULE_AUTHOR("Invensense Corporation");
1307MODULE_DESCRIPTION("User space character device interface for MPU");
1308MODULE_LICENSE("GPL");
1309MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h
new file mode 100644
index 00000000000..686f6e5d86a
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050b1.h
@@ -0,0 +1,435 @@
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 * @defgroup
22 * @brief
23 *
24 * @{
25 * @file mpu6050.h
26 * @brief
27 */
28
29#ifndef __MPU_H_
30#error Do not include this file directly. Include mpu.h instead.
31#endif
32
33#ifndef __MPU6050B1_H_
34#define __MPU6050B1_H_
35
36
37#define MPU_NAME "mpu6050B1"
38#define DEFAULT_MPU_SLAVEADDR 0x68
39
40/*==== MPU6050B1 REGISTER SET ====*/
41enum {
42 MPUREG_XG_OFFS_TC = 0, /* 0x00, 0 */
43 MPUREG_YG_OFFS_TC, /* 0x01, 1 */
44 MPUREG_ZG_OFFS_TC, /* 0x02, 2 */
45 MPUREG_X_FINE_GAIN, /* 0x03, 3 */
46 MPUREG_Y_FINE_GAIN, /* 0x04, 4 */
47 MPUREG_Z_FINE_GAIN, /* 0x05, 5 */
48 MPUREG_XA_OFFS_H, /* 0x06, 6 */
49 MPUREG_XA_OFFS_L, /* 0x07, 7 */
50 MPUREG_YA_OFFS_H, /* 0x08, 8 */
51 MPUREG_YA_OFFS_L, /* 0x09, 9 */
52 MPUREG_ZA_OFFS_H, /* 0x0a, 10 */
53 MPUREG_ZA_OFFS_L, /* 0x0B, 11 */
54 MPUREG_PRODUCT_ID, /* 0x0c, 12 */
55 MPUREG_0D_RSVD, /* 0x0d, 13 */
56 MPUREG_0E_RSVD, /* 0x0e, 14 */
57 MPUREG_0F_RSVD, /* 0x0f, 15 */
58 MPUREG_10_RSVD, /* 0x00, 16 */
59 MPUREG_11_RSVD, /* 0x11, 17 */
60 MPUREG_12_RSVD, /* 0x12, 18 */
61 MPUREG_XG_OFFS_USRH, /* 0x13, 19 */
62 MPUREG_XG_OFFS_USRL, /* 0x14, 20 */
63 MPUREG_YG_OFFS_USRH, /* 0x15, 21 */
64 MPUREG_YG_OFFS_USRL, /* 0x16, 22 */
65 MPUREG_ZG_OFFS_USRH, /* 0x17, 23 */
66 MPUREG_ZG_OFFS_USRL, /* 0x18, 24 */
67 MPUREG_SMPLRT_DIV, /* 0x19, 25 */
68 MPUREG_CONFIG, /* 0x1A, 26 */
69 MPUREG_GYRO_CONFIG, /* 0x1b, 27 */
70 MPUREG_ACCEL_CONFIG, /* 0x1c, 28 */
71 MPUREG_ACCEL_FF_THR, /* 0x1d, 29 */
72 MPUREG_ACCEL_FF_DUR, /* 0x1e, 30 */
73 MPUREG_ACCEL_MOT_THR, /* 0x1f, 31 */
74 MPUREG_ACCEL_MOT_DUR, /* 0x20, 32 */
75 MPUREG_ACCEL_ZRMOT_THR, /* 0x21, 33 */
76 MPUREG_ACCEL_ZRMOT_DUR, /* 0x22, 34 */
77 MPUREG_FIFO_EN, /* 0x23, 35 */
78 MPUREG_I2C_MST_CTRL, /* 0x24, 36 */
79 MPUREG_I2C_SLV0_ADDR, /* 0x25, 37 */
80 MPUREG_I2C_SLV0_REG, /* 0x26, 38 */
81 MPUREG_I2C_SLV0_CTRL, /* 0x27, 39 */
82 MPUREG_I2C_SLV1_ADDR, /* 0x28, 40 */
83 MPUREG_I2C_SLV1_REG, /* 0x29, 41 */
84 MPUREG_I2C_SLV1_CTRL, /* 0x2a, 42 */
85 MPUREG_I2C_SLV2_ADDR, /* 0x2B, 43 */
86 MPUREG_I2C_SLV2_REG, /* 0x2c, 44 */
87 MPUREG_I2C_SLV2_CTRL, /* 0x2d, 45 */
88 MPUREG_I2C_SLV3_ADDR, /* 0x2E, 46 */
89 MPUREG_I2C_SLV3_REG, /* 0x2f, 47 */
90 MPUREG_I2C_SLV3_CTRL, /* 0x30, 48 */
91 MPUREG_I2C_SLV4_ADDR, /* 0x31, 49 */
92 MPUREG_I2C_SLV4_REG, /* 0x32, 50 */
93 MPUREG_I2C_SLV4_DO, /* 0x33, 51 */
94 MPUREG_I2C_SLV4_CTRL, /* 0x34, 52 */
95 MPUREG_I2C_SLV4_DI, /* 0x35, 53 */
96 MPUREG_I2C_MST_STATUS, /* 0x36, 54 */
97 MPUREG_INT_PIN_CFG, /* 0x37, 55 */
98 MPUREG_INT_ENABLE, /* 0x38, 56 */
99 MPUREG_DMP_INT_STATUS, /* 0x39, 57 */
100 MPUREG_INT_STATUS, /* 0x3A, 58 */
101 MPUREG_ACCEL_XOUT_H, /* 0x3B, 59 */
102 MPUREG_ACCEL_XOUT_L, /* 0x3c, 60 */
103 MPUREG_ACCEL_YOUT_H, /* 0x3d, 61 */
104 MPUREG_ACCEL_YOUT_L, /* 0x3e, 62 */
105 MPUREG_ACCEL_ZOUT_H, /* 0x3f, 63 */
106 MPUREG_ACCEL_ZOUT_L, /* 0x40, 64 */
107 MPUREG_TEMP_OUT_H, /* 0x41, 65 */
108 MPUREG_TEMP_OUT_L, /* 0x42, 66 */
109 MPUREG_GYRO_XOUT_H, /* 0x43, 67 */
110 MPUREG_GYRO_XOUT_L, /* 0x44, 68 */
111 MPUREG_GYRO_YOUT_H, /* 0x45, 69 */
112 MPUREG_GYRO_YOUT_L, /* 0x46, 70 */
113 MPUREG_GYRO_ZOUT_H, /* 0x47, 71 */
114 MPUREG_GYRO_ZOUT_L, /* 0x48, 72 */
115 MPUREG_EXT_SLV_SENS_DATA_00, /* 0x49, 73 */
116 MPUREG_EXT_SLV_SENS_DATA_01, /* 0x4a, 74 */
117 MPUREG_EXT_SLV_SENS_DATA_02, /* 0x4b, 75 */
118 MPUREG_EXT_SLV_SENS_DATA_03, /* 0x4c, 76 */
119 MPUREG_EXT_SLV_SENS_DATA_04, /* 0x4d, 77 */
120 MPUREG_EXT_SLV_SENS_DATA_05, /* 0x4e, 78 */
121 MPUREG_EXT_SLV_SENS_DATA_06, /* 0x4F, 79 */
122 MPUREG_EXT_SLV_SENS_DATA_07, /* 0x50, 80 */
123 MPUREG_EXT_SLV_SENS_DATA_08, /* 0x51, 81 */
124 MPUREG_EXT_SLV_SENS_DATA_09, /* 0x52, 82 */
125 MPUREG_EXT_SLV_SENS_DATA_10, /* 0x53, 83 */
126 MPUREG_EXT_SLV_SENS_DATA_11, /* 0x54, 84 */
127 MPUREG_EXT_SLV_SENS_DATA_12, /* 0x55, 85 */
128 MPUREG_EXT_SLV_SENS_DATA_13, /* 0x56, 86 */
129 MPUREG_EXT_SLV_SENS_DATA_14, /* 0x57, 87 */
130 MPUREG_EXT_SLV_SENS_DATA_15, /* 0x58, 88 */
131 MPUREG_EXT_SLV_SENS_DATA_16, /* 0x59, 89 */
132 MPUREG_EXT_SLV_SENS_DATA_17, /* 0x5a, 90 */
133 MPUREG_EXT_SLV_SENS_DATA_18, /* 0x5B, 91 */
134 MPUREG_EXT_SLV_SENS_DATA_19, /* 0x5c, 92 */
135 MPUREG_EXT_SLV_SENS_DATA_20, /* 0x5d, 93 */
136 MPUREG_EXT_SLV_SENS_DATA_21, /* 0x5e, 94 */
137 MPUREG_EXT_SLV_SENS_DATA_22, /* 0x5f, 95 */
138 MPUREG_EXT_SLV_SENS_DATA_23, /* 0x60, 96 */
139 MPUREG_ACCEL_INTEL_STATUS, /* 0x61, 97 */
140 MPUREG_62_RSVD, /* 0x62, 98 */
141 MPUREG_I2C_SLV0_DO, /* 0x63, 99 */
142 MPUREG_I2C_SLV1_DO, /* 0x64, 100 */
143 MPUREG_I2C_SLV2_DO, /* 0x65, 101 */
144 MPUREG_I2C_SLV3_DO, /* 0x66, 102 */
145 MPUREG_I2C_MST_DELAY_CTRL, /* 0x67, 103 */
146 MPUREG_SIGNAL_PATH_RESET, /* 0x68, 104 */
147 MPUREG_ACCEL_INTEL_CTRL, /* 0x69, 105 */
148 MPUREG_USER_CTRL, /* 0x6A, 106 */
149 MPUREG_PWR_MGMT_1, /* 0x6B, 107 */
150 MPUREG_PWR_MGMT_2, /* 0x6C, 108 */
151 MPUREG_BANK_SEL, /* 0x6D, 109 */
152 MPUREG_MEM_START_ADDR, /* 0x6E, 100 */
153 MPUREG_MEM_R_W, /* 0x6F, 111 */
154 MPUREG_DMP_CFG_1, /* 0x70, 112 */
155 MPUREG_DMP_CFG_2, /* 0x71, 113 */
156 MPUREG_FIFO_COUNTH, /* 0x72, 114 */
157 MPUREG_FIFO_COUNTL, /* 0x73, 115 */
158 MPUREG_FIFO_R_W, /* 0x74, 116 */
159 MPUREG_WHOAMI, /* 0x75, 117 */
160
161 NUM_OF_MPU_REGISTERS /* = 0x76, 118 */
162};
163
164/*==== MPU6050B1 MEMORY ====*/
165enum MPU_MEMORY_BANKS {
166 MEM_RAM_BANK_0 = 0,
167 MEM_RAM_BANK_1,
168 MEM_RAM_BANK_2,
169 MEM_RAM_BANK_3,
170 MEM_RAM_BANK_4,
171 MEM_RAM_BANK_5,
172 MEM_RAM_BANK_6,
173 MEM_RAM_BANK_7,
174 MEM_RAM_BANK_8,
175 MEM_RAM_BANK_9,
176 MEM_RAM_BANK_10,
177 MEM_RAM_BANK_11,
178 MPU_MEM_NUM_RAM_BANKS,
179 MPU_MEM_OTP_BANK_0 = 16
180};
181
182
183/*==== MPU6050B1 parameters ====*/
184
185#define NUM_REGS (NUM_OF_MPU_REGISTERS)
186#define START_SENS_REGS (0x3B)
187#define NUM_SENS_REGS (0x60 - START_SENS_REGS + 1)
188
189/*---- MPU Memory ----*/
190#define NUM_BANKS (MPU_MEM_NUM_RAM_BANKS)
191#define BANK_SIZE (256)
192#define MEM_SIZE (NUM_BANKS * BANK_SIZE)
193#define MPU_MEM_BANK_SIZE (BANK_SIZE) /*alternative name */
194
195#define FIFO_HW_SIZE (1024)
196
197#define NUM_EXT_SLAVES (4)
198
199
200/*==== BITS FOR MPU6050B1 ====*/
201/*---- MPU6050B1 'XG_OFFS_TC' register (0, 1, 2) ----*/
202#define BIT_PU_SLEEP_MODE 0x80
203#define BITS_XG_OFFS_TC 0x7E
204#define BIT_OTP_BNK_VLD 0x01
205
206#define BIT_I2C_MST_VDDIO 0x80
207#define BITS_YG_OFFS_TC 0x7E
208#define BITS_ZG_OFFS_TC 0x7E
209/*---- MPU6050B1 'FIFO_EN' register (23) ----*/
210#define BIT_TEMP_OUT 0x80
211#define BIT_GYRO_XOUT 0x40
212#define BIT_GYRO_YOUT 0x20
213#define BIT_GYRO_ZOUT 0x10
214#define BIT_ACCEL 0x08
215#define BIT_SLV_2 0x04
216#define BIT_SLV_1 0x02
217#define BIT_SLV_0 0x01
218/*---- MPU6050B1 'CONFIG' register (1A) ----*/
219/*NONE 0xC0 */
220#define BITS_EXT_SYNC_SET 0x38
221#define BITS_DLPF_CFG 0x07
222/*---- MPU6050B1 'GYRO_CONFIG' register (1B) ----*/
223/* voluntarily modified label from BITS_FS_SEL to
224 * BITS_GYRO_FS_SEL to avoid confusion with MPU
225 */
226#define BITS_GYRO_FS_SEL 0x18
227/*NONE 0x07 */
228/*---- MPU6050B1 'ACCEL_CONFIG' register (1C) ----*/
229#define BITS_ACCEL_FS_SEL 0x18
230#define BITS_ACCEL_HPF 0x07
231/*---- MPU6050B1 'I2C_MST_CTRL' register (24) ----*/
232#define BIT_MULT_MST_EN 0x80
233#define BIT_WAIT_FOR_ES 0x40
234#define BIT_SLV_3_FIFO_EN 0x20
235#define BIT_I2C_MST_PSR 0x10
236#define BITS_I2C_MST_CLK 0x0F
237/*---- MPU6050B1 'I2C_SLV?_ADDR' register (27,2A,2D,30) ----*/
238#define BIT_I2C_READ 0x80
239#define BIT_I2C_WRITE 0x00
240#define BITS_I2C_ADDR 0x7F
241/*---- MPU6050B1 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/
242#define BIT_SLV_ENABLE 0x80
243#define BIT_SLV_BYTE_SW 0x40
244#define BIT_SLV_REG_DIS 0x20
245#define BIT_SLV_GRP 0x10
246#define BITS_SLV_LENG 0x0F
247/*---- MPU6050B1 'I2C_SLV4_ADDR' register (31) ----*/
248#define BIT_I2C_SLV4_RNW 0x80
249/*---- MPU6050B1 'I2C_SLV4_CTRL' register (34) ----*/
250#define BIT_I2C_SLV4_EN 0x80
251#define BIT_SLV4_DONE_INT_EN 0x40
252#define BIT_SLV4_REG_DIS 0x20
253#define MASK_I2C_MST_DLY 0x1F
254/*---- MPU6050B1 'I2C_MST_STATUS' register (36) ----*/
255#define BIT_PASS_THROUGH 0x80
256#define BIT_I2C_SLV4_DONE 0x40
257#define BIT_I2C_LOST_ARB 0x20
258#define BIT_I2C_SLV4_NACK 0x10
259#define BIT_I2C_SLV3_NACK 0x08
260#define BIT_I2C_SLV2_NACK 0x04
261#define BIT_I2C_SLV1_NACK 0x02
262#define BIT_I2C_SLV0_NACK 0x01
263/*---- MPU6050B1 'INT_PIN_CFG' register (37) ----*/
264#define BIT_ACTL 0x80
265#define BIT_ACTL_LOW 0x80
266#define BIT_ACTL_HIGH 0x00
267#define BIT_OPEN 0x40
268#define BIT_LATCH_INT_EN 0x20
269#define BIT_INT_ANYRD_2CLEAR 0x10
270#define BIT_ACTL_FSYNC 0x08
271#define BIT_FSYNC_INT_EN 0x04
272#define BIT_BYPASS_EN 0x02
273#define BIT_CLKOUT_EN 0x01
274/*---- MPU6050B1 'INT_ENABLE' register (38) ----*/
275#define BIT_FF_EN 0x80
276#define BIT_MOT_EN 0x40
277#define BIT_ZMOT_EN 0x20
278#define BIT_FIFO_OVERFLOW_EN 0x10
279#define BIT_I2C_MST_INT_EN 0x08
280#define BIT_PLL_RDY_EN 0x04
281#define BIT_DMP_INT_EN 0x02
282#define BIT_RAW_RDY_EN 0x01
283/*---- MPU6050B1 'DMP_INT_STATUS' register (39) ----*/
284/*NONE 0x80 */
285/*NONE 0x40 */
286#define BIT_DMP_INT_5 0x20
287#define BIT_DMP_INT_4 0x10
288#define BIT_DMP_INT_3 0x08
289#define BIT_DMP_INT_2 0x04
290#define BIT_DMP_INT_1 0x02
291#define BIT_DMP_INT_0 0x01
292/*---- MPU6050B1 'INT_STATUS' register (3A) ----*/
293#define BIT_FF_INT 0x80
294#define BIT_MOT_INT 0x40
295#define BIT_ZMOT_INT 0x20
296#define BIT_FIFO_OVERFLOW_INT 0x10
297#define BIT_I2C_MST_INT 0x08
298#define BIT_PLL_RDY_INT 0x04
299#define BIT_DMP_INT 0x02
300#define BIT_RAW_DATA_RDY_INT 0x01
301/*---- MPU6050B1 'MPUREG_I2C_MST_DELAY_CTRL' register (0x67) ----*/
302#define BIT_DELAY_ES_SHADOW 0x80
303#define BIT_SLV4_DLY_EN 0x10
304#define BIT_SLV3_DLY_EN 0x08
305#define BIT_SLV2_DLY_EN 0x04
306#define BIT_SLV1_DLY_EN 0x02
307#define BIT_SLV0_DLY_EN 0x01
308/*---- MPU6050B1 'BANK_SEL' register (6D) ----*/
309#define BIT_PRFTCH_EN 0x40
310#define BIT_CFG_USER_BANK 0x20
311#define BITS_MEM_SEL 0x1f
312/*---- MPU6050B1 'USER_CTRL' register (6A) ----*/
313#define BIT_DMP_EN 0x80
314#define BIT_FIFO_EN 0x40
315#define BIT_I2C_MST_EN 0x20
316#define BIT_I2C_IF_DIS 0x10
317#define BIT_DMP_RST 0x08
318#define BIT_FIFO_RST 0x04
319#define BIT_I2C_MST_RST 0x02
320#define BIT_SIG_COND_RST 0x01
321/*---- MPU6050B1 'PWR_MGMT_1' register (6B) ----*/
322#define BIT_H_RESET 0x80
323#define BIT_SLEEP 0x40
324#define BIT_CYCLE 0x20
325#define BIT_PD_PTAT 0x08
326#define BITS_CLKSEL 0x07
327/*---- MPU6050B1 'PWR_MGMT_2' register (6C) ----*/
328#define BITS_LPA_WAKE_CTRL 0xC0
329#define BITS_LPA_WAKE_1HZ 0x00
330#define BITS_LPA_WAKE_2HZ 0x40
331#define BITS_LPA_WAKE_10HZ 0x80
332#define BITS_LPA_WAKE_40HZ 0xC0
333#define BIT_STBY_XA 0x20
334#define BIT_STBY_YA 0x10
335#define BIT_STBY_ZA 0x08
336#define BIT_STBY_XG 0x04
337#define BIT_STBY_YG 0x02
338#define BIT_STBY_ZG 0x01
339
340#define ACCEL_MOT_THR_LSB (32) /* mg */
341#define ACCEL_MOT_DUR_LSB (1)
342#define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255)
343#define ACCEL_ZRMOT_DUR_LSB (64)
344
345/*----------------------------------------------------------------------------*/
346/*---- Alternative names to take care of conflicts with current mpu3050.h ----*/
347/*----------------------------------------------------------------------------*/
348
349/*-- registers --*/
350#define MPUREG_DLPF_FS_SYNC MPUREG_CONFIG /* 0x1A */
351
352#define MPUREG_PWR_MGM MPUREG_PWR_MGMT_1 /* 0x6B */
353#define MPUREG_FIFO_EN1 MPUREG_FIFO_EN /* 0x23 */
354#define MPUREG_INT_CFG MPUREG_INT_ENABLE /* 0x38 */
355#define MPUREG_X_OFFS_USRH MPUREG_XG_OFFS_USRH /* 0x13 */
356#define MPUREG_WHO_AM_I MPUREG_WHOAMI /* 0x75 */
357#define MPUREG_23_RSVD MPUREG_EXT_SLV_SENS_DATA_00 /* 0x49 */
358
359/*-- bits --*/
360/* 'USER_CTRL' register */
361#define BIT_AUX_IF_EN BIT_I2C_MST_EN
362#define BIT_AUX_RD_LENG BIT_I2C_MST_EN
363#define BIT_IME_IF_RST BIT_I2C_MST_RST
364#define BIT_GYRO_RST BIT_SIG_COND_RST
365/* 'INT_ENABLE' register */
366#define BIT_RAW_RDY BIT_RAW_DATA_RDY_INT
367#define BIT_MPU_RDY_EN BIT_PLL_RDY_EN
368/* 'INT_STATUS' register */
369#define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT
370
371/*---- MPU6050 Silicon Revisions ----*/
372#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
373#define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */
374
375/*---- MPU6050 notable product revisions ----*/
376#define MPU_PRODUCT_KEY_B1_E1_5 105
377#define MPU_PRODUCT_KEY_B2_F1 431
378
379/*---- structure containing control variables used by MLDL ----*/
380/*---- MPU clock source settings ----*/
381/*---- MPU filter selections ----*/
382enum mpu_filter {
383 MPU_FILTER_256HZ_NOLPF2 = 0,
384 MPU_FILTER_188HZ,
385 MPU_FILTER_98HZ,
386 MPU_FILTER_42HZ,
387 MPU_FILTER_20HZ,
388 MPU_FILTER_10HZ,
389 MPU_FILTER_5HZ,
390 MPU_FILTER_2100HZ_NOLPF,
391 NUM_MPU_FILTER
392};
393
394enum mpu_fullscale {
395 MPU_FS_250DPS = 0,
396 MPU_FS_500DPS,
397 MPU_FS_1000DPS,
398 MPU_FS_2000DPS,
399 NUM_MPU_FS
400};
401
402enum mpu_clock_sel {
403 MPU_CLK_SEL_INTERNAL = 0,
404 MPU_CLK_SEL_PLLGYROX,
405 MPU_CLK_SEL_PLLGYROY,
406 MPU_CLK_SEL_PLLGYROZ,
407 MPU_CLK_SEL_PLLEXT32K,
408 MPU_CLK_SEL_PLLEXT19M,
409 MPU_CLK_SEL_RESERVED,
410 MPU_CLK_SEL_STOP,
411 NUM_CLK_SEL
412};
413
414enum mpu_ext_sync {
415 MPU_EXT_SYNC_NONE = 0,
416 MPU_EXT_SYNC_TEMP,
417 MPU_EXT_SYNC_GYROX,
418 MPU_EXT_SYNC_GYROY,
419 MPU_EXT_SYNC_GYROZ,
420 MPU_EXT_SYNC_ACCELX,
421 MPU_EXT_SYNC_ACCELY,
422 MPU_EXT_SYNC_ACCELZ,
423 NUM_MPU_EXT_SYNC
424};
425
426#define MPUREG_CONFIG_VALUE(ext_sync, lpf) \
427 ((ext_sync << 3) | lpf)
428
429#define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \
430 ((x_st ? 0x80 : 0) | \
431 (y_st ? 0x70 : 0) | \
432 (z_st ? 0x60 : 0) | \
433 (full_scale << 3))
434
435#endif /* __MPU6050_H_ */
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c
new file mode 100644
index 00000000000..d8b721e4346
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.c
@@ -0,0 +1,254 @@
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#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/stat.h>
25#include <linux/irq.h>
26#include <linux/signal.h>
27#include <linux/miscdevice.h>
28#include <linux/i2c.h>
29#include <linux/i2c-dev.h>
30#include <linux/poll.h>
31
32#include <linux/errno.h>
33#include <linux/fs.h>
34#include <linux/mm.h>
35#include <linux/sched.h>
36#include <linux/wait.h>
37#include <linux/uaccess.h>
38#include <linux/io.h>
39
40#include <linux/mpu.h>
41#include "mpuirq.h"
42#include "mldl_cfg.h"
43
44#define MPUIRQ_NAME "mpuirq"
45
46/* function which gets accel data and sends it to MPU */
47
48DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait);
49
50struct mpuirq_dev_data {
51 struct i2c_client *mpu_client;
52 struct miscdevice *dev;
53 int irq;
54 int pid;
55 int accel_divider;
56 int data_ready;
57 int timeout;
58};
59
60static struct mpuirq_dev_data mpuirq_dev_data;
61static struct mpuirq_data mpuirq_data;
62static char *interface = MPUIRQ_NAME;
63
64static int mpuirq_open(struct inode *inode, struct file *file)
65{
66 dev_dbg(mpuirq_dev_data.dev->this_device,
67 "%s current->pid %d\n", __func__, current->pid);
68 mpuirq_dev_data.pid = current->pid;
69 file->private_data = &mpuirq_dev_data;
70 return 0;
71}
72
73/* close function - called when the "file" /dev/mpuirq is closed in userspace */
74static int mpuirq_release(struct inode *inode, struct file *file)
75{
76 dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n");
77 return 0;
78}
79
80/* read function called when from /dev/mpuirq is read */
81static ssize_t mpuirq_read(struct file *file,
82 char *buf, size_t count, loff_t *ppos)
83{
84 int len, err;
85 struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
86
87 if (!mpuirq_dev_data.data_ready &&
88 mpuirq_dev_data.timeout && (!(file->f_flags & O_NONBLOCK))) {
89 wait_event_interruptible_timeout(mpuirq_wait,
90 mpuirq_dev_data.data_ready,
91 mpuirq_dev_data.timeout);
92 }
93
94 if (mpuirq_dev_data.data_ready && NULL != buf
95 && count >= sizeof(mpuirq_data)) {
96 err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data));
97 mpuirq_data.data_type = 0;
98 } else {
99 return 0;
100 }
101 if (err != 0) {
102 dev_err(p_mpuirq_dev_data->dev->this_device,
103 "Copy to user returned %d\n", err);
104 return -EFAULT;
105 }
106 mpuirq_dev_data.data_ready = 0;
107 len = sizeof(mpuirq_data);
108 return len;
109}
110
111unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll)
112{
113 int mask = 0;
114
115 poll_wait(file, &mpuirq_wait, poll);
116 if (mpuirq_dev_data.data_ready)
117 mask |= POLLIN | POLLRDNORM;
118 return mask;
119}
120
121/* ioctl - I/O control */
122static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
123{
124 int retval = 0;
125 int data;
126
127 switch (cmd) {
128 case MPUIRQ_SET_TIMEOUT:
129 mpuirq_dev_data.timeout = arg;
130 break;
131
132 case MPUIRQ_GET_INTERRUPT_CNT:
133 data = mpuirq_data.interruptcount - 1;
134 if (mpuirq_data.interruptcount > 1)
135 mpuirq_data.interruptcount = 1;
136
137 if (copy_to_user((int *)arg, &data, sizeof(int)))
138 return -EFAULT;
139 break;
140 case MPUIRQ_GET_IRQ_TIME:
141 if (copy_to_user((int *)arg, &mpuirq_data.irqtime,
142 sizeof(mpuirq_data.irqtime)))
143 return -EFAULT;
144 mpuirq_data.irqtime = 0;
145 break;
146 case MPUIRQ_SET_FREQUENCY_DIVIDER:
147 mpuirq_dev_data.accel_divider = arg;
148 break;
149 default:
150 retval = -EINVAL;
151 }
152 return retval;
153}
154
155static irqreturn_t mpuirq_handler(int irq, void *dev_id)
156{
157 static int mycount;
158 struct timeval irqtime;
159 mycount++;
160
161 mpuirq_data.interruptcount++;
162
163 /* wake up (unblock) for reading data from userspace */
164 /* and ignore first interrupt generated in module init */
165 mpuirq_dev_data.data_ready = 1;
166
167 do_gettimeofday(&irqtime);
168 mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32);
169 mpuirq_data.irqtime += irqtime.tv_usec;
170 mpuirq_data.data_type = MPUIRQ_DATA_TYPE_MPU_IRQ;
171 mpuirq_data.data = 0;
172
173 wake_up_interruptible(&mpuirq_wait);
174
175 return IRQ_HANDLED;
176
177}
178
179/* define which file operations are supported */
180const struct file_operations mpuirq_fops = {
181 .owner = THIS_MODULE,
182 .read = mpuirq_read,
183 .poll = mpuirq_poll,
184
185 .unlocked_ioctl = mpuirq_ioctl,
186 .open = mpuirq_open,
187 .release = mpuirq_release,
188};
189
190static struct miscdevice mpuirq_device = {
191 .minor = MISC_DYNAMIC_MINOR,
192 .name = MPUIRQ_NAME,
193 .fops = &mpuirq_fops,
194};
195
196int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg,
197 unsigned long irq_flags)
198{
199
200 int res;
201
202 mpuirq_dev_data.mpu_client = mpu_client;
203
204 dev_info(&mpu_client->adapter->dev,
205 "Module Param interface = %s\n", interface);
206
207 mpuirq_dev_data.irq = mpu_client->irq;
208 mpuirq_dev_data.pid = 0;
209 mpuirq_dev_data.accel_divider = -1;
210 mpuirq_dev_data.data_ready = 0;
211 mpuirq_dev_data.timeout = 0;
212 mpuirq_dev_data.dev = &mpuirq_device;
213
214 if (mpuirq_dev_data.irq) {
215 irq_flags |= IRQF_SHARED;
216 res =
217 request_irq(mpuirq_dev_data.irq, mpuirq_handler, irq_flags,
218 interface, &mpuirq_dev_data.irq);
219 if (res) {
220 dev_err(&mpu_client->adapter->dev,
221 "myirqtest: cannot register IRQ %d\n",
222 mpuirq_dev_data.irq);
223 } else {
224 res = misc_register(&mpuirq_device);
225 if (res < 0) {
226 dev_err(&mpu_client->adapter->dev,
227 "misc_register returned %d\n", res);
228 free_irq(mpuirq_dev_data.irq,
229 &mpuirq_dev_data.irq);
230 }
231 }
232
233 } else {
234 res = 0;
235 }
236
237 return res;
238}
239EXPORT_SYMBOL(mpuirq_init);
240
241void mpuirq_exit(void)
242{
243 if (mpuirq_dev_data.irq > 0)
244 free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq);
245
246 dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME);
247 misc_deregister(&mpuirq_device);
248
249 return;
250}
251EXPORT_SYMBOL(mpuirq_exit);
252
253module_param(interface, charp, S_IRUGO | S_IWUSR);
254MODULE_PARM_DESC(interface, "The Interface name");
diff --git a/drivers/misc/inv_mpu/mpuirq.h b/drivers/misc/inv_mpu/mpuirq.h
new file mode 100644
index 00000000000..073867d830c
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.h
@@ -0,0 +1,37 @@
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#ifndef __MPUIRQ__
21#define __MPUIRQ__
22
23#include <linux/i2c-dev.h>
24#include <linux/time.h>
25#include <linux/ioctl.h>
26#include "mldl_cfg.h"
27
28#define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long)
29#define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long)
30#define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval)
31#define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long)
32
33void mpuirq_exit(void);
34int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg,
35 unsigned long irq_flags);
36
37#endif
diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig
new file mode 100644
index 00000000000..f1c021e8f12
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/Kconfig
@@ -0,0 +1,20 @@
1menuconfig: INV_SENSORS_PRESSURE
2 bool "Pressure Sensor Slaves"
3 depends on INV_SENSORS
4 default y
5 help
6 Select y to see a list of supported pressure sensors that can be
7 integrated with the MPUxxxx set of motion processors.
8
9if INV_SENSORS_PRESSURE
10
11config MPU_SENSORS_BMA085
12 tristate "Bosch BMA085"
13 help
14 This enables support for the Bosch bma085 pressure sensor
15 This support is for integration with the MPU3050 or MPU6050 gyroscope
16 device driver. Only one accelerometer can be registered at a time.
17 Specifying more that one accelerometer in the board file will result
18 in runtime errors.
19
20endif
diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile
new file mode 100644
index 00000000000..595923d809d
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/Makefile
@@ -0,0 +1,8 @@
1#
2# Pressure Slaves to MPUxxxx
3#
4obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o
5inv_mpu_bma085-objs += bma085.o
6
7EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
8EXTRA_CFLAGS += -D__C99_DESIGNATED_INITIALIZER
diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c
new file mode 100644
index 00000000000..696d2b6e183
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/bma085.c
@@ -0,0 +1,367 @@
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 * @defgroup ACCELDL (Motion Library - Pressure Driver Layer)
22 * @brief Provides the interface to setup and handle a pressure
23 * connected to the secondary I2C interface of the gyroscope.
24 *
25 * @{
26 * @file bma085.c
27 * @brief Pressure setup and handling methods.
28 */
29
30/* ------------------ */
31/* - Include Files. - */
32/* ------------------ */
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 <linux/mpu.h>
44#include "mlsl.h"
45#include "log.h"
46
47/*
48 * this structure holds all device specific calibration parameters
49 */
50struct bmp085_calibration_param_t {
51 short ac1;
52 short ac2;
53 short ac3;
54 unsigned short ac4;
55 unsigned short ac5;
56 unsigned short ac6;
57 short b1;
58 short b2;
59 short mb;
60 short mc;
61 short md;
62 long param_b5;
63};
64
65struct bmp085_calibration_param_t cal_param;
66
67#define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */
68#define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */
69#define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */
70
71/*********************************************
72 * Pressure Initialization Functions
73 *********************************************/
74
75static int bma085_suspend(void *mlsl_handle,
76 struct ext_slave_descr *slave,
77 struct ext_slave_platform_data *pdata)
78{
79 int result = INV_SUCCESS;
80 return result;
81}
82
83#define PRESSURE_BMA085_PROM_START_ADDR (0xAA)
84#define PRESSURE_BMA085_PROM_DATA_LEN (22)
85#define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4)
86/* temperature measurent */
87#define PRESSURE_BMP085_T_MEAS (0x2E)
88/* pressure measurement; oversampling_setting */
89#define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34)
90#define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74)
91#define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4)
92#define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4)
93#define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6)
94#define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7)
95
96static int bma085_resume(void *mlsl_handle,
97 struct ext_slave_descr *slave,
98 struct ext_slave_platform_data *pdata)
99{
100 int result;
101 unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN];
102
103 result =
104 inv_serial_read(mlsl_handle, pdata->address,
105 PRESSURE_BMA085_PROM_START_ADDR,
106 PRESSURE_BMA085_PROM_DATA_LEN, data);
107 if (result) {
108 LOG_RESULT_LOCATION(result);
109 return result;
110 }
111
112 /* parameters AC1-AC6 */
113 cal_param.ac1 = (data[0] << 8) | data[1];
114 cal_param.ac2 = (data[2] << 8) | data[3];
115 cal_param.ac3 = (data[4] << 8) | data[5];
116 cal_param.ac4 = (data[6] << 8) | data[7];
117 cal_param.ac5 = (data[8] << 8) | data[9];
118 cal_param.ac6 = (data[10] << 8) | data[11];
119
120 /* parameters B1,B2 */
121 cal_param.b1 = (data[12] << 8) | data[13];
122 cal_param.b2 = (data[14] << 8) | data[15];
123
124 /* parameters MB,MC,MD */
125 cal_param.mb = (data[16] << 8) | data[17];
126 cal_param.mc = (data[18] << 8) | data[19];
127 cal_param.md = (data[20] << 8) | data[21];
128
129 return result;
130}
131
132static int bma085_read(void *mlsl_handle,
133 struct ext_slave_descr *slave,
134 struct ext_slave_platform_data *pdata,
135 unsigned char *data)
136{
137 int result;
138 long pressure, x1, x2, x3, b3, b6;
139 unsigned long b4, b7;
140 unsigned long up;
141 unsigned short ut;
142 short oversampling_setting = 0;
143 short temperature;
144 long divisor;
145
146 /* get temprature */
147 result = inv_serial_single_write(mlsl_handle, pdata->address,
148 PRESSURE_BMP085_CTRL_MEAS_REG,
149 PRESSURE_BMP085_T_MEAS);
150 msleep(5);
151 result =
152 inv_serial_read(mlsl_handle, pdata->address,
153 PRESSURE_BMP085_ADC_OUT_MSB_REG, 2,
154 (unsigned char *)data);
155 if (result) {
156 LOG_RESULT_LOCATION(result);
157 return result;
158 }
159 ut = (data[0] << 8) | data[1];
160
161 x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15;
162 divisor = x1 + cal_param.md;
163 if (!divisor)
164 return INV_ERROR_DIVIDE_BY_ZERO;
165
166 x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md);
167 cal_param.param_b5 = x1 + x2;
168 /* temperature in 0.1 degree C */
169 temperature = (short)((cal_param.param_b5 + 8) >> 4);
170
171 /* get pressure */
172 result = inv_serial_single_write(mlsl_handle, pdata->address,
173 PRESSURE_BMP085_CTRL_MEAS_REG,
174 PRESSURE_BMP085_P_MEAS_OSS_0);
175 msleep(5);
176 result =
177 inv_serial_read(mlsl_handle, pdata->address,
178 PRESSURE_BMP085_ADC_OUT_MSB_REG, 2,
179 (unsigned char *)data);
180 if (result) {
181 LOG_RESULT_LOCATION(result);
182 return result;
183 }
184 up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1]));
185
186 b6 = cal_param.param_b5 - 4000;
187 /* calculate B3 */
188 x1 = (b6*b6) >> 12;
189 x1 *= cal_param.b2;
190 x1 >>= 11;
191
192 x2 = (cal_param.ac2*b6);
193 x2 >>= 11;
194
195 x3 = x1 + x2;
196
197 b3 = (((((long)cal_param.ac1) * 4 + x3)
198 << oversampling_setting) + 2) >> 2;
199
200 /* calculate B4 */
201 x1 = (cal_param.ac3 * b6) >> 13;
202 x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16;
203 x3 = ((x1 + x2) + 2) >> 2;
204 b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15;
205 if (!b4)
206 return INV_ERROR;
207
208 b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting));
209 if (b7 < 0x80000000)
210 pressure = (b7 << 1) / b4;
211 else
212 pressure = (b7 / b4) << 1;
213
214 x1 = pressure >> 8;
215 x1 *= x1;
216 x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16;
217 x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16;
218 /* pressure in Pa */
219 pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4;
220
221 data[0] = (unsigned char)(pressure >> 16);
222 data[1] = (unsigned char)(pressure >> 8);
223 data[2] = (unsigned char)(pressure & 0xFF);
224
225 return result;
226}
227
228static struct ext_slave_descr bma085_descr = {
229 .init = NULL,
230 .exit = NULL,
231 .suspend = bma085_suspend,
232 .resume = bma085_resume,
233 .read = bma085_read,
234 .config = NULL,
235 .get_config = NULL,
236 .name = "bma085",
237 .type = EXT_SLAVE_TYPE_PRESSURE,
238 .id = PRESSURE_ID_BMA085,
239 .read_reg = 0xF6,
240 .read_len = 3,
241 .endian = EXT_SLAVE_BIG_ENDIAN,
242 .range = {0, 0},
243};
244
245static
246struct ext_slave_descr *bma085_get_slave_descr(void)
247{
248 return &bma085_descr;
249}
250
251/* Platform data for the MPU */
252struct bma085_mod_private_data {
253 struct i2c_client *client;
254 struct ext_slave_platform_data *pdata;
255};
256
257static unsigned short normal_i2c[] = { I2C_CLIENT_END };
258
259static int bma085_mod_probe(struct i2c_client *client,
260 const struct i2c_device_id *devid)
261{
262 struct ext_slave_platform_data *pdata;
263 struct bma085_mod_private_data *private_data;
264 int result = 0;
265
266 dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
267
268 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
269 result = -ENODEV;
270 goto out_no_free;
271 }
272
273 pdata = client->dev.platform_data;
274 if (!pdata) {
275 dev_err(&client->adapter->dev,
276 "Missing platform data for slave %s\n", devid->name);
277 result = -EFAULT;
278 goto out_no_free;
279 }
280
281 private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
282 if (!private_data) {
283 result = -ENOMEM;
284 goto out_no_free;
285 }
286
287 i2c_set_clientdata(client, private_data);
288 private_data->client = client;
289 private_data->pdata = pdata;
290
291 result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
292 bma085_get_slave_descr);
293 if (result) {
294 dev_err(&client->adapter->dev,
295 "Slave registration failed: %s, %d\n",
296 devid->name, result);
297 goto out_free_memory;
298 }
299
300 return result;
301
302out_free_memory:
303 kfree(private_data);
304out_no_free:
305 dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
306 return result;
307
308}
309
310static int bma085_mod_remove(struct i2c_client *client)
311{
312 struct bma085_mod_private_data *private_data =
313 i2c_get_clientdata(client);
314
315 dev_dbg(&client->adapter->dev, "%s\n", __func__);
316
317 inv_mpu_unregister_slave(client, private_data->pdata,
318 bma085_get_slave_descr);
319
320 kfree(private_data);
321 return 0;
322}
323
324static const struct i2c_device_id bma085_mod_id[] = {
325 { "bma085", PRESSURE_ID_BMA085 },
326 {}
327};
328
329MODULE_DEVICE_TABLE(i2c, bma085_mod_id);
330
331static struct i2c_driver bma085_mod_driver = {
332 .class = I2C_CLASS_HWMON,
333 .probe = bma085_mod_probe,
334 .remove = bma085_mod_remove,
335 .id_table = bma085_mod_id,
336 .driver = {
337 .owner = THIS_MODULE,
338 .name = "bma085_mod",
339 },
340 .address_list = normal_i2c,
341};
342
343static int __init bma085_mod_init(void)
344{
345 int res = i2c_add_driver(&bma085_mod_driver);
346 pr_info("%s: Probe name %s\n", __func__, "bma085_mod");
347 if (res)
348 pr_err("%s failed\n", __func__);
349 return res;
350}
351
352static void __exit bma085_mod_exit(void)
353{
354 pr_info("%s\n", __func__);
355 i2c_del_driver(&bma085_mod_driver);
356}
357
358module_init(bma085_mod_init);
359module_exit(bma085_mod_exit);
360
361MODULE_AUTHOR("Invensense Corporation");
362MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU");
363MODULE_LICENSE("GPL");
364MODULE_ALIAS("bma085_mod");
365/**
366 * @}
367**/
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c
new file mode 100644
index 00000000000..22cfa4e458e
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.c
@@ -0,0 +1,268 @@
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#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/stat.h>
25#include <linux/irq.h>
26#include <linux/signal.h>
27#include <linux/miscdevice.h>
28#include <linux/i2c.h>
29#include <linux/i2c-dev.h>
30#include <linux/poll.h>
31
32#include <linux/errno.h>
33#include <linux/fs.h>
34#include <linux/mm.h>
35#include <linux/sched.h>
36#include <linux/wait.h>
37#include <linux/uaccess.h>
38#include <linux/io.h>
39#include <linux/wait.h>
40#include <linux/slab.h>
41
42#include <linux/mpu.h>
43#include "slaveirq.h"
44#include "mldl_cfg.h"
45
46/* function which gets slave data and sends it to SLAVE */
47
48struct slaveirq_dev_data {
49 struct miscdevice dev;
50 struct i2c_client *slave_client;
51 struct mpuirq_data data;
52 wait_queue_head_t slaveirq_wait;
53 int irq;
54 int pid;
55 int data_ready;
56 int timeout;
57};
58
59/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
60 * drivers: misc: pass miscdevice pointer via file private data
61 */
62static int slaveirq_open(struct inode *inode, struct file *file)
63{
64 /* Device node is availabe in the file->private_data, this is
65 * exactly what we want so we leave it there */
66 struct slaveirq_dev_data *data =
67 container_of(file->private_data, struct slaveirq_dev_data, dev);
68
69 dev_dbg(data->dev.this_device,
70 "%s current->pid %d\n", __func__, current->pid);
71 data->pid = current->pid;
72 return 0;
73}
74
75static int slaveirq_release(struct inode *inode, struct file *file)
76{
77 struct slaveirq_dev_data *data =
78 container_of(file->private_data, struct slaveirq_dev_data, dev);
79 dev_dbg(data->dev.this_device, "slaveirq_release\n");
80 return 0;
81}
82
83/* read function called when from /dev/slaveirq is read */
84static ssize_t slaveirq_read(struct file *file,
85 char *buf, size_t count, loff_t *ppos)
86{
87 int len, err;
88 struct slaveirq_dev_data *data =
89 container_of(file->private_data, struct slaveirq_dev_data, dev);
90
91 if (!data->data_ready && data->timeout &&
92 !(file->f_flags & O_NONBLOCK)) {
93 wait_event_interruptible_timeout(data->slaveirq_wait,
94 data->data_ready,
95 data->timeout);
96 }
97
98 if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
99 err = copy_to_user(buf, &data->data, sizeof(data->data));
100 data->data.data_type = 0;
101 } else {
102 return 0;
103 }
104 if (err != 0) {
105 dev_err(data->dev.this_device,
106 "Copy to user returned %d\n", err);
107 return -EFAULT;
108 }
109 data->data_ready = 0;
110 len = sizeof(data->data);
111 return len;
112}
113
114static unsigned int slaveirq_poll(struct file *file,
115 struct poll_table_struct *poll)
116{
117 int mask = 0;
118 struct slaveirq_dev_data *data =
119 container_of(file->private_data, struct slaveirq_dev_data, dev);
120
121 poll_wait(file, &data->slaveirq_wait, poll);
122 if (data->data_ready)
123 mask |= POLLIN | POLLRDNORM;
124 return mask;
125}
126
127/* ioctl - I/O control */
128static long slaveirq_ioctl(struct file *file,
129 unsigned int cmd, unsigned long arg)
130{
131 int retval = 0;
132 int tmp;
133 struct slaveirq_dev_data *data =
134 container_of(file->private_data, struct slaveirq_dev_data, dev);
135
136 switch (cmd) {
137 case SLAVEIRQ_SET_TIMEOUT:
138 data->timeout = arg;
139 break;
140
141 case SLAVEIRQ_GET_INTERRUPT_CNT:
142 tmp = data->data.interruptcount - 1;
143 if (data->data.interruptcount > 1)
144 data->data.interruptcount = 1;
145
146 if (copy_to_user((int *)arg, &tmp, sizeof(int)))
147 return -EFAULT;
148 break;
149 case SLAVEIRQ_GET_IRQ_TIME:
150 if (copy_to_user((int *)arg, &data->data.irqtime,
151 sizeof(data->data.irqtime)))
152 return -EFAULT;
153 data->data.irqtime = 0;
154 break;
155 default:
156 retval = -EINVAL;
157 }
158 return retval;
159}
160
161static irqreturn_t slaveirq_handler(int irq, void *dev_id)
162{
163 struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id;
164 static int mycount;
165 struct timeval irqtime;
166 mycount++;
167
168 data->data.interruptcount++;
169
170 /* wake up (unblock) for reading data from userspace */
171 data->data_ready = 1;
172
173 do_gettimeofday(&irqtime);
174 data->data.irqtime = (((long long)irqtime.tv_sec) << 32);
175 data->data.irqtime += irqtime.tv_usec;
176 data->data.data_type |= 1;
177
178 wake_up_interruptible(&data->slaveirq_wait);
179
180 return IRQ_HANDLED;
181
182}
183
184/* define which file operations are supported */
185static const struct file_operations slaveirq_fops = {
186 .owner = THIS_MODULE,
187 .read = slaveirq_read,
188 .poll = slaveirq_poll,
189
190#if HAVE_COMPAT_IOCTL
191 .compat_ioctl = slaveirq_ioctl,
192#endif
193#if HAVE_UNLOCKED_IOCTL
194 .unlocked_ioctl = slaveirq_ioctl,
195#endif
196 .open = slaveirq_open,
197 .release = slaveirq_release,
198};
199
200int slaveirq_init(struct i2c_adapter *slave_adapter,
201 struct ext_slave_platform_data *pdata, char *name)
202{
203
204 int res;
205 struct slaveirq_dev_data *data;
206
207 if (!pdata->irq)
208 return -EINVAL;
209
210 pdata->irq_data = kzalloc(sizeof(*data), GFP_KERNEL);
211 data = (struct slaveirq_dev_data *)pdata->irq_data;
212 if (!data)
213 return -ENOMEM;
214
215 data->dev.minor = MISC_DYNAMIC_MINOR;
216 data->dev.name = name;
217 data->dev.fops = &slaveirq_fops;
218 data->irq = pdata->irq;
219 data->pid = 0;
220 data->data_ready = 0;
221 data->timeout = 0;
222
223 init_waitqueue_head(&data->slaveirq_wait);
224
225 res = request_irq(data->irq, slaveirq_handler,
226 IRQF_TRIGGER_RISING | IRQF_SHARED,
227 data->dev.name, data);
228
229 if (res) {
230 dev_err(&slave_adapter->dev,
231 "myirqtest: cannot register IRQ %d\n", data->irq);
232 goto out_request_irq;
233 }
234
235 res = misc_register(&data->dev);
236 if (res < 0) {
237 dev_err(&slave_adapter->dev,
238 "misc_register returned %d\n", res);
239 goto out_misc_register;
240 }
241
242 return res;
243
244out_misc_register:
245 free_irq(data->irq, data);
246out_request_irq:
247 kfree(pdata->irq_data);
248 pdata->irq_data = NULL;
249
250 return res;
251}
252EXPORT_SYMBOL(slaveirq_init);
253
254void slaveirq_exit(struct ext_slave_platform_data *pdata)
255{
256 struct slaveirq_dev_data *data = pdata->irq_data;
257
258 if (!pdata->irq_data || data->irq <= 0)
259 return;
260
261 dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name);
262
263 free_irq(data->irq, data);
264 misc_deregister(&data->dev);
265 kfree(pdata->irq_data);
266 pdata->irq_data = NULL;
267}
268EXPORT_SYMBOL(slaveirq_exit);
diff --git a/drivers/misc/inv_mpu/slaveirq.h b/drivers/misc/inv_mpu/slaveirq.h
new file mode 100644
index 00000000000..6926634ff94
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.h
@@ -0,0 +1,36 @@
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#ifndef __SLAVEIRQ__
21#define __SLAVEIRQ__
22
23#include <linux/i2c-dev.h>
24
25#include <linux/mpu.h>
26#include "mpuirq.h"
27
28#define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long)
29#define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long)
30#define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long)
31
32void slaveirq_exit(struct ext_slave_platform_data *pdata);
33int slaveirq_init(struct i2c_adapter *slave_adapter,
34 struct ext_slave_platform_data *pdata, char *name);
35
36#endif
diff --git a/drivers/misc/inv_mpu/timerirq.c b/drivers/misc/inv_mpu/timerirq.c
new file mode 100644
index 00000000000..601858f9c4d
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.c
@@ -0,0 +1,296 @@
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#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/stat.h>
25#include <linux/signal.h>
26#include <linux/miscdevice.h>
27#include <linux/i2c.h>
28#include <linux/i2c-dev.h>
29#include <linux/poll.h>
30
31#include <linux/errno.h>
32#include <linux/fs.h>
33#include <linux/mm.h>
34#include <linux/sched.h>
35#include <linux/wait.h>
36#include <linux/uaccess.h>
37#include <linux/io.h>
38#include <linux/timer.h>
39#include <linux/slab.h>
40
41#include <linux/mpu.h>
42#include "mltypes.h"
43#include "timerirq.h"
44
45/* function which gets timer data and sends it to TIMER */
46struct timerirq_data {
47 int pid;
48 int data_ready;
49 int run;
50 int timeout;
51 unsigned long period;
52 struct mpuirq_data data;
53 struct completion timer_done;
54 wait_queue_head_t timerirq_wait;
55 struct timer_list timer;
56 struct miscdevice *dev;
57};
58
59static struct miscdevice *timerirq_dev_data;
60
61static void timerirq_handler(unsigned long arg)
62{
63 struct timerirq_data *data = (struct timerirq_data *)arg;
64 struct timeval irqtime;
65
66 data->data.interruptcount++;
67
68 data->data_ready = 1;
69
70 do_gettimeofday(&irqtime);
71 data->data.irqtime = (((long long)irqtime.tv_sec) << 32);
72 data->data.irqtime += irqtime.tv_usec;
73 data->data.data_type |= 1;
74
75 dev_dbg(data->dev->this_device,
76 "%s, %lld, %ld\n", __func__, data->data.irqtime,
77 (unsigned long)data);
78
79 wake_up_interruptible(&data->timerirq_wait);
80
81 if (data->run)
82 mod_timer(&data->timer,
83 jiffies + msecs_to_jiffies(data->period));
84 else
85 complete(&data->timer_done);
86}
87
88static int start_timerirq(struct timerirq_data *data)
89{
90 dev_dbg(data->dev->this_device,
91 "%s current->pid %d\n", __func__, current->pid);
92
93 /* Timer already running... success */
94 if (data->run)
95 return 0;
96
97 /* Don't allow a period of 0 since this would fire constantly */
98 if (!data->period)
99 return -EINVAL;
100
101 data->run = true;
102 data->data_ready = false;
103
104 init_completion(&data->timer_done);
105 setup_timer(&data->timer, timerirq_handler, (unsigned long)data);
106
107 return mod_timer(&data->timer,
108 jiffies + msecs_to_jiffies(data->period));
109}
110
111static int stop_timerirq(struct timerirq_data *data)
112{
113 dev_dbg(data->dev->this_device,
114 "%s current->pid %lx\n", __func__, (unsigned long)data);
115
116 if (data->run) {
117 data->run = false;
118 mod_timer(&data->timer, jiffies + 1);
119 wait_for_completion(&data->timer_done);
120 }
121 return 0;
122}
123
124/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
125 * drivers: misc: pass miscdevice pointer via file private data
126 */
127static int timerirq_open(struct inode *inode, struct file *file)
128{
129 /* Device node is availabe in the file->private_data, this is
130 * exactly what we want so we leave it there */
131 struct miscdevice *dev_data = file->private_data;
132 struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
133 if (!data)
134 return -ENOMEM;
135
136 data->dev = dev_data;
137 file->private_data = data;
138 data->pid = current->pid;
139 init_waitqueue_head(&data->timerirq_wait);
140
141 dev_dbg(data->dev->this_device,
142 "%s current->pid %d\n", __func__, current->pid);
143 return 0;
144}
145
146static int timerirq_release(struct inode *inode, struct file *file)
147{
148 struct timerirq_data *data = file->private_data;
149 dev_dbg(data->dev->this_device, "timerirq_release\n");
150 if (data->run)
151 stop_timerirq(data);
152 kfree(data);
153 return 0;
154}
155
156/* read function called when from /dev/timerirq is read */
157static ssize_t timerirq_read(struct file *file,
158 char *buf, size_t count, loff_t *ppos)
159{
160 int len, err;
161 struct timerirq_data *data = file->private_data;
162
163 if (!data->data_ready && data->timeout &&
164 !(file->f_flags & O_NONBLOCK)) {
165 wait_event_interruptible_timeout(data->timerirq_wait,
166 data->data_ready,
167 data->timeout);
168 }
169
170 if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
171 err = copy_to_user(buf, &data->data, sizeof(data->data));
172 data->data.data_type = 0;
173 } else {
174 return 0;
175 }
176 if (err != 0) {
177 dev_err(data->dev->this_device,
178 "Copy to user returned %d\n", err);
179 return -EFAULT;
180 }
181 data->data_ready = 0;
182 len = sizeof(data->data);
183 return len;
184}
185
186static unsigned int timerirq_poll(struct file *file,
187 struct poll_table_struct *poll)
188{
189 int mask = 0;
190 struct timerirq_data *data = file->private_data;
191
192 poll_wait(file, &data->timerirq_wait, poll);
193 if (data->data_ready)
194 mask |= POLLIN | POLLRDNORM;
195 return mask;
196}
197
198/* ioctl - I/O control */
199static long timerirq_ioctl(struct file *file,
200 unsigned int cmd, unsigned long arg)
201{
202 int retval = 0;
203 int tmp;
204 struct timerirq_data *data = file->private_data;
205
206 dev_dbg(data->dev->this_device,
207 "%s current->pid %d, %d, %ld\n",
208 __func__, current->pid, cmd, arg);
209
210 if (!data)
211 return -EFAULT;
212
213 switch (cmd) {
214 case TIMERIRQ_SET_TIMEOUT:
215 data->timeout = arg;
216 break;
217 case TIMERIRQ_GET_INTERRUPT_CNT:
218 tmp = data->data.interruptcount - 1;
219 if (data->data.interruptcount > 1)
220 data->data.interruptcount = 1;
221
222 if (copy_to_user((int *)arg, &tmp, sizeof(int)))
223 return -EFAULT;
224 break;
225 case TIMERIRQ_START:
226 data->period = arg;
227 retval = start_timerirq(data);
228 break;
229 case TIMERIRQ_STOP:
230 retval = stop_timerirq(data);
231 break;
232 default:
233 retval = -EINVAL;
234 }
235 return retval;
236}
237
238/* define which file operations are supported */
239static const struct file_operations timerirq_fops = {
240 .owner = THIS_MODULE,
241 .read = timerirq_read,
242 .poll = timerirq_poll,
243
244#if HAVE_COMPAT_IOCTL
245 .compat_ioctl = timerirq_ioctl,
246#endif
247#if HAVE_UNLOCKED_IOCTL
248 .unlocked_ioctl = timerirq_ioctl,
249#endif
250 .open = timerirq_open,
251 .release = timerirq_release,
252};
253
254static int __init timerirq_init(void)
255{
256
257 int res;
258 static struct miscdevice *data;
259
260 data = kzalloc(sizeof(*data), GFP_KERNEL);
261 if (!data)
262 return -ENOMEM;
263 timerirq_dev_data = data;
264 data->minor = MISC_DYNAMIC_MINOR;
265 data->name = "timerirq";
266 data->fops = &timerirq_fops;
267
268 res = misc_register(data);
269 if (res < 0) {
270 dev_err(data->this_device, "misc_register returned %d\n", res);
271 return res;
272 }
273
274 return res;
275}
276
277module_init(timerirq_init);
278
279static void __exit timerirq_exit(void)
280{
281 struct miscdevice *data = timerirq_dev_data;
282
283 dev_info(data->this_device, "Unregistering %s\n", data->name);
284
285 misc_deregister(data);
286 kfree(data);
287
288 timerirq_dev_data = NULL;
289}
290
291module_exit(timerirq_exit);
292
293MODULE_AUTHOR("Invensense Corporation");
294MODULE_DESCRIPTION("Timer IRQ device driver.");
295MODULE_LICENSE("GPL");
296MODULE_ALIAS("timerirq");
diff --git a/drivers/misc/inv_mpu/timerirq.h b/drivers/misc/inv_mpu/timerirq.h
new file mode 100644
index 00000000000..f69f07a45a3
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.h
@@ -0,0 +1,30 @@
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#ifndef __TIMERIRQ__
21#define __TIMERIRQ__
22
23#include <linux/mpu.h>
24
25#define TIMERIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x60, unsigned long)
26#define TIMERIRQ_GET_INTERRUPT_CNT _IOW(MPU_IOCTL, 0x61, unsigned long)
27#define TIMERIRQ_START _IOW(MPU_IOCTL, 0x62, unsigned long)
28#define TIMERIRQ_STOP _IO(MPU_IOCTL, 0x63)
29
30#endif