aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/ab8500-pwm.c168
-rw-r--r--drivers/misc/akm8975.c732
-rw-r--r--drivers/misc/apanic.c606
-rw-r--r--drivers/misc/bcm4329_rfkill.c207
-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
-rw-r--r--drivers/misc/iwmc3200top/Kconfig20
-rw-r--r--drivers/misc/iwmc3200top/Makefile29
-rw-r--r--drivers/misc/iwmc3200top/debugfs.c137
-rw-r--r--drivers/misc/iwmc3200top/debugfs.h58
-rw-r--r--drivers/misc/iwmc3200top/fw-download.c358
-rw-r--r--drivers/misc/iwmc3200top/fw-msg.h113
-rw-r--r--drivers/misc/iwmc3200top/iwmc3200top.h205
-rw-r--r--drivers/misc/iwmc3200top/log.c348
-rw-r--r--drivers/misc/iwmc3200top/log.h171
-rw-r--r--drivers/misc/iwmc3200top/main.c662
-rw-r--r--drivers/misc/max1749.c118
-rw-r--r--drivers/misc/mpu3050/Kconfig65
-rw-r--r--drivers/misc/mpu3050/Makefile132
-rw-r--r--drivers/misc/mpu3050/accel/kxtf9.c669
-rw-r--r--drivers/misc/mpu3050/compass/ak8975.c258
-rw-r--r--drivers/misc/mpu3050/log.h306
-rw-r--r--drivers/misc/mpu3050/mldl_cfg.c1739
-rw-r--r--drivers/misc/mpu3050/mldl_cfg.h199
-rw-r--r--drivers/misc/mpu3050/mlos-kernel.c89
-rw-r--r--drivers/misc/mpu3050/mlos.h73
-rw-r--r--drivers/misc/mpu3050/mlsl-kernel.c331
-rw-r--r--drivers/misc/mpu3050/mlsl.h103
-rw-r--r--drivers/misc/mpu3050/mltypes.h227
-rw-r--r--drivers/misc/mpu3050/mpu-dev.c1310
-rw-r--r--drivers/misc/mpu3050/mpu-i2c.c196
-rw-r--r--drivers/misc/mpu3050/mpu-i2c.h58
-rw-r--r--drivers/misc/mpu3050/mpuirq.c319
-rw-r--r--drivers/misc/mpu3050/mpuirq.h42
-rw-r--r--drivers/misc/mpu3050/slaveirq.c273
-rw-r--r--drivers/misc/mpu3050/slaveirq.h43
-rw-r--r--drivers/misc/mpu3050/timerirq.c299
-rw-r--r--drivers/misc/mpu3050/timerirq.h30
-rw-r--r--drivers/misc/nct1008.c944
-rw-r--r--drivers/misc/tegra-baseband/Kconfig32
-rw-r--r--drivers/misc/tegra-baseband/Makefile6
-rw-r--r--drivers/misc/tegra-baseband/bb-m7400.c340
-rw-r--r--drivers/misc/tegra-baseband/bb-power.c337
-rw-r--r--drivers/misc/tegra-baseband/bb-power.h99
-rw-r--r--drivers/misc/tegra-cryptodev.c349
-rw-r--r--drivers/misc/tegra-cryptodev.h70
-rw-r--r--drivers/misc/ti-st/gps_drv.c804
-rw-r--r--drivers/misc/uid_stat.c156
-rw-r--r--drivers/misc/wl127x-rfkill.c121
108 files changed, 39787 insertions, 0 deletions
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
new file mode 100644
index 00000000000..35903154ca2
--- /dev/null
+++ b/drivers/misc/ab8500-pwm.c
@@ -0,0 +1,168 @@
1/*
2 * Copyright (C) ST-Ericsson SA 2010
3 *
4 * Author: Arun R Murthy <arun.murthy@stericsson.com>
5 * License terms: GNU General Public License (GPL) version 2
6 */
7#include <linux/err.h>
8#include <linux/platform_device.h>
9#include <linux/slab.h>
10#include <linux/pwm.h>
11#include <linux/mfd/ab8500.h>
12#include <linux/mfd/abx500.h>
13
14/*
15 * PWM Out generators
16 * Bank: 0x10
17 */
18#define AB8500_PWM_OUT_CTRL1_REG 0x60
19#define AB8500_PWM_OUT_CTRL2_REG 0x61
20#define AB8500_PWM_OUT_CTRL7_REG 0x66
21
22/* backlight driver constants */
23#define ENABLE_PWM 1
24#define DISABLE_PWM 0
25
26struct pwm_device {
27 struct device *dev;
28 struct list_head node;
29 const char *label;
30 unsigned int pwm_id;
31};
32
33static LIST_HEAD(pwm_list);
34
35int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
36{
37 int ret = 0;
38 unsigned int higher_val, lower_val;
39 u8 reg;
40
41 /*
42 * get the first 8 bits that are be written to
43 * AB8500_PWM_OUT_CTRL1_REG[0:7]
44 */
45 lower_val = duty_ns & 0x00FF;
46 /*
47 * get bits [9:10] that are to be written to
48 * AB8500_PWM_OUT_CTRL2_REG[0:1]
49 */
50 higher_val = ((duty_ns & 0x0300) >> 8);
51
52 reg = AB8500_PWM_OUT_CTRL1_REG + ((pwm->pwm_id - 1) * 2);
53
54 ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
55 reg, (u8)lower_val);
56 if (ret < 0)
57 return ret;
58 ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
59 (reg + 1), (u8)higher_val);
60
61 return ret;
62}
63EXPORT_SYMBOL(pwm_config);
64
65int pwm_enable(struct pwm_device *pwm)
66{
67 int ret;
68
69 ret = abx500_mask_and_set_register_interruptible(pwm->dev,
70 AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
71 1 << (pwm->pwm_id-1), ENABLE_PWM);
72 if (ret < 0)
73 dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
74 pwm->label, ret);
75 return ret;
76}
77EXPORT_SYMBOL(pwm_enable);
78
79void pwm_disable(struct pwm_device *pwm)
80{
81 int ret;
82
83 ret = abx500_mask_and_set_register_interruptible(pwm->dev,
84 AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
85 1 << (pwm->pwm_id-1), DISABLE_PWM);
86 if (ret < 0)
87 dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
88 pwm->label, ret);
89 return;
90}
91EXPORT_SYMBOL(pwm_disable);
92
93struct pwm_device *pwm_request(int pwm_id, const char *label)
94{
95 struct pwm_device *pwm;
96
97 list_for_each_entry(pwm, &pwm_list, node) {
98 if (pwm->pwm_id == pwm_id) {
99 pwm->label = label;
100 pwm->pwm_id = pwm_id;
101 return pwm;
102 }
103 }
104
105 return ERR_PTR(-ENOENT);
106}
107EXPORT_SYMBOL(pwm_request);
108
109void pwm_free(struct pwm_device *pwm)
110{
111 pwm_disable(pwm);
112}
113EXPORT_SYMBOL(pwm_free);
114
115static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
116{
117 struct pwm_device *pwm;
118 /*
119 * Nothing to be done in probe, this is required to get the
120 * device which is required for ab8500 read and write
121 */
122 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
123 if (pwm == NULL) {
124 dev_err(&pdev->dev, "failed to allocate memory\n");
125 return -ENOMEM;
126 }
127 pwm->dev = &pdev->dev;
128 pwm->pwm_id = pdev->id;
129 list_add_tail(&pwm->node, &pwm_list);
130 platform_set_drvdata(pdev, pwm);
131 dev_dbg(pwm->dev, "pwm probe successful\n");
132 return 0;
133}
134
135static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
136{
137 struct pwm_device *pwm = platform_get_drvdata(pdev);
138 list_del(&pwm->node);
139 dev_dbg(&pdev->dev, "pwm driver removed\n");
140 kfree(pwm);
141 return 0;
142}
143
144static struct platform_driver ab8500_pwm_driver = {
145 .driver = {
146 .name = "ab8500-pwm",
147 .owner = THIS_MODULE,
148 },
149 .probe = ab8500_pwm_probe,
150 .remove = __devexit_p(ab8500_pwm_remove),
151};
152
153static int __init ab8500_pwm_init(void)
154{
155 return platform_driver_register(&ab8500_pwm_driver);
156}
157
158static void __exit ab8500_pwm_exit(void)
159{
160 platform_driver_unregister(&ab8500_pwm_driver);
161}
162
163subsys_initcall(ab8500_pwm_init);
164module_exit(ab8500_pwm_exit);
165MODULE_AUTHOR("Arun MURTHY <arun.murthy@stericsson.com>");
166MODULE_DESCRIPTION("AB8500 Pulse Width Modulation Driver");
167MODULE_ALIAS("platform:ab8500-pwm");
168MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/akm8975.c b/drivers/misc/akm8975.c
new file mode 100644
index 00000000000..aef7985d4ce
--- /dev/null
+++ b/drivers/misc/akm8975.c
@@ -0,0 +1,732 @@
1/* drivers/misc/akm8975.c - akm8975 compass driver
2 *
3 * Copyright (C) 2007-2008 HTC Corporation.
4 * Author: Hou-Kun Chen <houkun.chen@gmail.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
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 */
16
17/*
18 * Revised by AKM 2009/04/02
19 * Revised by Motorola 2010/05/27
20 *
21 */
22
23#include <linux/interrupt.h>
24#include <linux/i2c.h>
25#include <linux/slab.h>
26#include <linux/irq.h>
27#include <linux/miscdevice.h>
28#include <linux/gpio.h>
29#include <linux/uaccess.h>
30#include <linux/delay.h>
31#include <linux/input.h>
32#include <linux/workqueue.h>
33#include <linux/freezer.h>
34#include <linux/akm8975.h>
35#include <linux/earlysuspend.h>
36
37#define AK8975DRV_CALL_DBG 0
38#if AK8975DRV_CALL_DBG
39#define FUNCDBG(msg) pr_err("%s:%s\n", __func__, msg);
40#else
41#define FUNCDBG(msg)
42#endif
43
44#define AK8975DRV_DATA_DBG 0
45#define MAX_FAILURE_COUNT 10
46
47struct akm8975_data {
48 struct i2c_client *this_client;
49 struct akm8975_platform_data *pdata;
50 struct input_dev *input_dev;
51 struct work_struct work;
52 struct mutex flags_lock;
53#ifdef CONFIG_HAS_EARLYSUSPEND
54 struct early_suspend early_suspend;
55#endif
56};
57
58/*
59* Because misc devices can not carry a pointer from driver register to
60* open, we keep this global. This limits the driver to a single instance.
61*/
62struct akm8975_data *akmd_data;
63
64static DECLARE_WAIT_QUEUE_HEAD(open_wq);
65
66static atomic_t open_flag;
67
68static short m_flag;
69static short a_flag;
70static short t_flag;
71static short mv_flag;
72
73static short akmd_delay;
74
75static ssize_t akm8975_show(struct device *dev, struct device_attribute *attr,
76 char *buf)
77{
78 struct i2c_client *client = to_i2c_client(dev);
79 return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,
80 AK8975_REG_CNTL));
81}
82static ssize_t akm8975_store(struct device *dev, struct device_attribute *attr,
83 const char *buf, size_t count)
84{
85 struct i2c_client *client = to_i2c_client(dev);
86 unsigned long val;
87 strict_strtoul(buf, 10, &val);
88 if (val > 0xff)
89 return -EINVAL;
90 i2c_smbus_write_byte_data(client, AK8975_REG_CNTL, val);
91 return count;
92}
93static DEVICE_ATTR(akm_ms1, S_IWUSR | S_IRUGO, akm8975_show, akm8975_store);
94
95static int akm8975_i2c_rxdata(struct akm8975_data *akm, char *buf, int length)
96{
97 struct i2c_msg msgs[] = {
98 {
99 .addr = akm->this_client->addr,
100 .flags = 0,
101 .len = 1,
102 .buf = buf,
103 },
104 {
105 .addr = akm->this_client->addr,
106 .flags = I2C_M_RD,
107 .len = length,
108 .buf = buf,
109 },
110 };
111
112 FUNCDBG("called");
113
114 if (i2c_transfer(akm->this_client->adapter, msgs, 2) < 0) {
115 pr_err("akm8975_i2c_rxdata: transfer error\n");
116 return EIO;
117 } else
118 return 0;
119}
120
121static int akm8975_i2c_txdata(struct akm8975_data *akm, char *buf, int length)
122{
123 struct i2c_msg msgs[] = {
124 {
125 .addr = akm->this_client->addr,
126 .flags = 0,
127 .len = length,
128 .buf = buf,
129 },
130 };
131
132 FUNCDBG("called");
133
134 if (i2c_transfer(akm->this_client->adapter, msgs, 1) < 0) {
135 pr_err("akm8975_i2c_txdata: transfer error\n");
136 return -EIO;
137 } else
138 return 0;
139}
140
141static void akm8975_ecs_report_value(struct akm8975_data *akm, short *rbuf)
142{
143 struct akm8975_data *data = i2c_get_clientdata(akm->this_client);
144
145 FUNCDBG("called");
146
147#if AK8975DRV_DATA_DBG
148 pr_info("akm8975_ecs_report_value: yaw = %d, pitch = %d, roll = %d\n",
149 rbuf[0], rbuf[1], rbuf[2]);
150 pr_info("tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], rbuf[4], rbuf[5]);
151 pr_info("Acceleration: x = %d LSB, y = %d LSB, z = %d LSB\n",
152 rbuf[6], rbuf[7], rbuf[8]);
153 pr_info("Magnetic: x = %d LSB, y = %d LSB, z = %d LSB\n\n",
154 rbuf[9], rbuf[10], rbuf[11]);
155#endif
156 mutex_lock(&akm->flags_lock);
157 /* Report magnetic sensor information */
158 if (m_flag) {
159 input_report_abs(data->input_dev, ABS_RX, rbuf[0]);
160 input_report_abs(data->input_dev, ABS_RY, rbuf[1]);
161 input_report_abs(data->input_dev, ABS_RZ, rbuf[2]);
162 input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]);
163 }
164
165 /* Report acceleration sensor information */
166 if (a_flag) {
167 input_report_abs(data->input_dev, ABS_X, rbuf[6]);
168 input_report_abs(data->input_dev, ABS_Y, rbuf[7]);
169 input_report_abs(data->input_dev, ABS_Z, rbuf[8]);
170 input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]);
171 }
172
173 /* Report temperature information */
174 if (t_flag)
175 input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]);
176
177 if (mv_flag) {
178 input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]);
179 input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]);
180 input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]);
181 }
182 mutex_unlock(&akm->flags_lock);
183
184 input_sync(data->input_dev);
185}
186
187static void akm8975_ecs_close_done(struct akm8975_data *akm)
188{
189 FUNCDBG("called");
190 mutex_lock(&akm->flags_lock);
191 m_flag = 1;
192 a_flag = 1;
193 t_flag = 1;
194 mv_flag = 1;
195 mutex_unlock(&akm->flags_lock);
196}
197
198static int akm_aot_open(struct inode *inode, struct file *file)
199{
200 int ret = -1;
201
202 FUNCDBG("called");
203 if (atomic_cmpxchg(&open_flag, 0, 1) == 0) {
204 wake_up(&open_wq);
205 ret = 0;
206 }
207
208 ret = nonseekable_open(inode, file);
209 if (ret)
210 return ret;
211
212 file->private_data = akmd_data;
213
214 return ret;
215}
216
217static int akm_aot_release(struct inode *inode, struct file *file)
218{
219 FUNCDBG("called");
220 atomic_set(&open_flag, 0);
221 wake_up(&open_wq);
222 return 0;
223}
224
225static int akm_aot_ioctl(struct inode *inode, struct file *file,
226 unsigned int cmd, unsigned long arg)
227{
228 void __user *argp = (void __user *) arg;
229 short flag;
230 struct akm8975_data *akm = file->private_data;
231
232 FUNCDBG("called");
233
234 switch (cmd) {
235 case ECS_IOCTL_APP_SET_MFLAG:
236 case ECS_IOCTL_APP_SET_AFLAG:
237 case ECS_IOCTL_APP_SET_MVFLAG:
238 if (copy_from_user(&flag, argp, sizeof(flag)))
239 return -EFAULT;
240 if (flag < 0 || flag > 1)
241 return -EINVAL;
242 break;
243 case ECS_IOCTL_APP_SET_DELAY:
244 if (copy_from_user(&flag, argp, sizeof(flag)))
245 return -EFAULT;
246 break;
247 default:
248 break;
249 }
250
251 mutex_lock(&akm->flags_lock);
252 switch (cmd) {
253 case ECS_IOCTL_APP_SET_MFLAG:
254 m_flag = flag;
255 break;
256 case ECS_IOCTL_APP_GET_MFLAG:
257 flag = m_flag;
258 break;
259 case ECS_IOCTL_APP_SET_AFLAG:
260 a_flag = flag;
261 break;
262 case ECS_IOCTL_APP_GET_AFLAG:
263 flag = a_flag;
264 break;
265 case ECS_IOCTL_APP_SET_MVFLAG:
266 mv_flag = flag;
267 break;
268 case ECS_IOCTL_APP_GET_MVFLAG:
269 flag = mv_flag;
270 break;
271 case ECS_IOCTL_APP_SET_DELAY:
272 akmd_delay = flag;
273 break;
274 case ECS_IOCTL_APP_GET_DELAY:
275 flag = akmd_delay;
276 break;
277 default:
278 return -ENOTTY;
279 }
280 mutex_unlock(&akm->flags_lock);
281
282 switch (cmd) {
283 case ECS_IOCTL_APP_GET_MFLAG:
284 case ECS_IOCTL_APP_GET_AFLAG:
285 case ECS_IOCTL_APP_GET_MVFLAG:
286 case ECS_IOCTL_APP_GET_DELAY:
287 if (copy_to_user(argp, &flag, sizeof(flag)))
288 return -EFAULT;
289 break;
290 default:
291 break;
292 }
293
294 return 0;
295}
296
297static int akmd_open(struct inode *inode, struct file *file)
298{
299 int err = 0;
300
301 FUNCDBG("called");
302 err = nonseekable_open(inode, file);
303 if (err)
304 return err;
305
306 file->private_data = akmd_data;
307 return 0;
308}
309
310static int akmd_release(struct inode *inode, struct file *file)
311{
312 struct akm8975_data *akm = file->private_data;
313
314 FUNCDBG("called");
315 akm8975_ecs_close_done(akm);
316 return 0;
317}
318
319static int akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
320 unsigned long arg)
321{
322 void __user *argp = (void __user *) arg;
323
324 char rwbuf[16];
325 int ret = -1;
326 int status;
327 short value[12];
328 short delay;
329 struct akm8975_data *akm = file->private_data;
330
331 FUNCDBG("called");
332
333 switch (cmd) {
334 case ECS_IOCTL_READ:
335 case ECS_IOCTL_WRITE:
336 if (copy_from_user(&rwbuf, argp, sizeof(rwbuf)))
337 return -EFAULT;
338 break;
339
340 case ECS_IOCTL_SET_YPR:
341 if (copy_from_user(&value, argp, sizeof(value)))
342 return -EFAULT;
343 break;
344
345 default:
346 break;
347 }
348
349 switch (cmd) {
350 case ECS_IOCTL_READ:
351 if (rwbuf[0] < 1)
352 return -EINVAL;
353
354 ret = akm8975_i2c_rxdata(akm, &rwbuf[1], rwbuf[0]);
355 if (ret < 0)
356 return ret;
357 break;
358
359 case ECS_IOCTL_WRITE:
360 if (rwbuf[0] < 2)
361 return -EINVAL;
362
363 ret = akm8975_i2c_txdata(akm, &rwbuf[1], rwbuf[0]);
364 if (ret < 0)
365 return ret;
366 break;
367 case ECS_IOCTL_SET_YPR:
368 akm8975_ecs_report_value(akm, value);
369 break;
370
371 case ECS_IOCTL_GET_OPEN_STATUS:
372 wait_event_interruptible(open_wq,
373 (atomic_read(&open_flag) != 0));
374 status = atomic_read(&open_flag);
375 break;
376 case ECS_IOCTL_GET_CLOSE_STATUS:
377 wait_event_interruptible(open_wq,
378 (atomic_read(&open_flag) == 0));
379 status = atomic_read(&open_flag);
380 break;
381
382 case ECS_IOCTL_GET_DELAY:
383 delay = akmd_delay;
384 break;
385
386 default:
387 FUNCDBG("Unknown cmd\n");
388 return -ENOTTY;
389 }
390
391 switch (cmd) {
392 case ECS_IOCTL_READ:
393 if (copy_to_user(argp, &rwbuf, sizeof(rwbuf)))
394 return -EFAULT;
395 break;
396 case ECS_IOCTL_GET_OPEN_STATUS:
397 case ECS_IOCTL_GET_CLOSE_STATUS:
398 if (copy_to_user(argp, &status, sizeof(status)))
399 return -EFAULT;
400 break;
401 case ECS_IOCTL_GET_DELAY:
402 if (copy_to_user(argp, &delay, sizeof(delay)))
403 return -EFAULT;
404 break;
405 default:
406 break;
407 }
408
409 return 0;
410}
411
412/* needed to clear the int. pin */
413static void akm_work_func(struct work_struct *work)
414{
415 struct akm8975_data *akm =
416 container_of(work, struct akm8975_data, work);
417
418 FUNCDBG("called");
419 enable_irq(akm->this_client->irq);
420}
421
422static irqreturn_t akm8975_interrupt(int irq, void *dev_id)
423{
424 struct akm8975_data *akm = dev_id;
425 FUNCDBG("called");
426
427 disable_irq_nosync(akm->this_client->irq);
428 schedule_work(&akm->work);
429 return IRQ_HANDLED;
430}
431
432static int akm8975_power_off(struct akm8975_data *akm)
433{
434#if AK8975DRV_CALL_DBG
435 pr_info("%s\n", __func__);
436#endif
437 if (akm->pdata->power_off)
438 akm->pdata->power_off();
439
440 return 0;
441}
442
443static int akm8975_power_on(struct akm8975_data *akm)
444{
445 int err;
446
447#if AK8975DRV_CALL_DBG
448 pr_info("%s\n", __func__);
449#endif
450 if (akm->pdata->power_on) {
451 err = akm->pdata->power_on();
452 if (err < 0)
453 return err;
454 }
455 return 0;
456}
457
458static int akm8975_suspend(struct i2c_client *client, pm_message_t mesg)
459{
460 struct akm8975_data *akm = i2c_get_clientdata(client);
461
462#if AK8975DRV_CALL_DBG
463 pr_info("%s\n", __func__);
464#endif
465 /* TO DO: might need more work after power mgmt
466 is enabled */
467 return akm8975_power_off(akm);
468}
469
470static int akm8975_resume(struct i2c_client *client)
471{
472 struct akm8975_data *akm = i2c_get_clientdata(client);
473
474#if AK8975DRV_CALL_DBG
475 pr_info("%s\n", __func__);
476#endif
477 /* TO DO: might need more work after power mgmt
478 is enabled */
479 return akm8975_power_on(akm);
480}
481
482#ifdef CONFIG_HAS_EARLYSUSPEND
483static void akm8975_early_suspend(struct early_suspend *handler)
484{
485 struct akm8975_data *akm;
486 akm = container_of(handler, struct akm8975_data, early_suspend);
487
488#if AK8975DRV_CALL_DBG
489 pr_info("%s\n", __func__);
490#endif
491 akm8975_suspend(akm->this_client, PMSG_SUSPEND);
492}
493
494static void akm8975_early_resume(struct early_suspend *handler)
495{
496 struct akm8975_data *akm;
497 akm = container_of(handler, struct akm8975_data, early_suspend);
498
499#if AK8975DRV_CALL_DBG
500 pr_info("%s\n", __func__);
501#endif
502 akm8975_resume(akm->this_client);
503}
504#endif
505
506
507static int akm8975_init_client(struct i2c_client *client)
508{
509 struct akm8975_data *data;
510 int ret;
511
512 data = i2c_get_clientdata(client);
513
514 ret = request_irq(client->irq, akm8975_interrupt, IRQF_TRIGGER_RISING,
515 "akm8975", data);
516
517 if (ret < 0) {
518 pr_err("akm8975_init_client: request irq failed\n");
519 goto err;
520 }
521
522 init_waitqueue_head(&open_wq);
523
524 mutex_lock(&data->flags_lock);
525 m_flag = 1;
526 a_flag = 1;
527 t_flag = 1;
528 mv_flag = 1;
529 mutex_unlock(&data->flags_lock);
530
531 return 0;
532err:
533 return ret;
534}
535
536static const struct file_operations akmd_fops = {
537 .owner = THIS_MODULE,
538 .open = akmd_open,
539 .release = akmd_release,
540 .unlocked_ioctl = akmd_ioctl,
541};
542
543static const struct file_operations akm_aot_fops = {
544 .owner = THIS_MODULE,
545 .open = akm_aot_open,
546 .release = akm_aot_release,
547 .unlocked_ioctl = akm_aot_ioctl,
548};
549
550static struct miscdevice akm_aot_device = {
551 .minor = MISC_DYNAMIC_MINOR,
552 .name = "akm8975_aot",
553 .fops = &akm_aot_fops,
554};
555
556static struct miscdevice akmd_device = {
557 .minor = MISC_DYNAMIC_MINOR,
558 .name = "akm8975_dev",
559 .fops = &akmd_fops,
560};
561
562int akm8975_probe(struct i2c_client *client,
563 const struct i2c_device_id *devid)
564{
565 struct akm8975_data *akm;
566 int err;
567 FUNCDBG("called");
568
569 if (client->dev.platform_data == NULL) {
570 dev_err(&client->dev, "platform data is NULL. exiting.\n");
571 err = -ENODEV;
572 goto exit_platform_data_null;
573 }
574
575 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
576 dev_err(&client->dev, "platform data is NULL. exiting.\n");
577 err = -ENODEV;
578 goto exit_check_functionality_failed;
579 }
580
581 akm = kzalloc(sizeof(struct akm8975_data), GFP_KERNEL);
582 if (!akm) {
583 dev_err(&client->dev,
584 "failed to allocate memory for module data\n");
585 err = -ENOMEM;
586 goto exit_alloc_data_failed;
587 }
588
589 akm->pdata = client->dev.platform_data;
590
591 mutex_init(&akm->flags_lock);
592 INIT_WORK(&akm->work, akm_work_func);
593 i2c_set_clientdata(client, akm);
594
595 err = akm8975_power_on(akm);
596 if (err < 0)
597 goto exit_power_on_failed;
598
599 akm8975_init_client(client);
600 akm->this_client = client;
601 akmd_data = akm;
602
603 akm->input_dev = input_allocate_device();
604 if (!akm->input_dev) {
605 err = -ENOMEM;
606 dev_err(&akm->this_client->dev,
607 "input device allocate failed\n");
608 goto exit_input_dev_alloc_failed;
609 }
610
611 set_bit(EV_ABS, akm->input_dev->evbit);
612
613 /* yaw */
614 input_set_abs_params(akm->input_dev, ABS_RX, 0, 23040, 0, 0);
615 /* pitch */
616 input_set_abs_params(akm->input_dev, ABS_RY, -11520, 11520, 0, 0);
617 /* roll */
618 input_set_abs_params(akm->input_dev, ABS_RZ, -5760, 5760, 0, 0);
619 /* x-axis acceleration */
620 input_set_abs_params(akm->input_dev, ABS_X, -5760, 5760, 0, 0);
621 /* y-axis acceleration */
622 input_set_abs_params(akm->input_dev, ABS_Y, -5760, 5760, 0, 0);
623 /* z-axis acceleration */
624 input_set_abs_params(akm->input_dev, ABS_Z, -5760, 5760, 0, 0);
625 /* temparature */
626 input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0);
627 /* status of magnetic sensor */
628 input_set_abs_params(akm->input_dev, ABS_RUDDER, 0, 3, 0, 0);
629 /* status of acceleration sensor */
630 input_set_abs_params(akm->input_dev, ABS_WHEEL, 0, 3, 0, 0);
631 /* x-axis of raw magnetic vector */
632 input_set_abs_params(akm->input_dev, ABS_HAT0X, -20480, 20479, 0, 0);
633 /* y-axis of raw magnetic vector */
634 input_set_abs_params(akm->input_dev, ABS_HAT0Y, -20480, 20479, 0, 0);
635 /* z-axis of raw magnetic vector */
636 input_set_abs_params(akm->input_dev, ABS_BRAKE, -20480, 20479, 0, 0);
637
638 akm->input_dev->name = "compass";
639
640 err = input_register_device(akm->input_dev);
641 if (err) {
642 pr_err("akm8975_probe: Unable to register input device: %s\n",
643 akm->input_dev->name);
644 goto exit_input_register_device_failed;
645 }
646
647 err = misc_register(&akmd_device);
648 if (err) {
649 pr_err("akm8975_probe: akmd_device register failed\n");
650 goto exit_misc_device_register_failed;
651 }
652
653 err = misc_register(&akm_aot_device);
654 if (err) {
655 pr_err("akm8975_probe: akm_aot_device register failed\n");
656 goto exit_misc_device_register_failed;
657 }
658
659 err = device_create_file(&client->dev, &dev_attr_akm_ms1);
660
661#ifdef CONFIG_HAS_EARLYSUSPEND
662 akm->early_suspend.suspend = akm8975_early_suspend;
663 akm->early_suspend.resume = akm8975_early_resume;
664 register_early_suspend(&akm->early_suspend);
665#endif
666 return 0;
667
668exit_misc_device_register_failed:
669exit_input_register_device_failed:
670 input_free_device(akm->input_dev);
671exit_input_dev_alloc_failed:
672 akm8975_power_off(akm);
673exit_power_on_failed:
674 kfree(akm);
675exit_alloc_data_failed:
676exit_check_functionality_failed:
677exit_platform_data_null:
678 return err;
679}
680
681static int __devexit akm8975_remove(struct i2c_client *client)
682{
683 struct akm8975_data *akm = i2c_get_clientdata(client);
684 FUNCDBG("called");
685 free_irq(client->irq, NULL);
686 input_unregister_device(akm->input_dev);
687 misc_deregister(&akmd_device);
688 misc_deregister(&akm_aot_device);
689 akm8975_power_off(akm);
690 kfree(akm);
691 return 0;
692}
693
694static const struct i2c_device_id akm8975_id[] = {
695 { "akm8975", 0 },
696 { }
697};
698
699MODULE_DEVICE_TABLE(i2c, akm8975_id);
700
701static struct i2c_driver akm8975_driver = {
702 .probe = akm8975_probe,
703 .remove = akm8975_remove,
704#ifndef CONFIG_HAS_EARLYSUSPEND
705 .resume = akm8975_resume,
706 .suspend = akm8975_suspend,
707#endif
708 .id_table = akm8975_id,
709 .driver = {
710 .name = "akm8975",
711 },
712};
713
714static int __init akm8975_init(void)
715{
716 pr_info("AK8975 compass driver: init\n");
717 FUNCDBG("AK8975 compass driver: init\n");
718 return i2c_add_driver(&akm8975_driver);
719}
720
721static void __exit akm8975_exit(void)
722{
723 FUNCDBG("AK8975 compass driver: exit\n");
724 i2c_del_driver(&akm8975_driver);
725}
726
727module_init(akm8975_init);
728module_exit(akm8975_exit);
729
730MODULE_AUTHOR("Hou-Kun Chen <hk_chen@htc.com>");
731MODULE_DESCRIPTION("AK8975 compass driver");
732MODULE_LICENSE("GPL");
diff --git a/drivers/misc/apanic.c b/drivers/misc/apanic.c
new file mode 100644
index 00000000000..ca875f89da7
--- /dev/null
+++ b/drivers/misc/apanic.c
@@ -0,0 +1,606 @@
1/* drivers/misc/apanic.c
2 *
3 * Copyright (C) 2009 Google, Inc.
4 * Author: San Mehat <san@android.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
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 */
16
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/string.h>
20#include <linux/errno.h>
21#include <linux/init.h>
22#include <linux/interrupt.h>
23#include <linux/device.h>
24#include <linux/types.h>
25#include <linux/delay.h>
26#include <linux/sched.h>
27#include <linux/wait.h>
28#include <linux/wakelock.h>
29#include <linux/platform_device.h>
30#include <linux/uaccess.h>
31#include <linux/mtd/mtd.h>
32#include <linux/notifier.h>
33#include <linux/mtd/mtd.h>
34#include <linux/debugfs.h>
35#include <linux/fs.h>
36#include <linux/proc_fs.h>
37#include <linux/mutex.h>
38#include <linux/workqueue.h>
39#include <linux/preempt.h>
40
41extern void ram_console_enable_console(int);
42
43struct panic_header {
44 u32 magic;
45#define PANIC_MAGIC 0xdeadf00d
46
47 u32 version;
48#define PHDR_VERSION 0x01
49
50 u32 console_offset;
51 u32 console_length;
52
53 u32 threads_offset;
54 u32 threads_length;
55};
56
57struct apanic_data {
58 struct mtd_info *mtd;
59 struct panic_header curr;
60 void *bounce;
61 struct proc_dir_entry *apanic_console;
62 struct proc_dir_entry *apanic_threads;
63};
64
65static struct apanic_data drv_ctx;
66static struct work_struct proc_removal_work;
67static DEFINE_MUTEX(drv_mutex);
68
69static unsigned int *apanic_bbt;
70static unsigned int apanic_erase_blocks;
71static unsigned int apanic_good_blocks;
72
73static void set_bb(unsigned int block, unsigned int *bbt)
74{
75 unsigned int flag = 1;
76
77 BUG_ON(block >= apanic_erase_blocks);
78
79 flag = flag << (block%32);
80 apanic_bbt[block/32] |= flag;
81 apanic_good_blocks--;
82}
83
84static unsigned int get_bb(unsigned int block, unsigned int *bbt)
85{
86 unsigned int flag;
87
88 BUG_ON(block >= apanic_erase_blocks);
89
90 flag = 1 << (block%32);
91 return apanic_bbt[block/32] & flag;
92}
93
94static void alloc_bbt(struct mtd_info *mtd, unsigned int *bbt)
95{
96 int bbt_size;
97 apanic_erase_blocks = (mtd->size)>>(mtd->erasesize_shift);
98 bbt_size = (apanic_erase_blocks+32)/32;
99
100 apanic_bbt = kmalloc(bbt_size*4, GFP_KERNEL);
101 memset(apanic_bbt, 0, bbt_size*4);
102 apanic_good_blocks = apanic_erase_blocks;
103}
104static void scan_bbt(struct mtd_info *mtd, unsigned int *bbt)
105{
106 int i;
107
108 for (i = 0; i < apanic_erase_blocks; i++) {
109 if (mtd->block_isbad(mtd, i*mtd->erasesize))
110 set_bb(i, apanic_bbt);
111 }
112}
113
114#define APANIC_INVALID_OFFSET 0xFFFFFFFF
115
116static unsigned int phy_offset(struct mtd_info *mtd, unsigned int offset)
117{
118 unsigned int logic_block = offset>>(mtd->erasesize_shift);
119 unsigned int phy_block;
120 unsigned good_block = 0;
121
122 for (phy_block = 0; phy_block < apanic_erase_blocks; phy_block++) {
123 if (!get_bb(phy_block, apanic_bbt))
124 good_block++;
125 if (good_block == (logic_block + 1))
126 break;
127 }
128
129 if (good_block != (logic_block + 1))
130 return APANIC_INVALID_OFFSET;
131
132 return offset + ((phy_block-logic_block)<<mtd->erasesize_shift);
133}
134
135static void apanic_erase_callback(struct erase_info *done)
136{
137 wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
138 wake_up(wait_q);
139}
140
141static int apanic_proc_read(char *buffer, char **start, off_t offset,
142 int count, int *peof, void *dat)
143{
144 struct apanic_data *ctx = &drv_ctx;
145 size_t file_length;
146 off_t file_offset;
147 unsigned int page_no;
148 off_t page_offset;
149 int rc;
150 size_t len;
151
152 if (!count)
153 return 0;
154
155 mutex_lock(&drv_mutex);
156
157 switch ((int) dat) {
158 case 1: /* apanic_console */
159 file_length = ctx->curr.console_length;
160 file_offset = ctx->curr.console_offset;
161 break;
162 case 2: /* apanic_threads */
163 file_length = ctx->curr.threads_length;
164 file_offset = ctx->curr.threads_offset;
165 break;
166 default:
167 pr_err("Bad dat (%d)\n", (int) dat);
168 mutex_unlock(&drv_mutex);
169 return -EINVAL;
170 }
171
172 if ((offset + count) > file_length) {
173 mutex_unlock(&drv_mutex);
174 return 0;
175 }
176
177 /* We only support reading a maximum of a flash page */
178 if (count > ctx->mtd->writesize)
179 count = ctx->mtd->writesize;
180
181 page_no = (file_offset + offset) / ctx->mtd->writesize;
182 page_offset = (file_offset + offset) % ctx->mtd->writesize;
183
184
185 if (phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize))
186 == APANIC_INVALID_OFFSET) {
187 pr_err("apanic: reading an invalid address\n");
188 mutex_unlock(&drv_mutex);
189 return -EINVAL;
190 }
191 rc = ctx->mtd->read(ctx->mtd,
192 phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)),
193 ctx->mtd->writesize,
194 &len, ctx->bounce);
195
196 if (page_offset)
197 count -= page_offset;
198 memcpy(buffer, ctx->bounce + page_offset, count);
199
200 *start = count;
201
202 if ((offset + count) == file_length)
203 *peof = 1;
204
205 mutex_unlock(&drv_mutex);
206 return count;
207}
208
209static void mtd_panic_erase(void)
210{
211 struct apanic_data *ctx = &drv_ctx;
212 struct erase_info erase;
213 DECLARE_WAITQUEUE(wait, current);
214 wait_queue_head_t wait_q;
215 int rc, i;
216
217 init_waitqueue_head(&wait_q);
218 erase.mtd = ctx->mtd;
219 erase.callback = apanic_erase_callback;
220 erase.len = ctx->mtd->erasesize;
221 erase.priv = (u_long)&wait_q;
222 for (i = 0; i < ctx->mtd->size; i += ctx->mtd->erasesize) {
223 erase.addr = i;
224 set_current_state(TASK_INTERRUPTIBLE);
225 add_wait_queue(&wait_q, &wait);
226
227 if (get_bb(erase.addr>>ctx->mtd->erasesize_shift, apanic_bbt)) {
228 printk(KERN_WARNING
229 "apanic: Skipping erase of bad "
230 "block @%llx\n", erase.addr);
231 set_current_state(TASK_RUNNING);
232 remove_wait_queue(&wait_q, &wait);
233 continue;
234 }
235
236 rc = ctx->mtd->erase(ctx->mtd, &erase);
237 if (rc) {
238 set_current_state(TASK_RUNNING);
239 remove_wait_queue(&wait_q, &wait);
240 printk(KERN_ERR
241 "apanic: Erase of 0x%llx, 0x%llx failed\n",
242 (unsigned long long) erase.addr,
243 (unsigned long long) erase.len);
244 if (rc == -EIO) {
245 if (ctx->mtd->block_markbad(ctx->mtd,
246 erase.addr)) {
247 printk(KERN_ERR
248 "apanic: Err marking blk bad\n");
249 goto out;
250 }
251 printk(KERN_INFO
252 "apanic: Marked a bad block"
253 " @%llx\n", erase.addr);
254 set_bb(erase.addr>>ctx->mtd->erasesize_shift,
255 apanic_bbt);
256 continue;
257 }
258 goto out;
259 }
260 schedule();
261 remove_wait_queue(&wait_q, &wait);
262 }
263 printk(KERN_DEBUG "apanic: %s partition erased\n",
264 CONFIG_APANIC_PLABEL);
265out:
266 return;
267}
268
269static void apanic_remove_proc_work(struct work_struct *work)
270{
271 struct apanic_data *ctx = &drv_ctx;
272
273 mutex_lock(&drv_mutex);
274 mtd_panic_erase();
275 memset(&ctx->curr, 0, sizeof(struct panic_header));
276 if (ctx->apanic_console) {
277 remove_proc_entry("apanic_console", NULL);
278 ctx->apanic_console = NULL;
279 }
280 if (ctx->apanic_threads) {
281 remove_proc_entry("apanic_threads", NULL);
282 ctx->apanic_threads = NULL;
283 }
284 mutex_unlock(&drv_mutex);
285}
286
287static int apanic_proc_write(struct file *file, const char __user *buffer,
288 unsigned long count, void *data)
289{
290 schedule_work(&proc_removal_work);
291 return count;
292}
293
294static void mtd_panic_notify_add(struct mtd_info *mtd)
295{
296 struct apanic_data *ctx = &drv_ctx;
297 struct panic_header *hdr = ctx->bounce;
298 size_t len;
299 int rc;
300 int proc_entry_created = 0;
301
302 if (strcmp(mtd->name, CONFIG_APANIC_PLABEL))
303 return;
304
305 ctx->mtd = mtd;
306
307 alloc_bbt(mtd, apanic_bbt);
308 scan_bbt(mtd, apanic_bbt);
309
310 if (apanic_good_blocks == 0) {
311 printk(KERN_ERR "apanic: no any good blocks?!\n");
312 goto out_err;
313 }
314
315 rc = mtd->read(mtd, phy_offset(mtd, 0), mtd->writesize,
316 &len, ctx->bounce);
317 if (rc && rc == -EBADMSG) {
318 printk(KERN_WARNING
319 "apanic: Bad ECC on block 0 (ignored)\n");
320 } else if (rc && rc != -EUCLEAN) {
321 printk(KERN_ERR "apanic: Error reading block 0 (%d)\n", rc);
322 goto out_err;
323 }
324
325 if (len != mtd->writesize) {
326 printk(KERN_ERR "apanic: Bad read size (%d)\n", rc);
327 goto out_err;
328 }
329
330 printk(KERN_INFO "apanic: Bound to mtd partition '%s'\n", mtd->name);
331
332 if (hdr->magic != PANIC_MAGIC) {
333 printk(KERN_INFO "apanic: No panic data available\n");
334 mtd_panic_erase();
335 return;
336 }
337
338 if (hdr->version != PHDR_VERSION) {
339 printk(KERN_INFO "apanic: Version mismatch (%d != %d)\n",
340 hdr->version, PHDR_VERSION);
341 mtd_panic_erase();
342 return;
343 }
344
345 memcpy(&ctx->curr, hdr, sizeof(struct panic_header));
346
347 printk(KERN_INFO "apanic: c(%u, %u) t(%u, %u)\n",
348 hdr->console_offset, hdr->console_length,
349 hdr->threads_offset, hdr->threads_length);
350
351 if (hdr->console_length) {
352 ctx->apanic_console = create_proc_entry("apanic_console",
353 S_IFREG | S_IRUGO, NULL);
354 if (!ctx->apanic_console)
355 printk(KERN_ERR "%s: failed creating procfile\n",
356 __func__);
357 else {
358 ctx->apanic_console->read_proc = apanic_proc_read;
359 ctx->apanic_console->write_proc = apanic_proc_write;
360 ctx->apanic_console->size = hdr->console_length;
361 ctx->apanic_console->data = (void *) 1;
362 proc_entry_created = 1;
363 }
364 }
365
366 if (hdr->threads_length) {
367 ctx->apanic_threads = create_proc_entry("apanic_threads",
368 S_IFREG | S_IRUGO, NULL);
369 if (!ctx->apanic_threads)
370 printk(KERN_ERR "%s: failed creating procfile\n",
371 __func__);
372 else {
373 ctx->apanic_threads->read_proc = apanic_proc_read;
374 ctx->apanic_threads->write_proc = apanic_proc_write;
375 ctx->apanic_threads->size = hdr->threads_length;
376 ctx->apanic_threads->data = (void *) 2;
377 proc_entry_created = 1;
378 }
379 }
380
381 if (!proc_entry_created)
382 mtd_panic_erase();
383
384 return;
385out_err:
386 ctx->mtd = NULL;
387}
388
389static void mtd_panic_notify_remove(struct mtd_info *mtd)
390{
391 struct apanic_data *ctx = &drv_ctx;
392 if (mtd == ctx->mtd) {
393 ctx->mtd = NULL;
394 printk(KERN_INFO "apanic: Unbound from %s\n", mtd->name);
395 }
396}
397
398static struct mtd_notifier mtd_panic_notifier = {
399 .add = mtd_panic_notify_add,
400 .remove = mtd_panic_notify_remove,
401};
402
403static int in_panic = 0;
404
405static int apanic_writeflashpage(struct mtd_info *mtd, loff_t to,
406 const u_char *buf)
407{
408 int rc;
409 size_t wlen;
410 int panic = in_interrupt() | in_atomic();
411
412 if (panic && !mtd->panic_write) {
413 printk(KERN_EMERG "%s: No panic_write available\n", __func__);
414 return 0;
415 } else if (!panic && !mtd->write) {
416 printk(KERN_EMERG "%s: No write available\n", __func__);
417 return 0;
418 }
419
420 to = phy_offset(mtd, to);
421 if (to == APANIC_INVALID_OFFSET) {
422 printk(KERN_EMERG "apanic: write to invalid address\n");
423 return 0;
424 }
425
426 if (panic)
427 rc = mtd->panic_write(mtd, to, mtd->writesize, &wlen, buf);
428 else
429 rc = mtd->write(mtd, to, mtd->writesize, &wlen, buf);
430
431 if (rc) {
432 printk(KERN_EMERG
433 "%s: Error writing data to flash (%d)\n",
434 __func__, rc);
435 return rc;
436 }
437
438 return wlen;
439}
440
441extern int log_buf_copy(char *dest, int idx, int len);
442extern void log_buf_clear(void);
443
444/*
445 * Writes the contents of the console to the specified offset in flash.
446 * Returns number of bytes written
447 */
448static int apanic_write_console(struct mtd_info *mtd, unsigned int off)
449{
450 struct apanic_data *ctx = &drv_ctx;
451 int saved_oip;
452 int idx = 0;
453 int rc, rc2;
454 unsigned int last_chunk = 0;
455
456 while (!last_chunk) {
457 saved_oip = oops_in_progress;
458 oops_in_progress = 1;
459 rc = log_buf_copy(ctx->bounce, idx, mtd->writesize);
460 if (rc < 0)
461 break;
462
463 if (rc != mtd->writesize)
464 last_chunk = rc;
465
466 oops_in_progress = saved_oip;
467 if (rc <= 0)
468 break;
469 if (rc != mtd->writesize)
470 memset(ctx->bounce + rc, 0, mtd->writesize - rc);
471
472 rc2 = apanic_writeflashpage(mtd, off, ctx->bounce);
473 if (rc2 <= 0) {
474 printk(KERN_EMERG
475 "apanic: Flash write failed (%d)\n", rc2);
476 return idx;
477 }
478 if (!last_chunk)
479 idx += rc2;
480 else
481 idx += last_chunk;
482 off += rc2;
483 }
484 return idx;
485}
486
487static int apanic(struct notifier_block *this, unsigned long event,
488 void *ptr)
489{
490 struct apanic_data *ctx = &drv_ctx;
491 struct panic_header *hdr = (struct panic_header *) ctx->bounce;
492 int console_offset = 0;
493 int console_len = 0;
494 int threads_offset = 0;
495 int threads_len = 0;
496 int rc;
497
498 if (in_panic)
499 return NOTIFY_DONE;
500 in_panic = 1;
501#ifdef CONFIG_PREEMPT
502 /* Ensure that cond_resched() won't try to preempt anybody */
503 add_preempt_count(PREEMPT_ACTIVE);
504#endif
505 touch_softlockup_watchdog();
506
507 if (!ctx->mtd)
508 goto out;
509
510 if (ctx->curr.magic) {
511 printk(KERN_EMERG "Crash partition in use!\n");
512 goto out;
513 }
514 console_offset = ctx->mtd->writesize;
515
516 /*
517 * Write out the console
518 */
519 console_len = apanic_write_console(ctx->mtd, console_offset);
520 if (console_len < 0) {
521 printk(KERN_EMERG "Error writing console to panic log! (%d)\n",
522 console_len);
523 console_len = 0;
524 }
525
526 /*
527 * Write out all threads
528 */
529 threads_offset = ALIGN(console_offset + console_len,
530 ctx->mtd->writesize);
531 if (!threads_offset)
532 threads_offset = ctx->mtd->writesize;
533
534 ram_console_enable_console(0);
535
536 log_buf_clear();
537 show_state_filter(0);
538 threads_len = apanic_write_console(ctx->mtd, threads_offset);
539 if (threads_len < 0) {
540 printk(KERN_EMERG "Error writing threads to panic log! (%d)\n",
541 threads_len);
542 threads_len = 0;
543 }
544
545 /*
546 * Finally write the panic header
547 */
548 memset(ctx->bounce, 0, PAGE_SIZE);
549 hdr->magic = PANIC_MAGIC;
550 hdr->version = PHDR_VERSION;
551
552 hdr->console_offset = console_offset;
553 hdr->console_length = console_len;
554
555 hdr->threads_offset = threads_offset;
556 hdr->threads_length = threads_len;
557
558 rc = apanic_writeflashpage(ctx->mtd, 0, ctx->bounce);
559 if (rc <= 0) {
560 printk(KERN_EMERG "apanic: Header write failed (%d)\n",
561 rc);
562 goto out;
563 }
564
565 printk(KERN_EMERG "apanic: Panic dump sucessfully written to flash\n");
566
567 out:
568#ifdef CONFIG_PREEMPT
569 sub_preempt_count(PREEMPT_ACTIVE);
570#endif
571 in_panic = 0;
572 return NOTIFY_DONE;
573}
574
575static struct notifier_block panic_blk = {
576 .notifier_call = apanic,
577};
578
579static int panic_dbg_get(void *data, u64 *val)
580{
581 apanic(NULL, 0, NULL);
582 return 0;
583}
584
585static int panic_dbg_set(void *data, u64 val)
586{
587 BUG();
588 return -1;
589}
590
591DEFINE_SIMPLE_ATTRIBUTE(panic_dbg_fops, panic_dbg_get, panic_dbg_set, "%llu\n");
592
593int __init apanic_init(void)
594{
595 register_mtd_user(&mtd_panic_notifier);
596 atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
597 debugfs_create_file("apanic", 0644, NULL, NULL, &panic_dbg_fops);
598 memset(&drv_ctx, 0, sizeof(drv_ctx));
599 drv_ctx.bounce = (void *) __get_free_page(GFP_KERNEL);
600 INIT_WORK(&proc_removal_work, apanic_remove_proc_work);
601 printk(KERN_INFO "Android kernel panic handler initialized (bind=%s)\n",
602 CONFIG_APANIC_PLABEL);
603 return 0;
604}
605
606module_init(apanic_init);
diff --git a/drivers/misc/bcm4329_rfkill.c b/drivers/misc/bcm4329_rfkill.c
new file mode 100644
index 00000000000..a077326f255
--- /dev/null
+++ b/drivers/misc/bcm4329_rfkill.c
@@ -0,0 +1,207 @@
1/*
2 * drivers/misc/bcm4329_rfkill.c
3 *
4 * Copyright (c) 2010, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/err.h>
22#include <linux/types.h>
23#include <linux/uaccess.h>
24#include <linux/fs.h>
25#include <linux/gpio.h>
26#include <linux/init.h>
27#include <linux/kernel.h>
28#include <linux/miscdevice.h>
29#include <linux/module.h>
30#include <linux/rfkill.h>
31#include <linux/platform_device.h>
32#include <linux/clk.h>
33#include <linux/slab.h>
34#include <linux/delay.h>
35
36struct bcm4329_rfkill_data {
37 int gpio_reset;
38 int gpio_shutdown;
39 int delay;
40 struct clk *bt_32k_clk;
41};
42
43static struct bcm4329_rfkill_data *bcm4329_rfkill;
44
45static int bcm4329_bt_rfkill_set_power(void *data, bool blocked)
46{
47 if (blocked) {
48 if (bcm4329_rfkill->gpio_shutdown)
49 gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0);
50 if (bcm4329_rfkill->gpio_reset)
51 gpio_direction_output(bcm4329_rfkill->gpio_reset, 0);
52 if (bcm4329_rfkill->bt_32k_clk)
53 clk_disable(bcm4329_rfkill->bt_32k_clk);
54 } else {
55 if (bcm4329_rfkill->bt_32k_clk)
56 clk_enable(bcm4329_rfkill->bt_32k_clk);
57 if (bcm4329_rfkill->gpio_shutdown)
58 {
59 gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 0);
60 msleep(100);
61 gpio_direction_output(bcm4329_rfkill->gpio_shutdown, 1);
62 msleep(100);
63 }
64 if (bcm4329_rfkill->gpio_reset)
65 {
66 gpio_direction_output(bcm4329_rfkill->gpio_reset, 0);
67 msleep(100);
68 gpio_direction_output(bcm4329_rfkill->gpio_reset, 1);
69 msleep(100);
70 }
71 }
72
73 return 0;
74}
75
76static const struct rfkill_ops bcm4329_bt_rfkill_ops = {
77 .set_block = bcm4329_bt_rfkill_set_power,
78};
79
80static int bcm4329_rfkill_probe(struct platform_device *pdev)
81{
82 struct rfkill *bt_rfkill;
83 struct resource *res;
84 int ret;
85 bool enable = false; /* off */
86 bool default_sw_block_state;
87
88 bcm4329_rfkill = kzalloc(sizeof(*bcm4329_rfkill), GFP_KERNEL);
89 if (!bcm4329_rfkill)
90 return -ENOMEM;
91
92 bcm4329_rfkill->bt_32k_clk = clk_get(&pdev->dev, "bcm4329_32k_clk");
93 if (IS_ERR(bcm4329_rfkill->bt_32k_clk)) {
94 pr_warn("%s: can't find bcm4329_32k_clk.\
95 assuming 32k clock to chip\n", __func__);
96 bcm4329_rfkill->bt_32k_clk = NULL;
97 }
98
99 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
100 "bcm4329_nreset_gpio");
101 if (res) {
102 bcm4329_rfkill->gpio_reset = res->start;
103 tegra_gpio_enable(bcm4329_rfkill->gpio_reset);
104 ret = gpio_request(bcm4329_rfkill->gpio_reset,
105 "bcm4329_nreset_gpio");
106 } else {
107 pr_warn("%s : can't find reset gpio.\n", __func__);
108 bcm4329_rfkill->gpio_reset = 0;
109 }
110
111 res = platform_get_resource_byname(pdev, IORESOURCE_IO,
112 "bcm4329_nshutdown_gpio");
113 if (res) {
114 bcm4329_rfkill->gpio_shutdown = res->start;
115 tegra_gpio_enable(bcm4329_rfkill->gpio_shutdown);
116 ret = gpio_request(bcm4329_rfkill->gpio_shutdown,
117 "bcm4329_nshutdown_gpio");
118 } else {
119 pr_warn("%s : can't find shutdown gpio.\n", __func__);
120 bcm4329_rfkill->gpio_shutdown = 0;
121 }
122
123 /* make sure at-least one of the GPIO is defined */
124 if (!bcm4329_rfkill->gpio_reset && !bcm4329_rfkill->gpio_shutdown)
125 goto free_bcm_res;
126
127 if (bcm4329_rfkill->bt_32k_clk && enable)
128 clk_enable(bcm4329_rfkill->bt_32k_clk);
129 if (bcm4329_rfkill->gpio_shutdown)
130 gpio_direction_output(bcm4329_rfkill->gpio_shutdown, enable);
131 if (bcm4329_rfkill->gpio_reset)
132 gpio_direction_output(bcm4329_rfkill->gpio_reset, enable);
133
134 bt_rfkill = rfkill_alloc("bcm4329 Bluetooth", &pdev->dev,
135 RFKILL_TYPE_BLUETOOTH, &bcm4329_bt_rfkill_ops,
136 NULL);
137
138 if (unlikely(!bt_rfkill))
139 goto free_bcm_res;
140
141 default_sw_block_state = !enable;
142 rfkill_set_states(bt_rfkill, default_sw_block_state, false);
143
144 ret = rfkill_register(bt_rfkill);
145
146 if (unlikely(ret)) {
147 rfkill_destroy(bt_rfkill);
148 goto free_bcm_res;
149 }
150
151 return 0;
152
153free_bcm_res:
154 if (bcm4329_rfkill->gpio_shutdown)
155 gpio_free(bcm4329_rfkill->gpio_shutdown);
156 if (bcm4329_rfkill->gpio_reset)
157 gpio_free(bcm4329_rfkill->gpio_reset);
158 if (bcm4329_rfkill->bt_32k_clk && enable)
159 clk_disable(bcm4329_rfkill->bt_32k_clk);
160 if (bcm4329_rfkill->bt_32k_clk)
161 clk_put(bcm4329_rfkill->bt_32k_clk);
162 kfree(bcm4329_rfkill);
163 return -ENODEV;
164}
165
166static int bcm4329_rfkill_remove(struct platform_device *pdev)
167{
168 struct rfkill *bt_rfkill = platform_get_drvdata(pdev);
169
170 if (bcm4329_rfkill->bt_32k_clk)
171 clk_put(bcm4329_rfkill->bt_32k_clk);
172 rfkill_unregister(bt_rfkill);
173 rfkill_destroy(bt_rfkill);
174 if (bcm4329_rfkill->gpio_shutdown)
175 gpio_free(bcm4329_rfkill->gpio_shutdown);
176 if (bcm4329_rfkill->gpio_reset)
177 gpio_free(bcm4329_rfkill->gpio_reset);
178 kfree(bcm4329_rfkill);
179
180 return 0;
181}
182
183static struct platform_driver bcm4329_rfkill_driver = {
184 .probe = bcm4329_rfkill_probe,
185 .remove = bcm4329_rfkill_remove,
186 .driver = {
187 .name = "bcm4329_rfkill",
188 .owner = THIS_MODULE,
189 },
190};
191
192static int __init bcm4329_rfkill_init(void)
193{
194 return platform_driver_register(&bcm4329_rfkill_driver);
195}
196
197static void __exit bcm4329_rfkill_exit(void)
198{
199 platform_driver_unregister(&bcm4329_rfkill_driver);
200}
201
202module_init(bcm4329_rfkill_init);
203module_exit(bcm4329_rfkill_exit);
204
205MODULE_DESCRIPTION("BCM4329 rfkill");
206MODULE_AUTHOR("NVIDIA");
207MODULE_LICENSE("GPL");
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
diff --git a/drivers/misc/iwmc3200top/Kconfig b/drivers/misc/iwmc3200top/Kconfig
new file mode 100644
index 00000000000..9e4b88fb57f
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Kconfig
@@ -0,0 +1,20 @@
1config IWMC3200TOP
2 tristate "Intel Wireless MultiCom Top Driver"
3 depends on MMC && EXPERIMENTAL
4 select FW_LOADER
5 ---help---
6 Intel Wireless MultiCom 3200 Top driver is responsible for
7 for firmware load and enabled coms enumeration
8
9config IWMC3200TOP_DEBUG
10 bool "Enable full debug output of iwmc3200top Driver"
11 depends on IWMC3200TOP
12 ---help---
13 Enable full debug output of iwmc3200top Driver
14
15config IWMC3200TOP_DEBUGFS
16 bool "Enable Debugfs debugging interface for iwmc3200top"
17 depends on IWMC3200TOP
18 ---help---
19 Enable creation of debugfs files for iwmc3200top
20
diff --git a/drivers/misc/iwmc3200top/Makefile b/drivers/misc/iwmc3200top/Makefile
new file mode 100644
index 00000000000..fbf53fb4634
--- /dev/null
+++ b/drivers/misc/iwmc3200top/Makefile
@@ -0,0 +1,29 @@
1# iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
2# drivers/misc/iwmc3200top/Makefile
3#
4# Copyright (C) 2009 Intel Corporation. All rights reserved.
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License version
8# 2 as published by the Free Software Foundation.
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, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18# 02110-1301, USA.
19#
20#
21# Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
22# -
23#
24#
25
26obj-$(CONFIG_IWMC3200TOP) += iwmc3200top.o
27iwmc3200top-objs := main.o fw-download.o
28iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUG) += log.o
29iwmc3200top-$(CONFIG_IWMC3200TOP_DEBUGFS) += debugfs.o
diff --git a/drivers/misc/iwmc3200top/debugfs.c b/drivers/misc/iwmc3200top/debugfs.c
new file mode 100644
index 00000000000..62fbaec4820
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.c
@@ -0,0 +1,137 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/debufs.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/kernel.h>
28#include <linux/slab.h>
29#include <linux/string.h>
30#include <linux/ctype.h>
31#include <linux/mmc/sdio_func.h>
32#include <linux/mmc/sdio.h>
33#include <linux/debugfs.h>
34
35#include "iwmc3200top.h"
36#include "fw-msg.h"
37#include "log.h"
38#include "debugfs.h"
39
40
41
42/* Constants definition */
43#define HEXADECIMAL_RADIX 16
44
45/* Functions definition */
46
47
48#define DEBUGFS_ADD(name, parent) do { \
49 dbgfs->dbgfs_##parent##_files.file_##name = \
50 debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \
51 &iwmct_dbgfs_##name##_ops); \
52} while (0)
53
54#define DEBUGFS_RM(name) do { \
55 debugfs_remove(name); \
56 name = NULL; \
57} while (0)
58
59#define DEBUGFS_READ_FUNC(name) \
60ssize_t iwmct_dbgfs_##name##_read(struct file *file, \
61 char __user *user_buf, \
62 size_t count, loff_t *ppos);
63
64#define DEBUGFS_WRITE_FUNC(name) \
65ssize_t iwmct_dbgfs_##name##_write(struct file *file, \
66 const char __user *user_buf, \
67 size_t count, loff_t *ppos);
68
69#define DEBUGFS_READ_FILE_OPS(name) \
70 DEBUGFS_READ_FUNC(name) \
71 static const struct file_operations iwmct_dbgfs_##name##_ops = { \
72 .read = iwmct_dbgfs_##name##_read, \
73 .open = iwmct_dbgfs_open_file_generic, \
74 .llseek = generic_file_llseek, \
75 };
76
77#define DEBUGFS_WRITE_FILE_OPS(name) \
78 DEBUGFS_WRITE_FUNC(name) \
79 static const struct file_operations iwmct_dbgfs_##name##_ops = { \
80 .write = iwmct_dbgfs_##name##_write, \
81 .open = iwmct_dbgfs_open_file_generic, \
82 .llseek = generic_file_llseek, \
83 };
84
85#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
86 DEBUGFS_READ_FUNC(name) \
87 DEBUGFS_WRITE_FUNC(name) \
88 static const struct file_operations iwmct_dbgfs_##name##_ops = {\
89 .write = iwmct_dbgfs_##name##_write, \
90 .read = iwmct_dbgfs_##name##_read, \
91 .open = iwmct_dbgfs_open_file_generic, \
92 .llseek = generic_file_llseek, \
93 };
94
95
96/* Debugfs file ops definitions */
97
98/*
99 * Create the debugfs files and directories
100 *
101 */
102void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
103{
104 struct iwmct_debugfs *dbgfs;
105
106 dbgfs = kzalloc(sizeof(struct iwmct_debugfs), GFP_KERNEL);
107 if (!dbgfs) {
108 LOG_ERROR(priv, DEBUGFS, "failed to allocate %zd bytes\n",
109 sizeof(struct iwmct_debugfs));
110 return;
111 }
112
113 priv->dbgfs = dbgfs;
114 dbgfs->name = name;
115 dbgfs->dir_drv = debugfs_create_dir(name, NULL);
116 if (!dbgfs->dir_drv) {
117 LOG_ERROR(priv, DEBUGFS, "failed to create debugfs dir\n");
118 return;
119 }
120
121 return;
122}
123
124/**
125 * Remove the debugfs files and directories
126 *
127 */
128void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
129{
130 if (!dbgfs)
131 return;
132
133 DEBUGFS_RM(dbgfs->dir_drv);
134 kfree(dbgfs);
135 dbgfs = NULL;
136}
137
diff --git a/drivers/misc/iwmc3200top/debugfs.h b/drivers/misc/iwmc3200top/debugfs.h
new file mode 100644
index 00000000000..71d45759b40
--- /dev/null
+++ b/drivers/misc/iwmc3200top/debugfs.h
@@ -0,0 +1,58 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/debufs.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __DEBUGFS_H__
28#define __DEBUGFS_H__
29
30
31#ifdef CONFIG_IWMC3200TOP_DEBUGFS
32
33struct iwmct_debugfs {
34 const char *name;
35 struct dentry *dir_drv;
36 struct dir_drv_files {
37 } dbgfs_drv_files;
38};
39
40void iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name);
41void iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs);
42
43#else /* CONFIG_IWMC3200TOP_DEBUGFS */
44
45struct iwmct_debugfs;
46
47static inline void
48iwmct_dbgfs_register(struct iwmct_priv *priv, const char *name)
49{}
50
51static inline void
52iwmct_dbgfs_unregister(struct iwmct_debugfs *dbgfs)
53{}
54
55#endif /* CONFIG_IWMC3200TOP_DEBUGFS */
56
57#endif /* __DEBUGFS_H__ */
58
diff --git a/drivers/misc/iwmc3200top/fw-download.c b/drivers/misc/iwmc3200top/fw-download.c
new file mode 100644
index 00000000000..e27afde6e99
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-download.c
@@ -0,0 +1,358 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/fw-download.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/firmware.h>
28#include <linux/mmc/sdio_func.h>
29#include <linux/slab.h>
30#include <asm/unaligned.h>
31
32#include "iwmc3200top.h"
33#include "log.h"
34#include "fw-msg.h"
35
36#define CHECKSUM_BYTES_NUM sizeof(u32)
37
38/**
39 init parser struct with file
40 */
41static int iwmct_fw_parser_init(struct iwmct_priv *priv, const u8 *file,
42 size_t file_size, size_t block_size)
43{
44 struct iwmct_parser *parser = &priv->parser;
45 struct iwmct_fw_hdr *fw_hdr = &parser->versions;
46
47 LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
48
49 LOG_INFO(priv, FW_DOWNLOAD, "file_size=%zd\n", file_size);
50
51 parser->file = file;
52 parser->file_size = file_size;
53 parser->cur_pos = 0;
54 parser->entry_point = 0;
55 parser->buf = kzalloc(block_size, GFP_KERNEL);
56 if (!parser->buf) {
57 LOG_ERROR(priv, FW_DOWNLOAD, "kzalloc error\n");
58 return -ENOMEM;
59 }
60 parser->buf_size = block_size;
61
62 /* extract fw versions */
63 memcpy(fw_hdr, parser->file, sizeof(struct iwmct_fw_hdr));
64 LOG_INFO(priv, FW_DOWNLOAD, "fw versions are:\n"
65 "top %u.%u.%u gps %u.%u.%u bt %u.%u.%u tic %s\n",
66 fw_hdr->top_major, fw_hdr->top_minor, fw_hdr->top_revision,
67 fw_hdr->gps_major, fw_hdr->gps_minor, fw_hdr->gps_revision,
68 fw_hdr->bt_major, fw_hdr->bt_minor, fw_hdr->bt_revision,
69 fw_hdr->tic_name);
70
71 parser->cur_pos += sizeof(struct iwmct_fw_hdr);
72
73 LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
74 return 0;
75}
76
77static bool iwmct_checksum(struct iwmct_priv *priv)
78{
79 struct iwmct_parser *parser = &priv->parser;
80 __le32 *file = (__le32 *)parser->file;
81 int i, pad, steps;
82 u32 accum = 0;
83 u32 checksum;
84 u32 mask = 0xffffffff;
85
86 pad = (parser->file_size - CHECKSUM_BYTES_NUM) % 4;
87 steps = (parser->file_size - CHECKSUM_BYTES_NUM) / 4;
88
89 LOG_INFO(priv, FW_DOWNLOAD, "pad=%d steps=%d\n", pad, steps);
90
91 for (i = 0; i < steps; i++)
92 accum += le32_to_cpu(file[i]);
93
94 if (pad) {
95 mask <<= 8 * (4 - pad);
96 accum += le32_to_cpu(file[steps]) & mask;
97 }
98
99 checksum = get_unaligned_le32((__le32 *)(parser->file +
100 parser->file_size - CHECKSUM_BYTES_NUM));
101
102 LOG_INFO(priv, FW_DOWNLOAD,
103 "compare checksum accum=0x%x to checksum=0x%x\n",
104 accum, checksum);
105
106 return checksum == accum;
107}
108
109static int iwmct_parse_next_section(struct iwmct_priv *priv, const u8 **p_sec,
110 size_t *sec_size, __le32 *sec_addr)
111{
112 struct iwmct_parser *parser = &priv->parser;
113 struct iwmct_dbg *dbg = &priv->dbg;
114 struct iwmct_fw_sec_hdr *sec_hdr;
115
116 LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
117
118 while (parser->cur_pos + sizeof(struct iwmct_fw_sec_hdr)
119 <= parser->file_size) {
120
121 sec_hdr = (struct iwmct_fw_sec_hdr *)
122 (parser->file + parser->cur_pos);
123 parser->cur_pos += sizeof(struct iwmct_fw_sec_hdr);
124
125 LOG_INFO(priv, FW_DOWNLOAD,
126 "sec hdr: type=%s addr=0x%x size=%d\n",
127 sec_hdr->type, sec_hdr->target_addr,
128 sec_hdr->data_size);
129
130 if (strcmp(sec_hdr->type, "ENT") == 0)
131 parser->entry_point = le32_to_cpu(sec_hdr->target_addr);
132 else if (strcmp(sec_hdr->type, "LBL") == 0)
133 strcpy(dbg->label_fw, parser->file + parser->cur_pos);
134 else if (((strcmp(sec_hdr->type, "TOP") == 0) &&
135 (priv->barker & BARKER_DNLOAD_TOP_MSK)) ||
136 ((strcmp(sec_hdr->type, "GPS") == 0) &&
137 (priv->barker & BARKER_DNLOAD_GPS_MSK)) ||
138 ((strcmp(sec_hdr->type, "BTH") == 0) &&
139 (priv->barker & BARKER_DNLOAD_BT_MSK))) {
140 *sec_addr = sec_hdr->target_addr;
141 *sec_size = le32_to_cpu(sec_hdr->data_size);
142 *p_sec = parser->file + parser->cur_pos;
143 parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
144 return 1;
145 } else if (strcmp(sec_hdr->type, "LOG") != 0)
146 LOG_WARNING(priv, FW_DOWNLOAD,
147 "skipping section type %s\n",
148 sec_hdr->type);
149
150 parser->cur_pos += le32_to_cpu(sec_hdr->data_size);
151 LOG_INFO(priv, FW_DOWNLOAD,
152 "finished with section cur_pos=%zd\n", parser->cur_pos);
153 }
154
155 LOG_TRACE(priv, INIT, "<--\n");
156 return 0;
157}
158
159static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec,
160 size_t sec_size, __le32 addr)
161{
162 struct iwmct_parser *parser = &priv->parser;
163 struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
164 const u8 *cur_block = p_sec;
165 size_t sent = 0;
166 int cnt = 0;
167 int ret = 0;
168 u32 cmd = 0;
169
170 LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
171 LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n",
172 addr, sec_size);
173
174 while (sent < sec_size) {
175 int i;
176 u32 chksm = 0;
177 u32 reset = atomic_read(&priv->reset);
178 /* actual FW data */
179 u32 data_size = min(parser->buf_size - sizeof(*hdr),
180 sec_size - sent);
181 /* Pad to block size */
182 u32 trans_size = (data_size + sizeof(*hdr) +
183 IWMC_SDIO_BLK_SIZE - 1) &
184 ~(IWMC_SDIO_BLK_SIZE - 1);
185 ++cnt;
186
187 /* in case of reset, interrupt FW DOWNLAOD */
188 if (reset) {
189 LOG_INFO(priv, FW_DOWNLOAD,
190 "Reset detected. Abort FW download!!!");
191 ret = -ECANCELED;
192 goto exit;
193 }
194
195 memset(parser->buf, 0, parser->buf_size);
196 cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS;
197 cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
198 cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS;
199 cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS;
200 hdr->data_size = cpu_to_le32(data_size);
201 hdr->target_addr = addr;
202
203 /* checksum is allowed for sizes divisible by 4 */
204 if (data_size & 0x3)
205 cmd &= ~CMD_HDR_USE_CHECKSUM_MSK;
206
207 memcpy(hdr->data, cur_block, data_size);
208
209
210 if (cmd & CMD_HDR_USE_CHECKSUM_MSK) {
211
212 chksm = data_size + le32_to_cpu(addr) + cmd;
213 for (i = 0; i < data_size >> 2; i++)
214 chksm += ((u32 *)cur_block)[i];
215
216 hdr->block_chksm = cpu_to_le32(chksm);
217 LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n",
218 hdr->block_chksm);
219 }
220
221 LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, "
222 "sec_size=%zd, startAddress 0x%X\n",
223 cnt, trans_size, sent, sec_size, addr);
224
225 if (priv->dbg.dump)
226 LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size);
227
228
229 hdr->cmd = cpu_to_le32(cmd);
230 /* send it down */
231 /* TODO: add more proper sending and error checking */
232 ret = iwmct_tx(priv, parser->buf, trans_size);
233 if (ret != 0) {
234 LOG_INFO(priv, FW_DOWNLOAD,
235 "iwmct_tx returned %d\n", ret);
236 goto exit;
237 }
238
239 addr = cpu_to_le32(le32_to_cpu(addr) + data_size);
240 sent += data_size;
241 cur_block = p_sec + sent;
242
243 if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) {
244 LOG_INFO(priv, FW_DOWNLOAD,
245 "Block number limit is reached [%d]\n",
246 priv->dbg.blocks);
247 break;
248 }
249 }
250
251 if (sent < sec_size)
252 ret = -EINVAL;
253exit:
254 LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
255 return ret;
256}
257
258static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump)
259{
260 struct iwmct_parser *parser = &priv->parser;
261 struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf;
262 int ret;
263 u32 cmd;
264
265 LOG_TRACE(priv, FW_DOWNLOAD, "-->\n");
266
267 memset(parser->buf, 0, parser->buf_size);
268 cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS;
269 if (jump) {
270 cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS;
271 hdr->target_addr = cpu_to_le32(parser->entry_point);
272 LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n",
273 parser->entry_point);
274 } else {
275 cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS;
276 LOG_INFO(priv, FW_DOWNLOAD, "last command\n");
277 }
278
279 hdr->cmd = cpu_to_le32(cmd);
280
281 LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr));
282 /* send it down */
283 /* TODO: add more proper sending and error checking */
284 ret = iwmct_tx(priv, parser->buf, IWMC_SDIO_BLK_SIZE);
285 if (ret)
286 LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret);
287
288 LOG_TRACE(priv, FW_DOWNLOAD, "<--\n");
289 return 0;
290}
291
292int iwmct_fw_load(struct iwmct_priv *priv)
293{
294 const u8 *fw_name = FW_NAME(FW_API_VER);
295 const struct firmware *raw;
296 const u8 *pdata;
297 size_t len;
298 __le32 addr;
299 int ret;
300
301
302 LOG_INFO(priv, FW_DOWNLOAD, "barker download request 0x%x is:\n",
303 priv->barker);
304 LOG_INFO(priv, FW_DOWNLOAD, "******* Top FW %s requested ********\n",
305 (priv->barker & BARKER_DNLOAD_TOP_MSK) ? "was" : "not");
306 LOG_INFO(priv, FW_DOWNLOAD, "******* GPS FW %s requested ********\n",
307 (priv->barker & BARKER_DNLOAD_GPS_MSK) ? "was" : "not");
308 LOG_INFO(priv, FW_DOWNLOAD, "******* BT FW %s requested ********\n",
309 (priv->barker & BARKER_DNLOAD_BT_MSK) ? "was" : "not");
310
311
312 /* get the firmware */
313 ret = request_firmware(&raw, fw_name, &priv->func->dev);
314 if (ret < 0) {
315 LOG_ERROR(priv, FW_DOWNLOAD, "%s request_firmware failed %d\n",
316 fw_name, ret);
317 goto exit;
318 }
319
320 if (raw->size < sizeof(struct iwmct_fw_sec_hdr)) {
321 LOG_ERROR(priv, FW_DOWNLOAD, "%s smaller then (%zd) (%zd)\n",
322 fw_name, sizeof(struct iwmct_fw_sec_hdr), raw->size);
323 goto exit;
324 }
325
326 LOG_INFO(priv, FW_DOWNLOAD, "Read firmware '%s'\n", fw_name);
327
328 /* clear parser struct */
329 ret = iwmct_fw_parser_init(priv, raw->data, raw->size, priv->trans_len);
330 if (ret < 0) {
331 LOG_ERROR(priv, FW_DOWNLOAD,
332 "iwmct_parser_init failed: Reason %d\n", ret);
333 goto exit;
334 }
335
336 if (!iwmct_checksum(priv)) {
337 LOG_ERROR(priv, FW_DOWNLOAD, "checksum error\n");
338 ret = -EINVAL;
339 goto exit;
340 }
341
342 /* download firmware to device */
343 while (iwmct_parse_next_section(priv, &pdata, &len, &addr)) {
344 ret = iwmct_download_section(priv, pdata, len, addr);
345 if (ret) {
346 LOG_ERROR(priv, FW_DOWNLOAD,
347 "%s download section failed\n", fw_name);
348 goto exit;
349 }
350 }
351
352 ret = iwmct_kick_fw(priv, !!(priv->barker & BARKER_DNLOAD_JUMP_MSK));
353
354exit:
355 kfree(priv->parser.buf);
356 release_firmware(raw);
357 return ret;
358}
diff --git a/drivers/misc/iwmc3200top/fw-msg.h b/drivers/misc/iwmc3200top/fw-msg.h
new file mode 100644
index 00000000000..9e26b75bd48
--- /dev/null
+++ b/drivers/misc/iwmc3200top/fw-msg.h
@@ -0,0 +1,113 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/fw-msg.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __FWMSG_H__
28#define __FWMSG_H__
29
30#define COMM_TYPE_D2H 0xFF
31#define COMM_TYPE_H2D 0xEE
32
33#define COMM_CATEGORY_OPERATIONAL 0x00
34#define COMM_CATEGORY_DEBUG 0x01
35#define COMM_CATEGORY_TESTABILITY 0x02
36#define COMM_CATEGORY_DIAGNOSTICS 0x03
37
38#define OP_DBG_ZSTR_MSG cpu_to_le16(0x1A)
39
40#define FW_LOG_SRC_MAX 32
41#define FW_LOG_SRC_ALL 255
42
43#define FW_STRING_TABLE_ADDR cpu_to_le32(0x0C000000)
44
45#define CMD_DBG_LOG_LEVEL cpu_to_le16(0x0001)
46#define CMD_TST_DEV_RESET cpu_to_le16(0x0060)
47#define CMD_TST_FUNC_RESET cpu_to_le16(0x0062)
48#define CMD_TST_IFACE_RESET cpu_to_le16(0x0064)
49#define CMD_TST_CPU_UTILIZATION cpu_to_le16(0x0065)
50#define CMD_TST_TOP_DEEP_SLEEP cpu_to_le16(0x0080)
51#define CMD_TST_WAKEUP cpu_to_le16(0x0081)
52#define CMD_TST_FUNC_WAKEUP cpu_to_le16(0x0082)
53#define CMD_TST_FUNC_DEEP_SLEEP_REQUEST cpu_to_le16(0x0083)
54#define CMD_TST_GET_MEM_DUMP cpu_to_le16(0x0096)
55
56#define OP_OPR_ALIVE cpu_to_le16(0x0010)
57#define OP_OPR_CMD_ACK cpu_to_le16(0x001F)
58#define OP_OPR_CMD_NACK cpu_to_le16(0x0020)
59#define OP_TST_MEM_DUMP cpu_to_le16(0x0043)
60
61#define CMD_FLAG_PADDING_256 0x80
62
63#define FW_HCMD_BLOCK_SIZE 256
64
65struct msg_hdr {
66 u8 type;
67 u8 category;
68 __le16 opcode;
69 u8 seqnum;
70 u8 flags;
71 __le16 length;
72} __attribute__((__packed__));
73
74struct log_hdr {
75 __le32 timestamp;
76 u8 severity;
77 u8 logsource;
78 __le16 reserved;
79} __attribute__((__packed__));
80
81struct mdump_hdr {
82 u8 dmpid;
83 u8 frag;
84 __le16 size;
85 __le32 addr;
86} __attribute__((__packed__));
87
88struct top_msg {
89 struct msg_hdr hdr;
90 union {
91 /* D2H messages */
92 struct {
93 struct log_hdr log_hdr;
94 u8 data[1];
95 } __attribute__((__packed__)) log;
96
97 struct {
98 struct log_hdr log_hdr;
99 struct mdump_hdr md_hdr;
100 u8 data[1];
101 } __attribute__((__packed__)) mdump;
102
103 /* H2D messages */
104 struct {
105 u8 logsource;
106 u8 sevmask;
107 } __attribute__((__packed__)) logdefs[FW_LOG_SRC_MAX];
108 struct mdump_hdr mdump_req;
109 } u;
110} __attribute__((__packed__));
111
112
113#endif /* __FWMSG_H__ */
diff --git a/drivers/misc/iwmc3200top/iwmc3200top.h b/drivers/misc/iwmc3200top/iwmc3200top.h
new file mode 100644
index 00000000000..620973ed8bf
--- /dev/null
+++ b/drivers/misc/iwmc3200top/iwmc3200top.h
@@ -0,0 +1,205 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/iwmc3200top.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __IWMC3200TOP_H__
28#define __IWMC3200TOP_H__
29
30#include <linux/workqueue.h>
31
32#define DRV_NAME "iwmc3200top"
33#define FW_API_VER 1
34#define _FW_NAME(api) DRV_NAME "." #api ".fw"
35#define FW_NAME(api) _FW_NAME(api)
36
37#define IWMC_SDIO_BLK_SIZE 256
38#define IWMC_DEFAULT_TR_BLK 64
39#define IWMC_SDIO_DATA_ADDR 0x0
40#define IWMC_SDIO_INTR_ENABLE_ADDR 0x14
41#define IWMC_SDIO_INTR_STATUS_ADDR 0x13
42#define IWMC_SDIO_INTR_CLEAR_ADDR 0x13
43#define IWMC_SDIO_INTR_GET_SIZE_ADDR 0x2C
44
45#define COMM_HUB_HEADER_LENGTH 16
46#define LOGGER_HEADER_LENGTH 10
47
48
49#define BARKER_DNLOAD_BT_POS 0
50#define BARKER_DNLOAD_BT_MSK BIT(BARKER_DNLOAD_BT_POS)
51#define BARKER_DNLOAD_GPS_POS 1
52#define BARKER_DNLOAD_GPS_MSK BIT(BARKER_DNLOAD_GPS_POS)
53#define BARKER_DNLOAD_TOP_POS 2
54#define BARKER_DNLOAD_TOP_MSK BIT(BARKER_DNLOAD_TOP_POS)
55#define BARKER_DNLOAD_RESERVED1_POS 3
56#define BARKER_DNLOAD_RESERVED1_MSK BIT(BARKER_DNLOAD_RESERVED1_POS)
57#define BARKER_DNLOAD_JUMP_POS 4
58#define BARKER_DNLOAD_JUMP_MSK BIT(BARKER_DNLOAD_JUMP_POS)
59#define BARKER_DNLOAD_SYNC_POS 5
60#define BARKER_DNLOAD_SYNC_MSK BIT(BARKER_DNLOAD_SYNC_POS)
61#define BARKER_DNLOAD_RESERVED2_POS 6
62#define BARKER_DNLOAD_RESERVED2_MSK (0x3 << BARKER_DNLOAD_RESERVED2_POS)
63#define BARKER_DNLOAD_BARKER_POS 8
64#define BARKER_DNLOAD_BARKER_MSK (0xffffff << BARKER_DNLOAD_BARKER_POS)
65
66#define IWMC_BARKER_REBOOT (0xdeadbe << BARKER_DNLOAD_BARKER_POS)
67/* whole field barker */
68#define IWMC_BARKER_ACK 0xfeedbabe
69
70#define IWMC_CMD_SIGNATURE 0xcbbc
71
72#define CMD_HDR_OPCODE_POS 0
73#define CMD_HDR_OPCODE_MSK_MSK (0xf << CMD_HDR_OPCODE_MSK_POS)
74#define CMD_HDR_RESPONSE_CODE_POS 4
75#define CMD_HDR_RESPONSE_CODE_MSK (0xf << CMD_HDR_RESPONSE_CODE_POS)
76#define CMD_HDR_USE_CHECKSUM_POS 8
77#define CMD_HDR_USE_CHECKSUM_MSK BIT(CMD_HDR_USE_CHECKSUM_POS)
78#define CMD_HDR_RESPONSE_REQUIRED_POS 9
79#define CMD_HDR_RESPONSE_REQUIRED_MSK BIT(CMD_HDR_RESPONSE_REQUIRED_POS)
80#define CMD_HDR_DIRECT_ACCESS_POS 10
81#define CMD_HDR_DIRECT_ACCESS_MSK BIT(CMD_HDR_DIRECT_ACCESS_POS)
82#define CMD_HDR_RESERVED_POS 11
83#define CMD_HDR_RESERVED_MSK BIT(0x1f << CMD_HDR_RESERVED_POS)
84#define CMD_HDR_SIGNATURE_POS 16
85#define CMD_HDR_SIGNATURE_MSK BIT(0xffff << CMD_HDR_SIGNATURE_POS)
86
87enum {
88 IWMC_OPCODE_PING = 0,
89 IWMC_OPCODE_READ = 1,
90 IWMC_OPCODE_WRITE = 2,
91 IWMC_OPCODE_JUMP = 3,
92 IWMC_OPCODE_REBOOT = 4,
93 IWMC_OPCODE_PERSISTENT_WRITE = 5,
94 IWMC_OPCODE_PERSISTENT_READ = 6,
95 IWMC_OPCODE_READ_MODIFY_WRITE = 7,
96 IWMC_OPCODE_LAST_COMMAND = 15
97};
98
99struct iwmct_fw_load_hdr {
100 __le32 cmd;
101 __le32 target_addr;
102 __le32 data_size;
103 __le32 block_chksm;
104 u8 data[0];
105};
106
107/**
108 * struct iwmct_fw_hdr
109 * holds all sw components versions
110 */
111struct iwmct_fw_hdr {
112 u8 top_major;
113 u8 top_minor;
114 u8 top_revision;
115 u8 gps_major;
116 u8 gps_minor;
117 u8 gps_revision;
118 u8 bt_major;
119 u8 bt_minor;
120 u8 bt_revision;
121 u8 tic_name[31];
122};
123
124/**
125 * struct iwmct_fw_sec_hdr
126 * @type: function type
127 * @data_size: section's data size
128 * @target_addr: download address
129 */
130struct iwmct_fw_sec_hdr {
131 u8 type[4];
132 __le32 data_size;
133 __le32 target_addr;
134};
135
136/**
137 * struct iwmct_parser
138 * @file: fw image
139 * @file_size: fw size
140 * @cur_pos: position in file
141 * @buf: temp buf for download
142 * @buf_size: size of buf
143 * @entry_point: address to jump in fw kick-off
144 */
145struct iwmct_parser {
146 const u8 *file;
147 size_t file_size;
148 size_t cur_pos;
149 u8 *buf;
150 size_t buf_size;
151 u32 entry_point;
152 struct iwmct_fw_hdr versions;
153};
154
155
156struct iwmct_work_struct {
157 struct list_head list;
158 ssize_t iosize;
159};
160
161struct iwmct_dbg {
162 int blocks;
163 bool dump;
164 bool jump;
165 bool direct;
166 bool checksum;
167 bool fw_download;
168 int block_size;
169 int download_trans_blks;
170
171 char label_fw[256];
172};
173
174struct iwmct_debugfs;
175
176struct iwmct_priv {
177 struct sdio_func *func;
178 struct iwmct_debugfs *dbgfs;
179 struct iwmct_parser parser;
180 atomic_t reset;
181 atomic_t dev_sync;
182 u32 trans_len;
183 u32 barker;
184 struct iwmct_dbg dbg;
185
186 /* drivers work items */
187 struct work_struct bus_rescan_worker;
188 struct work_struct isr_worker;
189
190 /* drivers wait queue */
191 wait_queue_head_t wait_q;
192
193 /* rx request list */
194 struct list_head read_req_list;
195};
196
197extern int iwmct_tx(struct iwmct_priv *priv, void *src, int count);
198extern int iwmct_fw_load(struct iwmct_priv *priv);
199
200extern void iwmct_dbg_init_params(struct iwmct_priv *drv);
201extern void iwmct_dbg_init_drv_attrs(struct device_driver *drv);
202extern void iwmct_dbg_remove_drv_attrs(struct device_driver *drv);
203extern int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len);
204
205#endif /* __IWMC3200TOP_H__ */
diff --git a/drivers/misc/iwmc3200top/log.c b/drivers/misc/iwmc3200top/log.c
new file mode 100644
index 00000000000..a36a55a49ca
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.c
@@ -0,0 +1,348 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/log.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/kernel.h>
28#include <linux/mmc/sdio_func.h>
29#include <linux/slab.h>
30#include <linux/ctype.h>
31#include "fw-msg.h"
32#include "iwmc3200top.h"
33#include "log.h"
34
35/* Maximal hexadecimal string size of the FW memdump message */
36#define LOG_MSG_SIZE_MAX 12400
37
38/* iwmct_logdefs is a global used by log macros */
39u8 iwmct_logdefs[LOG_SRC_MAX];
40static u8 iwmct_fw_logdefs[FW_LOG_SRC_MAX];
41
42
43static int _log_set_log_filter(u8 *logdefs, int size, u8 src, u8 logmask)
44{
45 int i;
46
47 if (src < size)
48 logdefs[src] = logmask;
49 else if (src == LOG_SRC_ALL)
50 for (i = 0; i < size; i++)
51 logdefs[i] = logmask;
52 else
53 return -1;
54
55 return 0;
56}
57
58
59int iwmct_log_set_filter(u8 src, u8 logmask)
60{
61 return _log_set_log_filter(iwmct_logdefs, LOG_SRC_MAX, src, logmask);
62}
63
64
65int iwmct_log_set_fw_filter(u8 src, u8 logmask)
66{
67 return _log_set_log_filter(iwmct_fw_logdefs,
68 FW_LOG_SRC_MAX, src, logmask);
69}
70
71
72static int log_msg_format_hex(char *str, int slen, u8 *ibuf,
73 int ilen, char *pref)
74{
75 int pos = 0;
76 int i;
77 int len;
78
79 for (pos = 0, i = 0; pos < slen - 2 && pref[i] != '\0'; i++, pos++)
80 str[pos] = pref[i];
81
82 for (i = 0; pos < slen - 2 && i < ilen; pos += len, i++)
83 len = snprintf(&str[pos], slen - pos - 1, " %2.2X", ibuf[i]);
84
85 if (i < ilen)
86 return -1;
87
88 return 0;
89}
90
91/* NOTE: This function is not thread safe.
92 Currently it's called only from sdio rx worker - no race there
93*/
94void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len)
95{
96 struct top_msg *msg;
97 static char logbuf[LOG_MSG_SIZE_MAX];
98
99 msg = (struct top_msg *)buf;
100
101 if (len < sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr)) {
102 LOG_ERROR(priv, FW_MSG, "Log message from TOP "
103 "is too short %d (expected %zd)\n",
104 len, sizeof(msg->hdr) + sizeof(msg->u.log.log_hdr));
105 return;
106 }
107
108 if (!(iwmct_fw_logdefs[msg->u.log.log_hdr.logsource] &
109 BIT(msg->u.log.log_hdr.severity)) ||
110 !(iwmct_logdefs[LOG_SRC_FW_MSG] & BIT(msg->u.log.log_hdr.severity)))
111 return;
112
113 switch (msg->hdr.category) {
114 case COMM_CATEGORY_TESTABILITY:
115 if (!(iwmct_logdefs[LOG_SRC_TST] &
116 BIT(msg->u.log.log_hdr.severity)))
117 return;
118 if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
119 le16_to_cpu(msg->hdr.length) +
120 sizeof(msg->hdr), "<TST>"))
121 LOG_WARNING(priv, TST,
122 "TOP TST message is too long, truncating...");
123 LOG_WARNING(priv, TST, "%s\n", logbuf);
124 break;
125 case COMM_CATEGORY_DEBUG:
126 if (msg->hdr.opcode == OP_DBG_ZSTR_MSG)
127 LOG_INFO(priv, FW_MSG, "%s %s", "<DBG>",
128 ((u8 *)msg) + sizeof(msg->hdr)
129 + sizeof(msg->u.log.log_hdr));
130 else {
131 if (log_msg_format_hex(logbuf, LOG_MSG_SIZE_MAX, buf,
132 le16_to_cpu(msg->hdr.length)
133 + sizeof(msg->hdr),
134 "<DBG>"))
135 LOG_WARNING(priv, FW_MSG,
136 "TOP DBG message is too long,"
137 "truncating...");
138 LOG_WARNING(priv, FW_MSG, "%s\n", logbuf);
139 }
140 break;
141 default:
142 break;
143 }
144}
145
146static int _log_get_filter_str(u8 *logdefs, int logdefsz, char *buf, int size)
147{
148 int i, pos, len;
149 for (i = 0, pos = 0; (pos < size-1) && (i < logdefsz); i++) {
150 len = snprintf(&buf[pos], size - pos - 1, "0x%02X%02X,",
151 i, logdefs[i]);
152 pos += len;
153 }
154 buf[pos-1] = '\n';
155 buf[pos] = '\0';
156
157 if (i < logdefsz)
158 return -1;
159 return 0;
160}
161
162int log_get_filter_str(char *buf, int size)
163{
164 return _log_get_filter_str(iwmct_logdefs, LOG_SRC_MAX, buf, size);
165}
166
167int log_get_fw_filter_str(char *buf, int size)
168{
169 return _log_get_filter_str(iwmct_fw_logdefs, FW_LOG_SRC_MAX, buf, size);
170}
171
172#define HEXADECIMAL_RADIX 16
173#define LOG_SRC_FORMAT 7 /* log level is in format of "0xXXXX," */
174
175ssize_t show_iwmct_log_level(struct device *d,
176 struct device_attribute *attr, char *buf)
177{
178 struct iwmct_priv *priv = dev_get_drvdata(d);
179 char *str_buf;
180 int buf_size;
181 ssize_t ret;
182
183 buf_size = (LOG_SRC_FORMAT * LOG_SRC_MAX) + 1;
184 str_buf = kzalloc(buf_size, GFP_KERNEL);
185 if (!str_buf) {
186 LOG_ERROR(priv, DEBUGFS,
187 "failed to allocate %d bytes\n", buf_size);
188 ret = -ENOMEM;
189 goto exit;
190 }
191
192 if (log_get_filter_str(str_buf, buf_size) < 0) {
193 ret = -EINVAL;
194 goto exit;
195 }
196
197 ret = sprintf(buf, "%s", str_buf);
198
199exit:
200 kfree(str_buf);
201 return ret;
202}
203
204ssize_t store_iwmct_log_level(struct device *d,
205 struct device_attribute *attr,
206 const char *buf, size_t count)
207{
208 struct iwmct_priv *priv = dev_get_drvdata(d);
209 char *token, *str_buf = NULL;
210 long val;
211 ssize_t ret = count;
212 u8 src, mask;
213
214 if (!count)
215 goto exit;
216
217 str_buf = kzalloc(count, GFP_KERNEL);
218 if (!str_buf) {
219 LOG_ERROR(priv, DEBUGFS,
220 "failed to allocate %zd bytes\n", count);
221 ret = -ENOMEM;
222 goto exit;
223 }
224
225 memcpy(str_buf, buf, count);
226
227 while ((token = strsep(&str_buf, ",")) != NULL) {
228 while (isspace(*token))
229 ++token;
230 if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
231 LOG_ERROR(priv, DEBUGFS,
232 "failed to convert string to long %s\n",
233 token);
234 ret = -EINVAL;
235 goto exit;
236 }
237
238 mask = val & 0xFF;
239 src = (val & 0XFF00) >> 8;
240 iwmct_log_set_filter(src, mask);
241 }
242
243exit:
244 kfree(str_buf);
245 return ret;
246}
247
248ssize_t show_iwmct_log_level_fw(struct device *d,
249 struct device_attribute *attr, char *buf)
250{
251 struct iwmct_priv *priv = dev_get_drvdata(d);
252 char *str_buf;
253 int buf_size;
254 ssize_t ret;
255
256 buf_size = (LOG_SRC_FORMAT * FW_LOG_SRC_MAX) + 2;
257
258 str_buf = kzalloc(buf_size, GFP_KERNEL);
259 if (!str_buf) {
260 LOG_ERROR(priv, DEBUGFS,
261 "failed to allocate %d bytes\n", buf_size);
262 ret = -ENOMEM;
263 goto exit;
264 }
265
266 if (log_get_fw_filter_str(str_buf, buf_size) < 0) {
267 ret = -EINVAL;
268 goto exit;
269 }
270
271 ret = sprintf(buf, "%s", str_buf);
272
273exit:
274 kfree(str_buf);
275 return ret;
276}
277
278ssize_t store_iwmct_log_level_fw(struct device *d,
279 struct device_attribute *attr,
280 const char *buf, size_t count)
281{
282 struct iwmct_priv *priv = dev_get_drvdata(d);
283 struct top_msg cmd;
284 char *token, *str_buf = NULL;
285 ssize_t ret = count;
286 u16 cmdlen = 0;
287 int i;
288 long val;
289 u8 src, mask;
290
291 if (!count)
292 goto exit;
293
294 str_buf = kzalloc(count, GFP_KERNEL);
295 if (!str_buf) {
296 LOG_ERROR(priv, DEBUGFS,
297 "failed to allocate %zd bytes\n", count);
298 ret = -ENOMEM;
299 goto exit;
300 }
301
302 memcpy(str_buf, buf, count);
303
304 cmd.hdr.type = COMM_TYPE_H2D;
305 cmd.hdr.category = COMM_CATEGORY_DEBUG;
306 cmd.hdr.opcode = CMD_DBG_LOG_LEVEL;
307
308 for (i = 0; ((token = strsep(&str_buf, ",")) != NULL) &&
309 (i < FW_LOG_SRC_MAX); i++) {
310
311 while (isspace(*token))
312 ++token;
313
314 if (strict_strtol(token, HEXADECIMAL_RADIX, &val)) {
315 LOG_ERROR(priv, DEBUGFS,
316 "failed to convert string to long %s\n",
317 token);
318 ret = -EINVAL;
319 goto exit;
320 }
321
322 mask = val & 0xFF; /* LSB */
323 src = (val & 0XFF00) >> 8; /* 2nd least significant byte. */
324 iwmct_log_set_fw_filter(src, mask);
325
326 cmd.u.logdefs[i].logsource = src;
327 cmd.u.logdefs[i].sevmask = mask;
328 }
329
330 cmd.hdr.length = cpu_to_le16(i * sizeof(cmd.u.logdefs[0]));
331 cmdlen = (i * sizeof(cmd.u.logdefs[0]) + sizeof(cmd.hdr));
332
333 ret = iwmct_send_hcmd(priv, (u8 *)&cmd, cmdlen);
334 if (ret) {
335 LOG_ERROR(priv, DEBUGFS,
336 "Failed to send %d bytes of fwcmd, ret=%zd\n",
337 cmdlen, ret);
338 goto exit;
339 } else
340 LOG_INFO(priv, DEBUGFS, "fwcmd sent (%d bytes)\n", cmdlen);
341
342 ret = count;
343
344exit:
345 kfree(str_buf);
346 return ret;
347}
348
diff --git a/drivers/misc/iwmc3200top/log.h b/drivers/misc/iwmc3200top/log.h
new file mode 100644
index 00000000000..4434bb16cea
--- /dev/null
+++ b/drivers/misc/iwmc3200top/log.h
@@ -0,0 +1,171 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/log.h
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#ifndef __LOG_H__
28#define __LOG_H__
29
30
31/* log severity:
32 * The log levels here match FW log levels
33 * so values need to stay as is */
34#define LOG_SEV_CRITICAL 0
35#define LOG_SEV_ERROR 1
36#define LOG_SEV_WARNING 2
37#define LOG_SEV_INFO 3
38#define LOG_SEV_INFOEX 4
39
40/* Log levels not defined for FW */
41#define LOG_SEV_TRACE 5
42#define LOG_SEV_DUMP 6
43
44#define LOG_SEV_FW_FILTER_ALL \
45 (BIT(LOG_SEV_CRITICAL) | \
46 BIT(LOG_SEV_ERROR) | \
47 BIT(LOG_SEV_WARNING) | \
48 BIT(LOG_SEV_INFO) | \
49 BIT(LOG_SEV_INFOEX))
50
51#define LOG_SEV_FILTER_ALL \
52 (BIT(LOG_SEV_CRITICAL) | \
53 BIT(LOG_SEV_ERROR) | \
54 BIT(LOG_SEV_WARNING) | \
55 BIT(LOG_SEV_INFO) | \
56 BIT(LOG_SEV_INFOEX) | \
57 BIT(LOG_SEV_TRACE) | \
58 BIT(LOG_SEV_DUMP))
59
60/* log source */
61#define LOG_SRC_INIT 0
62#define LOG_SRC_DEBUGFS 1
63#define LOG_SRC_FW_DOWNLOAD 2
64#define LOG_SRC_FW_MSG 3
65#define LOG_SRC_TST 4
66#define LOG_SRC_IRQ 5
67
68#define LOG_SRC_MAX 6
69#define LOG_SRC_ALL 0xFF
70
71/**
72 * Default intitialization runtime log level
73 */
74#ifndef LOG_SEV_FILTER_RUNTIME
75#define LOG_SEV_FILTER_RUNTIME \
76 (BIT(LOG_SEV_CRITICAL) | \
77 BIT(LOG_SEV_ERROR) | \
78 BIT(LOG_SEV_WARNING))
79#endif
80
81#ifndef FW_LOG_SEV_FILTER_RUNTIME
82#define FW_LOG_SEV_FILTER_RUNTIME LOG_SEV_FILTER_ALL
83#endif
84
85#ifdef CONFIG_IWMC3200TOP_DEBUG
86/**
87 * Log macros
88 */
89
90#define priv2dev(priv) (&(priv->func)->dev)
91
92#define LOG_CRITICAL(priv, src, fmt, args...) \
93do { \
94 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_CRITICAL)) \
95 dev_crit(priv2dev(priv), "%s %d: " fmt, \
96 __func__, __LINE__, ##args); \
97} while (0)
98
99#define LOG_ERROR(priv, src, fmt, args...) \
100do { \
101 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_ERROR)) \
102 dev_err(priv2dev(priv), "%s %d: " fmt, \
103 __func__, __LINE__, ##args); \
104} while (0)
105
106#define LOG_WARNING(priv, src, fmt, args...) \
107do { \
108 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_WARNING)) \
109 dev_warn(priv2dev(priv), "%s %d: " fmt, \
110 __func__, __LINE__, ##args); \
111} while (0)
112
113#define LOG_INFO(priv, src, fmt, args...) \
114do { \
115 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_INFO)) \
116 dev_info(priv2dev(priv), "%s %d: " fmt, \
117 __func__, __LINE__, ##args); \
118} while (0)
119
120#define LOG_TRACE(priv, src, fmt, args...) \
121do { \
122 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_TRACE)) \
123 dev_dbg(priv2dev(priv), "%s %d: " fmt, \
124 __func__, __LINE__, ##args); \
125} while (0)
126
127#define LOG_HEXDUMP(src, ptr, len) \
128do { \
129 if (iwmct_logdefs[LOG_SRC_ ## src] & BIT(LOG_SEV_DUMP)) \
130 print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, \
131 16, 1, ptr, len, false); \
132} while (0)
133
134void iwmct_log_top_message(struct iwmct_priv *priv, u8 *buf, int len);
135
136extern u8 iwmct_logdefs[];
137
138int iwmct_log_set_filter(u8 src, u8 logmask);
139int iwmct_log_set_fw_filter(u8 src, u8 logmask);
140
141ssize_t show_iwmct_log_level(struct device *d,
142 struct device_attribute *attr, char *buf);
143ssize_t store_iwmct_log_level(struct device *d,
144 struct device_attribute *attr,
145 const char *buf, size_t count);
146ssize_t show_iwmct_log_level_fw(struct device *d,
147 struct device_attribute *attr, char *buf);
148ssize_t store_iwmct_log_level_fw(struct device *d,
149 struct device_attribute *attr,
150 const char *buf, size_t count);
151
152#else
153
154#define LOG_CRITICAL(priv, src, fmt, args...)
155#define LOG_ERROR(priv, src, fmt, args...)
156#define LOG_WARNING(priv, src, fmt, args...)
157#define LOG_INFO(priv, src, fmt, args...)
158#define LOG_TRACE(priv, src, fmt, args...)
159#define LOG_HEXDUMP(src, ptr, len)
160
161static inline void iwmct_log_top_message(struct iwmct_priv *priv,
162 u8 *buf, int len) {}
163static inline int iwmct_log_set_filter(u8 src, u8 logmask) { return 0; }
164static inline int iwmct_log_set_fw_filter(u8 src, u8 logmask) { return 0; }
165
166#endif /* CONFIG_IWMC3200TOP_DEBUG */
167
168int log_get_filter_str(char *buf, int size);
169int log_get_fw_filter_str(char *buf, int size);
170
171#endif /* __LOG_H__ */
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c
new file mode 100644
index 00000000000..b1f4563be9a
--- /dev/null
+++ b/drivers/misc/iwmc3200top/main.c
@@ -0,0 +1,662 @@
1/*
2 * iwmc3200top - Intel Wireless MultiCom 3200 Top Driver
3 * drivers/misc/iwmc3200top/main.c
4 *
5 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version
9 * 2 as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
20 *
21 *
22 * Author Name: Maxim Grabarnik <maxim.grabarnink@intel.com>
23 * -
24 *
25 */
26
27#include <linux/module.h>
28#include <linux/slab.h>
29#include <linux/init.h>
30#include <linux/kernel.h>
31#include <linux/debugfs.h>
32#include <linux/mmc/sdio_ids.h>
33#include <linux/mmc/sdio_func.h>
34#include <linux/mmc/sdio.h>
35
36#include "iwmc3200top.h"
37#include "log.h"
38#include "fw-msg.h"
39#include "debugfs.h"
40
41
42#define DRIVER_DESCRIPTION "Intel(R) IWMC 3200 Top Driver"
43#define DRIVER_COPYRIGHT "Copyright (c) 2008 Intel Corporation."
44
45#define DRIVER_VERSION "0.1.62"
46
47MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
48MODULE_VERSION(DRIVER_VERSION);
49MODULE_LICENSE("GPL");
50MODULE_AUTHOR(DRIVER_COPYRIGHT);
51MODULE_FIRMWARE(FW_NAME(FW_API_VER));
52
53
54static inline int __iwmct_tx(struct iwmct_priv *priv, void *src, int count)
55{
56 return sdio_memcpy_toio(priv->func, IWMC_SDIO_DATA_ADDR, src, count);
57
58}
59int iwmct_tx(struct iwmct_priv *priv, void *src, int count)
60{
61 int ret;
62 sdio_claim_host(priv->func);
63 ret = __iwmct_tx(priv, src, count);
64 sdio_release_host(priv->func);
65 return ret;
66}
67/*
68 * This workers main task is to wait for OP_OPR_ALIVE
69 * from TOP FW until ALIVE_MSG_TIMOUT timeout is elapsed.
70 * When OP_OPR_ALIVE received it will issue
71 * a call to "bus_rescan_devices".
72 */
73static void iwmct_rescan_worker(struct work_struct *ws)
74{
75 struct iwmct_priv *priv;
76 int ret;
77
78 priv = container_of(ws, struct iwmct_priv, bus_rescan_worker);
79
80 LOG_INFO(priv, FW_MSG, "Calling bus_rescan\n");
81
82 ret = bus_rescan_devices(priv->func->dev.bus);
83 if (ret < 0)
84 LOG_INFO(priv, INIT, "bus_rescan_devices FAILED!!!\n");
85}
86
87static void op_top_message(struct iwmct_priv *priv, struct top_msg *msg)
88{
89 switch (msg->hdr.opcode) {
90 case OP_OPR_ALIVE:
91 LOG_INFO(priv, FW_MSG, "Got ALIVE from device, wake rescan\n");
92 schedule_work(&priv->bus_rescan_worker);
93 break;
94 default:
95 LOG_INFO(priv, FW_MSG, "Received msg opcode 0x%X\n",
96 msg->hdr.opcode);
97 break;
98 }
99}
100
101
102static void handle_top_message(struct iwmct_priv *priv, u8 *buf, int len)
103{
104 struct top_msg *msg;
105
106 msg = (struct top_msg *)buf;
107
108 if (msg->hdr.type != COMM_TYPE_D2H) {
109 LOG_ERROR(priv, FW_MSG,
110 "Message from TOP with invalid message type 0x%X\n",
111 msg->hdr.type);
112 return;
113 }
114
115 if (len < sizeof(msg->hdr)) {
116 LOG_ERROR(priv, FW_MSG,
117 "Message from TOP is too short for message header "
118 "received %d bytes, expected at least %zd bytes\n",
119 len, sizeof(msg->hdr));
120 return;
121 }
122
123 if (len < le16_to_cpu(msg->hdr.length) + sizeof(msg->hdr)) {
124 LOG_ERROR(priv, FW_MSG,
125 "Message length (%d bytes) is shorter than "
126 "in header (%d bytes)\n",
127 len, le16_to_cpu(msg->hdr.length));
128 return;
129 }
130
131 switch (msg->hdr.category) {
132 case COMM_CATEGORY_OPERATIONAL:
133 op_top_message(priv, (struct top_msg *)buf);
134 break;
135
136 case COMM_CATEGORY_DEBUG:
137 case COMM_CATEGORY_TESTABILITY:
138 case COMM_CATEGORY_DIAGNOSTICS:
139 iwmct_log_top_message(priv, buf, len);
140 break;
141
142 default:
143 LOG_ERROR(priv, FW_MSG,
144 "Message from TOP with unknown category 0x%X\n",
145 msg->hdr.category);
146 break;
147 }
148}
149
150int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len)
151{
152 int ret;
153 u8 *buf;
154
155 LOG_TRACE(priv, FW_MSG, "Sending hcmd:\n");
156
157 /* add padding to 256 for IWMC */
158 ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256;
159
160 LOG_HEXDUMP(FW_MSG, cmd, len);
161
162 if (len > FW_HCMD_BLOCK_SIZE) {
163 LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n",
164 len, FW_HCMD_BLOCK_SIZE);
165 return -1;
166 }
167
168 buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL);
169 if (!buf) {
170 LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n",
171 FW_HCMD_BLOCK_SIZE);
172 return -1;
173 }
174
175 memcpy(buf, cmd, len);
176 ret = iwmct_tx(priv, buf, FW_HCMD_BLOCK_SIZE);
177
178 kfree(buf);
179 return ret;
180}
181
182
183static void iwmct_irq_read_worker(struct work_struct *ws)
184{
185 struct iwmct_priv *priv;
186 struct iwmct_work_struct *read_req;
187 __le32 *buf = NULL;
188 int ret;
189 int iosize;
190 u32 barker;
191 bool is_barker;
192
193 priv = container_of(ws, struct iwmct_priv, isr_worker);
194
195 LOG_TRACE(priv, IRQ, "enter iwmct_irq_read_worker %p\n", ws);
196
197 /* --------------------- Handshake with device -------------------- */
198 sdio_claim_host(priv->func);
199
200 /* all list manipulations have to be protected by
201 * sdio_claim_host/sdio_release_host */
202 if (list_empty(&priv->read_req_list)) {
203 LOG_ERROR(priv, IRQ, "read_req_list empty in read worker\n");
204 goto exit_release;
205 }
206
207 read_req = list_entry(priv->read_req_list.next,
208 struct iwmct_work_struct, list);
209
210 list_del(&read_req->list);
211 iosize = read_req->iosize;
212 kfree(read_req);
213
214 buf = kzalloc(iosize, GFP_KERNEL);
215 if (!buf) {
216 LOG_ERROR(priv, IRQ, "kzalloc error, buf size %d\n", iosize);
217 goto exit_release;
218 }
219
220 LOG_INFO(priv, IRQ, "iosize=%d, buf=%p, func=%d\n",
221 iosize, buf, priv->func->num);
222
223 /* read from device */
224 ret = sdio_memcpy_fromio(priv->func, buf, IWMC_SDIO_DATA_ADDR, iosize);
225 if (ret) {
226 LOG_ERROR(priv, IRQ, "error %d reading buffer\n", ret);
227 goto exit_release;
228 }
229
230 LOG_HEXDUMP(IRQ, (u8 *)buf, iosize);
231
232 barker = le32_to_cpu(buf[0]);
233
234 /* Verify whether it's a barker and if not - treat as regular Rx */
235 if (barker == IWMC_BARKER_ACK ||
236 (barker & BARKER_DNLOAD_BARKER_MSK) == IWMC_BARKER_REBOOT) {
237
238 /* Valid Barker is equal on first 4 dwords */
239 is_barker = (buf[1] == buf[0]) &&
240 (buf[2] == buf[0]) &&
241 (buf[3] == buf[0]);
242
243 if (!is_barker) {
244 LOG_WARNING(priv, IRQ,
245 "Potentially inconsistent barker "
246 "%08X_%08X_%08X_%08X\n",
247 le32_to_cpu(buf[0]), le32_to_cpu(buf[1]),
248 le32_to_cpu(buf[2]), le32_to_cpu(buf[3]));
249 }
250 } else {
251 is_barker = false;
252 }
253
254 /* Handle Top CommHub message */
255 if (!is_barker) {
256 sdio_release_host(priv->func);
257 handle_top_message(priv, (u8 *)buf, iosize);
258 goto exit;
259 } else if (barker == IWMC_BARKER_ACK) { /* Handle barkers */
260 if (atomic_read(&priv->dev_sync) == 0) {
261 LOG_ERROR(priv, IRQ,
262 "ACK barker arrived out-of-sync\n");
263 goto exit_release;
264 }
265
266 /* Continuing to FW download (after Sync is completed)*/
267 atomic_set(&priv->dev_sync, 0);
268 LOG_INFO(priv, IRQ, "ACK barker arrived "
269 "- starting FW download\n");
270 } else { /* REBOOT barker */
271 LOG_INFO(priv, IRQ, "Received reboot barker: %x\n", barker);
272 priv->barker = barker;
273
274 if (barker & BARKER_DNLOAD_SYNC_MSK) {
275 /* Send the same barker back */
276 ret = __iwmct_tx(priv, buf, iosize);
277 if (ret) {
278 LOG_ERROR(priv, IRQ,
279 "error %d echoing barker\n", ret);
280 goto exit_release;
281 }
282 LOG_INFO(priv, IRQ, "Echoing barker to device\n");
283 atomic_set(&priv->dev_sync, 1);
284 goto exit_release;
285 }
286
287 /* Continuing to FW download (without Sync) */
288 LOG_INFO(priv, IRQ, "No sync requested "
289 "- starting FW download\n");
290 }
291
292 sdio_release_host(priv->func);
293
294 if (priv->dbg.fw_download)
295 iwmct_fw_load(priv);
296 else
297 LOG_ERROR(priv, IRQ, "FW download not allowed\n");
298
299 goto exit;
300
301exit_release:
302 sdio_release_host(priv->func);
303exit:
304 kfree(buf);
305 LOG_TRACE(priv, IRQ, "exit iwmct_irq_read_worker\n");
306}
307
308static void iwmct_irq(struct sdio_func *func)
309{
310 struct iwmct_priv *priv;
311 int val, ret;
312 int iosize;
313 int addr = IWMC_SDIO_INTR_GET_SIZE_ADDR;
314 struct iwmct_work_struct *read_req;
315
316 priv = sdio_get_drvdata(func);
317
318 LOG_TRACE(priv, IRQ, "enter iwmct_irq\n");
319
320 /* read the function's status register */
321 val = sdio_readb(func, IWMC_SDIO_INTR_STATUS_ADDR, &ret);
322
323 LOG_TRACE(priv, IRQ, "iir value = %d, ret=%d\n", val, ret);
324
325 if (!val) {
326 LOG_ERROR(priv, IRQ, "iir = 0, exiting ISR\n");
327 goto exit_clear_intr;
328 }
329
330
331 /*
332 * read 2 bytes of the transaction size
333 * IMPORTANT: sdio transaction size has to be read before clearing
334 * sdio interrupt!!!
335 */
336 val = sdio_readb(priv->func, addr++, &ret);
337 iosize = val;
338 val = sdio_readb(priv->func, addr++, &ret);
339 iosize += val << 8;
340
341 LOG_INFO(priv, IRQ, "READ size %d\n", iosize);
342
343 if (iosize == 0) {
344 LOG_ERROR(priv, IRQ, "READ size %d, exiting ISR\n", iosize);
345 goto exit_clear_intr;
346 }
347
348 /* allocate a work structure to pass iosize to the worker */
349 read_req = kzalloc(sizeof(struct iwmct_work_struct), GFP_KERNEL);
350 if (!read_req) {
351 LOG_ERROR(priv, IRQ, "failed to allocate read_req, exit ISR\n");
352 goto exit_clear_intr;
353 }
354
355 INIT_LIST_HEAD(&read_req->list);
356 read_req->iosize = iosize;
357
358 list_add_tail(&priv->read_req_list, &read_req->list);
359
360 /* clear the function's interrupt request bit (write 1 to clear) */
361 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
362
363 schedule_work(&priv->isr_worker);
364
365 LOG_TRACE(priv, IRQ, "exit iwmct_irq\n");
366
367 return;
368
369exit_clear_intr:
370 /* clear the function's interrupt request bit (write 1 to clear) */
371 sdio_writeb(func, 1, IWMC_SDIO_INTR_CLEAR_ADDR, &ret);
372}
373
374
375static int blocks;
376module_param(blocks, int, 0604);
377MODULE_PARM_DESC(blocks, "max_blocks_to_send");
378
379static int dump;
380module_param(dump, bool, 0604);
381MODULE_PARM_DESC(dump, "dump_hex_content");
382
383static int jump = 1;
384module_param(jump, bool, 0604);
385
386static int direct = 1;
387module_param(direct, bool, 0604);
388
389static int checksum = 1;
390module_param(checksum, bool, 0604);
391
392static int fw_download = 1;
393module_param(fw_download, bool, 0604);
394
395static int block_size = IWMC_SDIO_BLK_SIZE;
396module_param(block_size, int, 0404);
397
398static int download_trans_blks = IWMC_DEFAULT_TR_BLK;
399module_param(download_trans_blks, int, 0604);
400
401static int rubbish_barker;
402module_param(rubbish_barker, bool, 0604);
403
404#ifdef CONFIG_IWMC3200TOP_DEBUG
405static int log_level[LOG_SRC_MAX];
406static unsigned int log_level_argc;
407module_param_array(log_level, int, &log_level_argc, 0604);
408MODULE_PARM_DESC(log_level, "log_level");
409
410static int log_level_fw[FW_LOG_SRC_MAX];
411static unsigned int log_level_fw_argc;
412module_param_array(log_level_fw, int, &log_level_fw_argc, 0604);
413MODULE_PARM_DESC(log_level_fw, "log_level_fw");
414#endif
415
416void iwmct_dbg_init_params(struct iwmct_priv *priv)
417{
418#ifdef CONFIG_IWMC3200TOP_DEBUG
419 int i;
420
421 for (i = 0; i < log_level_argc; i++) {
422 dev_notice(&priv->func->dev, "log_level[%d]=0x%X\n",
423 i, log_level[i]);
424 iwmct_log_set_filter((log_level[i] >> 8) & 0xFF,
425 log_level[i] & 0xFF);
426 }
427 for (i = 0; i < log_level_fw_argc; i++) {
428 dev_notice(&priv->func->dev, "log_level_fw[%d]=0x%X\n",
429 i, log_level_fw[i]);
430 iwmct_log_set_fw_filter((log_level_fw[i] >> 8) & 0xFF,
431 log_level_fw[i] & 0xFF);
432 }
433#endif
434
435 priv->dbg.blocks = blocks;
436 LOG_INFO(priv, INIT, "blocks=%d\n", blocks);
437 priv->dbg.dump = (bool)dump;
438 LOG_INFO(priv, INIT, "dump=%d\n", dump);
439 priv->dbg.jump = (bool)jump;
440 LOG_INFO(priv, INIT, "jump=%d\n", jump);
441 priv->dbg.direct = (bool)direct;
442 LOG_INFO(priv, INIT, "direct=%d\n", direct);
443 priv->dbg.checksum = (bool)checksum;
444 LOG_INFO(priv, INIT, "checksum=%d\n", checksum);
445 priv->dbg.fw_download = (bool)fw_download;
446 LOG_INFO(priv, INIT, "fw_download=%d\n", fw_download);
447 priv->dbg.block_size = block_size;
448 LOG_INFO(priv, INIT, "block_size=%d\n", block_size);
449 priv->dbg.download_trans_blks = download_trans_blks;
450 LOG_INFO(priv, INIT, "download_trans_blks=%d\n", download_trans_blks);
451}
452
453/*****************************************************************************
454 *
455 * sysfs attributes
456 *
457 *****************************************************************************/
458static ssize_t show_iwmct_fw_version(struct device *d,
459 struct device_attribute *attr, char *buf)
460{
461 struct iwmct_priv *priv = dev_get_drvdata(d);
462 return sprintf(buf, "%s\n", priv->dbg.label_fw);
463}
464static DEVICE_ATTR(cc_label_fw, S_IRUGO, show_iwmct_fw_version, NULL);
465
466#ifdef CONFIG_IWMC3200TOP_DEBUG
467static DEVICE_ATTR(log_level, S_IWUSR | S_IRUGO,
468 show_iwmct_log_level, store_iwmct_log_level);
469static DEVICE_ATTR(log_level_fw, S_IWUSR | S_IRUGO,
470 show_iwmct_log_level_fw, store_iwmct_log_level_fw);
471#endif
472
473static struct attribute *iwmct_sysfs_entries[] = {
474 &dev_attr_cc_label_fw.attr,
475#ifdef CONFIG_IWMC3200TOP_DEBUG
476 &dev_attr_log_level.attr,
477 &dev_attr_log_level_fw.attr,
478#endif
479 NULL
480};
481
482static struct attribute_group iwmct_attribute_group = {
483 .name = NULL, /* put in device directory */
484 .attrs = iwmct_sysfs_entries,
485};
486
487
488static int iwmct_probe(struct sdio_func *func,
489 const struct sdio_device_id *id)
490{
491 struct iwmct_priv *priv;
492 int ret;
493 int val = 1;
494 int addr = IWMC_SDIO_INTR_ENABLE_ADDR;
495
496 dev_dbg(&func->dev, "enter iwmct_probe\n");
497
498 dev_dbg(&func->dev, "IRQ polling period id %u msecs, HZ is %d\n",
499 jiffies_to_msecs(2147483647), HZ);
500
501 priv = kzalloc(sizeof(struct iwmct_priv), GFP_KERNEL);
502 if (!priv) {
503 dev_err(&func->dev, "kzalloc error\n");
504 return -ENOMEM;
505 }
506 priv->func = func;
507 sdio_set_drvdata(func, priv);
508
509 INIT_WORK(&priv->bus_rescan_worker, iwmct_rescan_worker);
510 INIT_WORK(&priv->isr_worker, iwmct_irq_read_worker);
511
512 init_waitqueue_head(&priv->wait_q);
513
514 sdio_claim_host(func);
515 /* FIXME: Remove after it is fixed in the Boot ROM upgrade */
516 func->enable_timeout = 10;
517
518 /* In our HW, setting the block size also wakes up the boot rom. */
519 ret = sdio_set_block_size(func, priv->dbg.block_size);
520 if (ret) {
521 LOG_ERROR(priv, INIT,
522 "sdio_set_block_size() failure: %d\n", ret);
523 goto error_sdio_enable;
524 }
525
526 ret = sdio_enable_func(func);
527 if (ret) {
528 LOG_ERROR(priv, INIT, "sdio_enable_func() failure: %d\n", ret);
529 goto error_sdio_enable;
530 }
531
532 /* init reset and dev_sync states */
533 atomic_set(&priv->reset, 0);
534 atomic_set(&priv->dev_sync, 0);
535
536 /* init read req queue */
537 INIT_LIST_HEAD(&priv->read_req_list);
538
539 /* process configurable parameters */
540 iwmct_dbg_init_params(priv);
541 ret = sysfs_create_group(&func->dev.kobj, &iwmct_attribute_group);
542 if (ret) {
543 LOG_ERROR(priv, INIT, "Failed to register attributes and "
544 "initialize module_params\n");
545 goto error_dev_attrs;
546 }
547
548 iwmct_dbgfs_register(priv, DRV_NAME);
549
550 if (!priv->dbg.direct && priv->dbg.download_trans_blks > 8) {
551 LOG_INFO(priv, INIT,
552 "Reducing transaction to 8 blocks = 2K (from %d)\n",
553 priv->dbg.download_trans_blks);
554 priv->dbg.download_trans_blks = 8;
555 }
556 priv->trans_len = priv->dbg.download_trans_blks * priv->dbg.block_size;
557 LOG_INFO(priv, INIT, "Transaction length = %d\n", priv->trans_len);
558
559 ret = sdio_claim_irq(func, iwmct_irq);
560 if (ret) {
561 LOG_ERROR(priv, INIT, "sdio_claim_irq() failure: %d\n", ret);
562 goto error_claim_irq;
563 }
564
565
566 /* Enable function's interrupt */
567 sdio_writeb(priv->func, val, addr, &ret);
568 if (ret) {
569 LOG_ERROR(priv, INIT, "Failure writing to "
570 "Interrupt Enable Register (%d): %d\n", addr, ret);
571 goto error_enable_int;
572 }
573
574 sdio_release_host(func);
575
576 LOG_INFO(priv, INIT, "exit iwmct_probe\n");
577
578 return ret;
579
580error_enable_int:
581 sdio_release_irq(func);
582error_claim_irq:
583 sdio_disable_func(func);
584error_dev_attrs:
585 iwmct_dbgfs_unregister(priv->dbgfs);
586 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
587error_sdio_enable:
588 sdio_release_host(func);
589 return ret;
590}
591
592static void iwmct_remove(struct sdio_func *func)
593{
594 struct iwmct_work_struct *read_req;
595 struct iwmct_priv *priv = sdio_get_drvdata(func);
596
597 LOG_INFO(priv, INIT, "enter\n");
598
599 sdio_claim_host(func);
600 sdio_release_irq(func);
601 sdio_release_host(func);
602
603 /* Make sure works are finished */
604 flush_work_sync(&priv->bus_rescan_worker);
605 flush_work_sync(&priv->isr_worker);
606
607 sdio_claim_host(func);
608 sdio_disable_func(func);
609 sysfs_remove_group(&func->dev.kobj, &iwmct_attribute_group);
610 iwmct_dbgfs_unregister(priv->dbgfs);
611 sdio_release_host(func);
612
613 /* free read requests */
614 while (!list_empty(&priv->read_req_list)) {
615 read_req = list_entry(priv->read_req_list.next,
616 struct iwmct_work_struct, list);
617
618 list_del(&read_req->list);
619 kfree(read_req);
620 }
621
622 kfree(priv);
623}
624
625
626static const struct sdio_device_id iwmct_ids[] = {
627 /* Intel Wireless MultiCom 3200 Top Driver */
628 { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1404)},
629 { }, /* Terminating entry */
630};
631
632MODULE_DEVICE_TABLE(sdio, iwmct_ids);
633
634static struct sdio_driver iwmct_driver = {
635 .probe = iwmct_probe,
636 .remove = iwmct_remove,
637 .name = DRV_NAME,
638 .id_table = iwmct_ids,
639};
640
641static int __init iwmct_init(void)
642{
643 int rc;
644
645 /* Default log filter settings */
646 iwmct_log_set_filter(LOG_SRC_ALL, LOG_SEV_FILTER_RUNTIME);
647 iwmct_log_set_filter(LOG_SRC_FW_MSG, LOG_SEV_FW_FILTER_ALL);
648 iwmct_log_set_fw_filter(LOG_SRC_ALL, FW_LOG_SEV_FILTER_RUNTIME);
649
650 rc = sdio_register_driver(&iwmct_driver);
651
652 return rc;
653}
654
655static void __exit iwmct_exit(void)
656{
657 sdio_unregister_driver(&iwmct_driver);
658}
659
660module_init(iwmct_init);
661module_exit(iwmct_exit);
662
diff --git a/drivers/misc/max1749.c b/drivers/misc/max1749.c
new file mode 100644
index 00000000000..e9896428900
--- /dev/null
+++ b/drivers/misc/max1749.c
@@ -0,0 +1,118 @@
1/*
2 * drivers/misc/max1749.c
3 *
4 * Driver for MAX1749, vibrator motor driver.
5 *
6 * Copyright (c) 2011, NVIDIA Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#include <linux/regulator/consumer.h>
24#include <linux/kernel.h>
25#include <linux/platform_device.h>
26#include <linux/err.h>
27#include <linux/hrtimer.h>
28#include <linux/delay.h>
29
30#include "../staging/android/timed_output.h"
31
32static struct regulator *regulator;
33static int timeout;
34
35static void vibrator_start(void)
36{
37 regulator_enable(regulator);
38}
39
40static void vibrator_stop(void)
41{
42 int ret;
43
44 ret = regulator_is_enabled(regulator);
45 if (ret > 0)
46 regulator_disable(regulator);
47}
48
49/*
50 * Timeout value can be changed from sysfs entry
51 * created by timed_output_dev.
52 * echo 100 > /sys/class/timed_output/vibrator/enable
53 */
54static void vibrator_enable(struct timed_output_dev *dev, int value)
55{
56 timeout = value;
57 if (!regulator)
58 return;
59
60 if (value) {
61 vibrator_start();
62 msleep(value);
63 vibrator_stop();
64 } else {
65 vibrator_stop();
66 }
67}
68
69/*
70 * Timeout value can be read from sysfs entry
71 * created by timed_output_dev.
72 * cat /sys/class/timed_output/vibrator/enable
73 */
74static int vibrator_get_time(struct timed_output_dev *dev)
75{
76 return timeout;
77}
78
79static struct timed_output_dev vibrator_dev = {
80 .name = "vibrator",
81 .get_time = vibrator_get_time,
82 .enable = vibrator_enable,
83};
84
85static int __init vibrator_init(void)
86{
87 int status;
88
89 regulator = regulator_get(NULL, "vdd_vbrtr");
90 if (IS_ERR_OR_NULL(regulator)) {
91 pr_err("vibrator_init:Couldn't get regulator vdd_vbrtr\n");
92 regulator = NULL;
93 return PTR_ERR(regulator);
94 }
95
96 status = timed_output_dev_register(&vibrator_dev);
97
98 if (status) {
99 regulator_put(regulator);
100 regulator = NULL;
101 }
102 return status;
103}
104
105static void __exit vibrator_exit(void)
106{
107 if (regulator) {
108 timed_output_dev_unregister(&vibrator_dev);
109 regulator_put(regulator);
110 regulator = NULL;
111 }
112}
113
114MODULE_DESCRIPTION("timed output vibrator device");
115MODULE_AUTHOR("GPL");
116
117module_init(vibrator_init);
118module_exit(vibrator_exit);
diff --git a/drivers/misc/mpu3050/Kconfig b/drivers/misc/mpu3050/Kconfig
new file mode 100644
index 00000000000..de240fa0ad8
--- /dev/null
+++ b/drivers/misc/mpu3050/Kconfig
@@ -0,0 +1,65 @@
1
2menu "Motion Sensors Support"
3
4choice
5 tristate "Motion Processing Unit"
6 depends on I2C
7 optional
8
9config MPU_SENSORS_MPU3050
10 tristate "MPU3050"
11 help
12 If you say yes here you get support for the MPU3050 Gyroscope driver
13 This driver can also be built as a module. If so, the module
14 will be called mpu3050.
15
16config MPU_SENSORS_MPU6000
17 tristate "MPU6000"
18 help
19 If you say yes here you get support for the MPU6000 Gyroscope driver
20 This driver can also be built as a module. If so, the module
21 will be called mpu6000.
22
23endchoice
24
25choice
26 prompt "Accelerometer Type"
27 depends on MPU_SENSORS_MPU3050
28 optional
29
30config MPU_SENSORS_KXTF9
31 bool "Kionix KXTF9"
32 help
33 This enables support for the Kionix KXFT9 accelerometer
34
35endchoice
36
37choice
38 prompt "Compass Type"
39 depends on MPU_SENSORS_MPU6000 || MPU_SENSORS_MPU3050
40 optional
41
42config MPU_SENSORS_AK8975
43 bool "AKM ak8975"
44 help
45 This enables support for the AKM ak8975 compass
46
47endchoice
48
49config MPU_SENSORS_TIMERIRQ
50 tristate "Timer IRQ"
51 help
52 If you say yes here you get access to the timerirq device handle which
53 can be used to select on. This can be used instead of IRQ's, sleeping,
54 or timer threads. Reading from this device returns the same type of
55 information as reading from the MPU and slave IRQ's.
56
57config MPU_SENSORS_DEBUG
58 bool "MPU debug"
59 depends on MPU_SENSORS_MPU3050 || MPU_SENSORS_MPU6000 || MPU_SENSORS_TIMERIRQ
60 help
61 If you say yes here you get extra debug messages from the MPU3050
62 and other slave sensors.
63
64endmenu
65
diff --git a/drivers/misc/mpu3050/Makefile b/drivers/misc/mpu3050/Makefile
new file mode 100644
index 00000000000..89ac46fdac5
--- /dev/null
+++ b/drivers/misc/mpu3050/Makefile
@@ -0,0 +1,132 @@
1
2# Kernel makefile for motions sensors
3#
4#
5
6# MPU
7obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o
8mpu3050-objs += mpuirq.o \
9 slaveirq.o \
10 mpu-dev.o \
11 mpu-i2c.o \
12 mlsl-kernel.o \
13 mlos-kernel.o \
14 $(MLLITE_DIR)mldl_cfg.o
15
16#
17# Accel options
18#
19ifdef CONFIG_MPU_SENSORS_ADXL346
20mpu3050-objs += $(MLLITE_DIR)accel/adxl346.o
21endif
22
23ifdef CONFIG_MPU_SENSORS_BMA150
24mpu3050-objs += $(MLLITE_DIR)accel/bma150.o
25endif
26
27ifdef CONFIG_MPU_SENSORS_BMA222
28mpu3050-objs += $(MLLITE_DIR)accel/bma222.o
29endif
30
31ifdef CONFIG_MPU_SENSORS_KXSD9
32mpu3050-objs += $(MLLITE_DIR)accel/kxsd9.o
33endif
34
35ifdef CONFIG_MPU_SENSORS_KXTF9
36mpu3050-objs += $(MLLITE_DIR)accel/kxtf9.o
37endif
38
39ifdef CONFIG_MPU_SENSORS_LIS331DLH
40mpu3050-objs += $(MLLITE_DIR)accel/lis331.o
41endif
42
43ifdef CONFIG_MPU_SENSORS_LIS3DH
44mpu3050-objs += $(MLLITE_DIR)accel/lis3dh.o
45endif
46
47ifdef CONFIG_MPU_SENSORS_LSM303DLHA
48mpu3050-objs += $(MLLITE_DIR)accel/lsm303a.o
49endif
50
51ifdef CONFIG_MPU_SENSORS_MMA8450
52mpu3050-objs += $(MLLITE_DIR)accel/mma8450.o
53endif
54
55ifdef CONFIG_MPU_SENSORS_MMA845X
56mpu3050-objs += $(MLLITE_DIR)accel/mma845x.o
57endif
58
59#
60# Compass options
61#
62ifdef CONFIG_MPU_SENSORS_AK8975
63mpu3050-objs += $(MLLITE_DIR)compass/ak8975.o
64endif
65
66ifdef CONFIG_MPU_SENSORS_AMI30X
67mpu3050-objs += $(MLLITE_DIR)compass/ami30x.o
68endif
69
70ifdef CONFIG_MPU_SENSORS_AMI306
71mpu3050-objs += $(MLLITE_DIR)compass/ami306.o
72endif
73
74ifdef CONFIG_MPU_SENSORS_HMC5883
75mpu3050-objs += $(MLLITE_DIR)compass/hmc5883.o
76endif
77
78ifdef CONFIG_MPU_SENSORS_LSM303DLHM
79mpu3050-objs += $(MLLITE_DIR)compass/lsm303m.o
80endif
81
82ifdef CONFIG_MPU_SENSORS_MMC314X
83mpu3050-objs += $(MLLITE_DIR)compass/mmc314x.o
84endif
85
86ifdef CONFIG_MPU_SENSORS_YAS529
87mpu3050-objs += $(MLLITE_DIR)compass/yas529-kernel.o
88endif
89
90ifdef CONFIG_MPU_SENSORS_YAS530
91mpu3050-objs += $(MLLITE_DIR)compass/yas530.o
92endif
93
94ifdef CONFIG_MPU_SENSORS_HSCDTD002B
95mpu3050-objs += $(MLLITE_DIR)compass/hscdtd002b.o
96endif
97
98ifdef CONFIG_MPU_SENSORS_HSCDTD004A
99mpu3050-objs += $(MLLITE_DIR)compass/hscdtd004a.o
100endif
101#
102# Pressure options
103#
104ifdef CONFIG_MPU_SENSORS_BMA085
105mpu3050-objs += $(MLLITE_DIR)pressure/bma085.o
106endif
107
108EXTRA_CFLAGS += -I$(M)/$(MLLITE_DIR) \
109 -I$(M)/../../include \
110 -Idrivers/misc/mpu3050 \
111 -Iinclude/linux
112
113obj-$(CONFIG_MPU_SENSORS_MPU6000)+= mpu6000.o
114mpu6000-objs += mpuirq.o \
115 slaveirq.o \
116 mpu-dev.o \
117 mpu-i2c.o \
118 mlsl-kernel.o \
119 mlos-kernel.o \
120 $(MLLITE_DIR)mldl_cfg.o \
121 $(MLLITE_DIR)accel/mantis.o
122
123ifdef CONFIG_MPU_SENSORS_AK8975
124mpu6000-objs += $(MLLITE_DIR)compass/ak8975.o
125endif
126
127ifdef CONFIG_MPU_SENSORS_MPU6000
128EXTRA_CFLAGS += -DM_HW
129endif
130
131obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o
132
diff --git a/drivers/misc/mpu3050/accel/kxtf9.c b/drivers/misc/mpu3050/accel/kxtf9.c
new file mode 100644
index 00000000000..938cd572a8f
--- /dev/null
+++ b/drivers/misc/mpu3050/accel/kxtf9.c
@@ -0,0 +1,669 @@
1/*
2 $License:
3 Copyright (C) 2010 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 - Accelerometer Driver Layer)
22 * @brief Provides the interface to setup and handle an accelerometers
23 * connected to the secondary I2C interface of the gyroscope.
24 *
25 * @{
26 * @file kxtf9.c
27 * @brief Accelerometer setup and handling methods.
28*/
29
30/* ------------------ */
31/* - Include Files. - */
32/* ------------------ */
33
34#undef MPL_LOG_NDEBUG
35#define MPL_LOG_NDEBUG 1
36
37#ifdef __KERNEL__
38#include <linux/module.h>
39#endif
40
41#include "mpu.h"
42#include "mlsl.h"
43#include "mlos.h"
44
45#include <log.h>
46#undef MPL_LOG_TAG
47#define MPL_LOG_TAG "MPL-acc"
48
49#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
50#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
51#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
52#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
53#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
54#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
55#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
56#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
57#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
58#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
59#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
60#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
61#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
62#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
63#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
64#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
65#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
66#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
67#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
68#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
69#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
70#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
71#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
72#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
73#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
74#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
75#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
76#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
77#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
78#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
79#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
80#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
81#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
82#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
83#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
84#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
85#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
86#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
87#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
88
89#define KXTF9_MAX_DUR (0xFF)
90#define KXTF9_MAX_THS (0xFF)
91#define KXTF9_THS_COUNTS_P_G (32)
92
93/* --------------------- */
94/* - Variables. - */
95/* --------------------- */
96
97struct kxtf9_config {
98 unsigned int odr; /* Output data rate mHz */
99 unsigned int fsr; /* full scale range mg */
100 unsigned int ths; /* Motion no-motion thseshold mg */
101 unsigned int dur; /* Motion no-motion duration ms */
102 unsigned int irq_type;
103 unsigned char reg_ths;
104 unsigned char reg_dur;
105 unsigned char reg_odr;
106 unsigned char reg_int_cfg1;
107 unsigned char reg_int_cfg2;
108 unsigned char ctrl_reg1;
109};
110
111struct kxtf9_private_data {
112 struct kxtf9_config suspend;
113 struct kxtf9_config resume;
114};
115
116/*****************************************
117 Accelerometer Initialization Functions
118*****************************************/
119
120static int kxtf9_set_ths(void *mlsl_handle,
121 struct ext_slave_platform_data *pdata,
122 struct kxtf9_config *config,
123 int apply,
124 long ths)
125{
126 int result = ML_SUCCESS;
127 if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS)
128 ths = (KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G;
129
130 if (ths < 0)
131 ths = 0;
132
133 config->ths = ths;
134 config->reg_ths = (unsigned char)
135 ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000);
136 MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
137 if (apply)
138 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
139 KXTF9_WUF_THRESH,
140 config->reg_ths);
141 return result;
142}
143
144static int kxtf9_set_dur(void *mlsl_handle,
145 struct ext_slave_platform_data *pdata,
146 struct kxtf9_config *config,
147 int apply,
148 long dur)
149{
150 int result = ML_SUCCESS;
151 long reg_dur = (dur * config->odr) / 1000000;
152 config->dur = dur;
153
154 if (reg_dur > KXTF9_MAX_DUR)
155 reg_dur = KXTF9_MAX_DUR;
156
157 config->reg_dur = (unsigned char) reg_dur;
158 MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
159 if (apply)
160 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
161 KXTF9_WUF_TIMER,
162 (unsigned char)reg_dur);
163 return result;
164}
165
166/**
167 * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
168 * duration will not be used uless the type is MOT or NMOT.
169 *
170 * @param config configuration to apply to, suspend or resume
171 * @param irq_type The type of IRQ. Valid values are
172 * - MPU_SLAVE_IRQ_TYPE_NONE
173 * - MPU_SLAVE_IRQ_TYPE_MOTION
174 * - MPU_SLAVE_IRQ_TYPE_DATA_READY
175 */
176static int kxtf9_set_irq(void *mlsl_handle,
177 struct ext_slave_platform_data *pdata,
178 struct kxtf9_config *config,
179 int apply,
180 long irq_type)
181{
182 int result = ML_SUCCESS;
183 struct kxtf9_private_data *private_data = pdata->private_data;
184
185 config->irq_type = (unsigned char)irq_type;
186 config->ctrl_reg1 &= ~0x22;
187 if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
188 config->ctrl_reg1 |= 0x20;
189 config->reg_int_cfg1 = 0x38;
190 config->reg_int_cfg2 = 0x00;
191 } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
192 config->ctrl_reg1 |= 0x02;
193 if ((unsigned long) config ==
194 (unsigned long) &private_data->suspend)
195 config->reg_int_cfg1 = 0x34;
196 else
197 config->reg_int_cfg1 = 0x24;
198 config->reg_int_cfg2 = 0xE0;
199 } else {
200 config->reg_int_cfg1 = 0x00;
201 config->reg_int_cfg2 = 0x00;
202 }
203
204 if (apply) {
205 /* Must clear bit 7 before writing new configuration */
206 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
207 KXTF9_CTRL_REG1, 0x40);
208 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
209 KXTF9_INT_CTRL_REG1,
210 config->reg_int_cfg1);
211 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
212 KXTF9_INT_CTRL_REG2,
213 config->reg_int_cfg2);
214 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
215 KXTF9_CTRL_REG1,
216 config->ctrl_reg1);
217 }
218 MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n",
219 (unsigned long)config->ctrl_reg1,
220 (unsigned long)config->reg_int_cfg1,
221 (unsigned long)config->reg_int_cfg2);
222
223 return result;
224}
225
226/**
227 * Set the Output data rate for the particular configuration
228 *
229 * @param config Config to modify with new ODR
230 * @param odr Output data rate in units of 1/1000Hz
231 */
232static int kxtf9_set_odr(void *mlsl_handle,
233 struct ext_slave_platform_data *pdata,
234 struct kxtf9_config *config,
235 int apply,
236 long odr)
237{
238 unsigned char bits;
239 int result = ML_SUCCESS;
240
241 /* Data sheet says there is 12.5 hz, but that seems to produce a single
242 * correct data value, thus we remove it from the table */
243 if (odr > 400000) {
244 config->odr = 800000;
245 bits = 0x06;
246 } else if (odr > 200000) {
247 config->odr = 400000;
248 bits = 0x05;
249 } else if (odr > 100000) {
250 config->odr = 200000;
251 bits = 0x04;
252 } else if (odr > 50000) {
253 config->odr = 100000;
254 bits = 0x03;
255 } else if (odr > 25000) {
256 config->odr = 50000;
257 bits = 0x02;
258 } else if (odr != 0) {
259 config->odr = 25000;
260 bits = 0x01;
261 } else {
262 config->odr = 0;
263 bits = 0;
264 }
265
266 config->reg_odr = bits;
267 kxtf9_set_dur(mlsl_handle, pdata,
268 config, apply, config->dur);
269 MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
270 if (apply) {
271 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
272 KXTF9_DATA_CTRL_REG,
273 config->reg_odr);
274 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
275 KXTF9_CTRL_REG1,
276 0x40);
277 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
278 KXTF9_CTRL_REG1,
279 config->ctrl_reg1);
280 }
281 return result;
282}
283
284/**
285 * Set the full scale range of the accels
286 *
287 * @param config pointer to configuration
288 * @param fsr requested full scale range
289 */
290static int kxtf9_set_fsr(void *mlsl_handle,
291 struct ext_slave_platform_data *pdata,
292 struct kxtf9_config *config,
293 int apply,
294 long fsr)
295{
296 int result = ML_SUCCESS;
297
298 config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7);
299 if (fsr <= 2000) {
300 config->fsr = 2000;
301 config->ctrl_reg1 |= 0x00;
302 } else if (fsr <= 4000) {
303 config->fsr = 4000;
304 config->ctrl_reg1 |= 0x08;
305 } else {
306 config->fsr = 8000;
307 config->ctrl_reg1 |= 0x10;
308 }
309
310 MPL_LOGV("FSR: %d\n", config->fsr);
311 if (apply) {
312 /* Must clear bit 7 before writing new configuration */
313 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
314 KXTF9_CTRL_REG1, 0x40);
315 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
316 KXTF9_CTRL_REG1, config->ctrl_reg1);
317 }
318 return result;
319}
320
321static int kxtf9_suspend(void *mlsl_handle,
322 struct ext_slave_descr *slave,
323 struct ext_slave_platform_data *pdata)
324{
325 int result;
326 unsigned char data;
327 struct kxtf9_private_data *private_data = pdata->private_data;
328
329 /* Wake up */
330 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
331 KXTF9_CTRL_REG1, 0x40);
332 ERROR_CHECK(result);
333 /* INT_CTRL_REG1: */
334 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
335 KXTF9_INT_CTRL_REG1,
336 private_data->suspend.reg_int_cfg1);
337 ERROR_CHECK(result);
338 /* WUF_THRESH: */
339 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
340 KXTF9_WUF_THRESH,
341 private_data->suspend.reg_ths);
342 ERROR_CHECK(result);
343 /* DATA_CTRL_REG */
344 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
345 KXTF9_DATA_CTRL_REG,
346 private_data->suspend.reg_odr);
347 ERROR_CHECK(result);
348 /* WUF_TIMER */
349 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
350 KXTF9_WUF_TIMER, private_data->suspend.reg_dur);
351 ERROR_CHECK(result);
352
353 /* Normal operation */
354 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
355 KXTF9_CTRL_REG1,
356 private_data->suspend.ctrl_reg1);
357 ERROR_CHECK(result);
358 result = MLSLSerialRead(mlsl_handle, pdata->address,
359 KXTF9_INT_REL, 1, &data);
360 ERROR_CHECK(result);
361
362 return result;
363}
364
365/* full scale setting - register and mask */
366#define ACCEL_KIONIX_CTRL_REG (0x1b)
367#define ACCEL_KIONIX_CTRL_MASK (0x18)
368
369static int kxtf9_resume(void *mlsl_handle,
370 struct ext_slave_descr *slave,
371 struct ext_slave_platform_data *pdata)
372{
373 int result = ML_SUCCESS;
374 unsigned char data;
375 struct kxtf9_private_data *private_data = pdata->private_data;
376
377 /* Wake up */
378 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
379 KXTF9_CTRL_REG1, 0x40);
380 ERROR_CHECK(result);
381 /* INT_CTRL_REG1: */
382 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
383 KXTF9_INT_CTRL_REG1,
384 private_data->resume.reg_int_cfg1);
385 ERROR_CHECK(result);
386 /* WUF_THRESH: */
387 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
388 KXTF9_WUF_THRESH, private_data->resume.reg_ths);
389 ERROR_CHECK(result);
390 /* DATA_CTRL_REG */
391 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
392 KXTF9_DATA_CTRL_REG,
393 private_data->resume.reg_odr);
394 ERROR_CHECK(result);
395 /* WUF_TIMER */
396 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
397 KXTF9_WUF_TIMER, private_data->resume.reg_dur);
398 ERROR_CHECK(result);
399
400 /* Normal operation */
401 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
402 KXTF9_CTRL_REG1,
403 private_data->resume.ctrl_reg1);
404 ERROR_CHECK(result);
405 result = MLSLSerialRead(mlsl_handle, pdata->address,
406 KXTF9_INT_REL, 1, &data);
407 ERROR_CHECK(result);
408
409 return ML_SUCCESS;
410}
411
412static int kxtf9_init(void *mlsl_handle,
413 struct ext_slave_descr *slave,
414 struct ext_slave_platform_data *pdata)
415{
416
417 struct kxtf9_private_data *private_data;
418 int result = ML_SUCCESS;
419
420 private_data = (struct kxtf9_private_data *)
421 MLOSMalloc(sizeof(struct kxtf9_private_data));
422
423 if (!private_data)
424 return ML_ERROR_MEMORY_EXAUSTED;
425
426 /* RAM reset */
427 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
428 KXTF9_CTRL_REG1,
429 0x40); /* Fastest Reset */
430 ERROR_CHECK(result);
431 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
432 KXTF9_DATA_CTRL_REG,
433 0x36); /* Fastest Reset */
434 ERROR_CHECK(result);
435 result = MLSLSerialWriteSingle(mlsl_handle, pdata->address,
436 KXTF9_CTRL_REG3, 0xcd); /* Reset */
437 ERROR_CHECK(result);
438 MLOSSleep(2);
439
440 pdata->private_data = private_data;
441
442 private_data->resume.ctrl_reg1 = 0xC0;
443 private_data->suspend.ctrl_reg1 = 0x40;
444
445 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend,
446 FALSE, 1000);
447 ERROR_CHECK(result);
448 result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume,
449 FALSE, 2540);
450 ERROR_CHECK(result);
451
452 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend,
453 FALSE, 50000);
454 ERROR_CHECK(result);
455 result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume,
456 FALSE, 200000);
457
458 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend,
459 FALSE, 2000);
460 ERROR_CHECK(result);
461 result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume,
462 FALSE, 2000);
463 ERROR_CHECK(result);
464
465 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend,
466 FALSE, 80);
467 ERROR_CHECK(result);
468 result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume,
469 FALSE, 40);
470 ERROR_CHECK(result);
471
472 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend,
473 FALSE,
474 MPU_SLAVE_IRQ_TYPE_NONE);
475 ERROR_CHECK(result);
476 result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume,
477 FALSE,
478 MPU_SLAVE_IRQ_TYPE_NONE);
479 ERROR_CHECK(result);
480 return result;
481}
482
483static int kxtf9_exit(void *mlsl_handle,
484 struct ext_slave_descr *slave,
485 struct ext_slave_platform_data *pdata)
486{
487 if (pdata->private_data)
488 return MLOSFree(pdata->private_data);
489 else
490 return ML_SUCCESS;
491}
492
493static int kxtf9_config(void *mlsl_handle,
494 struct ext_slave_descr *slave,
495 struct ext_slave_platform_data *pdata,
496 struct ext_slave_config *data)
497{
498 int retval;
499 long odr;
500 struct kxtf9_private_data *private_data = pdata->private_data;
501 if (!data->data)
502 return ML_ERROR_INVALID_PARAMETER;
503
504 switch (data->key) {
505 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
506 return kxtf9_set_odr(mlsl_handle, pdata,
507 &private_data->suspend,
508 data->apply,
509 *((long *)data->data));
510 case MPU_SLAVE_CONFIG_ODR_RESUME:
511 odr = *((long *)data->data);
512 if (odr != 0)
513 private_data->resume.ctrl_reg1 |= 0x80;
514
515 retval = kxtf9_set_odr(mlsl_handle, pdata,
516 &private_data->resume,
517 data->apply,
518 odr);
519 return retval;
520 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
521 return kxtf9_set_fsr(mlsl_handle, pdata,
522 &private_data->suspend,
523 data->apply,
524 *((long *)data->data));
525 case MPU_SLAVE_CONFIG_FSR_RESUME:
526 return kxtf9_set_fsr(mlsl_handle, pdata,
527 &private_data->resume,
528 data->apply,
529 *((long *)data->data));
530 case MPU_SLAVE_CONFIG_MOT_THS:
531 return kxtf9_set_ths(mlsl_handle, pdata,
532 &private_data->suspend,
533 data->apply,
534 *((long *)data->data));
535 case MPU_SLAVE_CONFIG_NMOT_THS:
536 return kxtf9_set_ths(mlsl_handle, pdata,
537 &private_data->resume,
538 data->apply,
539 *((long *)data->data));
540 case MPU_SLAVE_CONFIG_MOT_DUR:
541 return kxtf9_set_dur(mlsl_handle, pdata,
542 &private_data->suspend,
543 data->apply,
544 *((long *)data->data));
545 case MPU_SLAVE_CONFIG_NMOT_DUR:
546 return kxtf9_set_dur(mlsl_handle, pdata,
547 &private_data->resume,
548 data->apply,
549 *((long *)data->data));
550 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
551 return kxtf9_set_irq(mlsl_handle, pdata,
552 &private_data->suspend,
553 data->apply,
554 *((long *)data->data));
555 case MPU_SLAVE_CONFIG_IRQ_RESUME:
556 return kxtf9_set_irq(mlsl_handle, pdata,
557 &private_data->resume,
558 data->apply,
559 *((long *)data->data));
560 default:
561 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
562 };
563
564 return ML_SUCCESS;
565}
566
567static int kxtf9_get_config(void *mlsl_handle,
568 struct ext_slave_descr *slave,
569 struct ext_slave_platform_data *pdata,
570 struct ext_slave_config *data)
571{
572 struct kxtf9_private_data *private_data = pdata->private_data;
573 if (!data->data)
574 return ML_ERROR_INVALID_PARAMETER;
575
576 switch (data->key) {
577 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
578 (*(unsigned long *)data->data) =
579 (unsigned long) private_data->suspend.odr;
580 break;
581 case MPU_SLAVE_CONFIG_ODR_RESUME:
582 (*(unsigned long *)data->data) =
583 (unsigned long) private_data->resume.odr;
584 break;
585 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
586 (*(unsigned long *)data->data) =
587 (unsigned long) private_data->suspend.fsr;
588 break;
589 case MPU_SLAVE_CONFIG_FSR_RESUME:
590 (*(unsigned long *)data->data) =
591 (unsigned long) private_data->resume.fsr;
592 break;
593 case MPU_SLAVE_CONFIG_MOT_THS:
594 (*(unsigned long *)data->data) =
595 (unsigned long) private_data->suspend.ths;
596 break;
597 case MPU_SLAVE_CONFIG_NMOT_THS:
598 (*(unsigned long *)data->data) =
599 (unsigned long) private_data->resume.ths;
600 break;
601 case MPU_SLAVE_CONFIG_MOT_DUR:
602 (*(unsigned long *)data->data) =
603 (unsigned long) private_data->suspend.dur;
604 break;
605 case MPU_SLAVE_CONFIG_NMOT_DUR:
606 (*(unsigned long *)data->data) =
607 (unsigned long) private_data->resume.dur;
608 break;
609 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
610 (*(unsigned long *)data->data) =
611 (unsigned long) private_data->suspend.irq_type;
612 break;
613 case MPU_SLAVE_CONFIG_IRQ_RESUME:
614 (*(unsigned long *)data->data) =
615 (unsigned long) private_data->resume.irq_type;
616 break;
617 default:
618 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
619 };
620
621 return ML_SUCCESS;
622}
623
624static int kxtf9_read(void *mlsl_handle,
625 struct ext_slave_descr *slave,
626 struct ext_slave_platform_data *pdata,
627 unsigned char *data)
628{
629 int result;
630 unsigned char reg;
631 result = MLSLSerialRead(mlsl_handle, pdata->address,
632 KXTF9_INT_SRC_REG2, 1, &reg);
633 ERROR_CHECK(result);
634
635 if (!(reg & 0x10))
636 return ML_ERROR_ACCEL_DATA_NOT_READY;
637
638 result = MLSLSerialRead(mlsl_handle, pdata->address,
639 slave->reg, slave->len, data);
640 ERROR_CHECK(result);
641 return result;
642}
643
644static struct ext_slave_descr kxtf9_descr = {
645 /*.init = */ kxtf9_init,
646 /*.exit = */ kxtf9_exit,
647 /*.suspend = */ kxtf9_suspend,
648 /*.resume = */ kxtf9_resume,
649 /*.read = */ kxtf9_read,
650 /*.config = */ kxtf9_config,
651 /*.get_config = */ kxtf9_get_config,
652 /*.name = */ "kxtf9",
653 /*.type = */ EXT_SLAVE_TYPE_ACCELEROMETER,
654 /*.id = */ ACCEL_ID_KXTF9,
655 /*.reg = */ 0x06,
656 /*.len = */ 6,
657 /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
658 /*.range = */ {2, 0},
659};
660
661struct ext_slave_descr *kxtf9_get_slave_descr(void)
662{
663 return &kxtf9_descr;
664}
665EXPORT_SYMBOL(kxtf9_get_slave_descr);
666
667/**
668 * @}
669**/
diff --git a/drivers/misc/mpu3050/compass/ak8975.c b/drivers/misc/mpu3050/compass/ak8975.c
new file mode 100644
index 00000000000..b8aed30ba39
--- /dev/null
+++ b/drivers/misc/mpu3050/compass/ak8975.c
@@ -0,0 +1,258 @@
1/*
2 $License:
3 Copyright (C) 2010 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 COMPASSDL (Motion Library - Accelerometer Driver Layer)
22 * @brief Provides the interface to setup and handle an accelerometers
23 * connected to the secondary I2C interface of the gyroscope.
24 *
25 * @{
26 * @file AK8975.c
27 * @brief Magnetometer setup and handling methods for AKM 8975 compass.
28 */
29
30/* ------------------ */
31/* - Include Files. - */
32/* ------------------ */
33
34#include <string.h>
35
36#ifdef __KERNEL__
37#include <linux/module.h>
38#endif
39
40#include "mpu.h"
41#include "mlsl.h"
42#include "mlos.h"
43
44#include <log.h>
45#undef MPL_LOG_TAG
46#define MPL_LOG_TAG "MPL-compass"
47
48
49#define AK8975_REG_ST1 (0x02)
50#define AK8975_REG_HXL (0x03)
51#define AK8975_REG_ST2 (0x09)
52
53#define AK8975_REG_CNTL (0x0A)
54
55#define AK8975_CNTL_MODE_POWER_DOWN (0x00)
56#define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
57
58int ak8975_suspend(void *mlsl_handle,
59 struct ext_slave_descr *slave,
60 struct ext_slave_platform_data *pdata)
61{
62 int result = ML_SUCCESS;
63 result =
64 MLSLSerialWriteSingle(mlsl_handle, pdata->address,
65 AK8975_REG_CNTL,
66 AK8975_CNTL_MODE_POWER_DOWN);
67 MLOSSleep(1); /* wait at least 100us */
68 ERROR_CHECK(result);
69 return result;
70}
71
72int ak8975_resume(void *mlsl_handle,
73 struct ext_slave_descr *slave,
74 struct ext_slave_platform_data *pdata)
75{
76 int result = ML_SUCCESS;
77 result =
78 MLSLSerialWriteSingle(mlsl_handle, pdata->address,
79 AK8975_REG_CNTL,
80 AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
81 ERROR_CHECK(result);
82 return result;
83}
84
85int ak8975_read(void *mlsl_handle,
86 struct ext_slave_descr *slave,
87 struct ext_slave_platform_data *pdata, unsigned char *data)
88{
89 unsigned char regs[8];
90 unsigned char *stat = &regs[0];
91 unsigned char *stat2 = &regs[7];
92 int result = ML_SUCCESS;
93 int status = ML_SUCCESS;
94
95 result =
96 MLSLSerialRead(mlsl_handle, pdata->address, AK8975_REG_ST1,
97 8, regs);
98 ERROR_CHECK(result);
99
100 /*
101 * ST : data ready -
102 * Measurement has been completed and data is ready to be read.
103 */
104 if (*stat & 0x01) {
105 memcpy(data, &regs[1], 6);
106 status = ML_SUCCESS;
107 }
108
109 /*
110 * ST2 : data error -
111 * occurs when data read is started outside of a readable period;
112 * data read would not be correct.
113 * Valid in continuous measurement mode only.
114 * In single measurement mode this error should not occour but we
115 * stil account for it and return an error, since the data would be
116 * corrupted.
117 * DERR bit is self-clearing when ST2 register is read.
118 */
119 if (*stat2 & 0x04)
120 status = ML_ERROR_COMPASS_DATA_ERROR;
121 /*
122 * ST2 : overflow -
123 * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
124 * This is likely to happen in presence of an external magnetic
125 * disturbance; it indicates, the sensor data is incorrect and should
126 * be ignored.
127 * An error is returned.
128 * HOFL bit clears when a new measurement starts.
129 */
130 if (*stat2 & 0x08)
131 status = ML_ERROR_COMPASS_DATA_OVERFLOW;
132 /*
133 * ST : overrun -
134 * the previous sample was not fetched and lost.
135 * Valid in continuous measurement mode only.
136 * In single measurement mode this error should not occour and we
137 * don't consider this condition an error.
138 * DOR bit is self-clearing when ST2 or any meas. data register is
139 * read.
140 */
141 if (*stat & 0x02) {
142 /* status = ML_ERROR_COMPASS_DATA_UNDERFLOW; */
143 status = ML_SUCCESS;
144 }
145
146 /*
147 * trigger next measurement if:
148 * - stat is non zero;
149 * - if stat is zero and stat2 is non zero.
150 * Won't trigger if data is not ready and there was no error.
151 */
152 if (*stat != 0x00 || *stat2 != 0x00) {
153 result =
154 MLSLSerialWriteSingle(mlsl_handle, pdata->address,
155 AK8975_REG_CNTL,
156 AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
157 ERROR_CHECK(result);
158 }
159
160 return status;
161}
162
163static int ak8975_config(void *mlsl_handle,
164 struct ext_slave_descr *slave,
165 struct ext_slave_platform_data *pdata,
166 struct ext_slave_config *data)
167{
168 int result;
169 if (!data->data)
170 return ML_ERROR_INVALID_PARAMETER;
171
172 switch (data->key) {
173 case MPU_SLAVE_WRITE_REGISTERS:
174 result = MLSLSerialWrite(mlsl_handle, pdata->address,
175 data->len,
176 (unsigned char *)data->data);
177 ERROR_CHECK(result);
178 break;
179 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
180 case MPU_SLAVE_CONFIG_ODR_RESUME:
181 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
182 case MPU_SLAVE_CONFIG_FSR_RESUME:
183 case MPU_SLAVE_CONFIG_MOT_THS:
184 case MPU_SLAVE_CONFIG_NMOT_THS:
185 case MPU_SLAVE_CONFIG_MOT_DUR:
186 case MPU_SLAVE_CONFIG_NMOT_DUR:
187 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
188 case MPU_SLAVE_CONFIG_IRQ_RESUME:
189 default:
190 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
191 };
192
193 return ML_SUCCESS;
194}
195
196static int ak8975_get_config(void *mlsl_handle,
197 struct ext_slave_descr *slave,
198 struct ext_slave_platform_data *pdata,
199 struct ext_slave_config *data)
200{
201 int result;
202 if (!data->data)
203 return ML_ERROR_INVALID_PARAMETER;
204
205 switch (data->key) {
206 case MPU_SLAVE_READ_REGISTERS:
207 {
208 unsigned char *serial_data = (unsigned char *)data->data;
209 result = MLSLSerialRead(mlsl_handle, pdata->address,
210 serial_data[0],
211 data->len - 1,
212 &serial_data[1]);
213 ERROR_CHECK(result);
214 break;
215 }
216 case MPU_SLAVE_CONFIG_ODR_SUSPEND:
217 case MPU_SLAVE_CONFIG_ODR_RESUME:
218 case MPU_SLAVE_CONFIG_FSR_SUSPEND:
219 case MPU_SLAVE_CONFIG_FSR_RESUME:
220 case MPU_SLAVE_CONFIG_MOT_THS:
221 case MPU_SLAVE_CONFIG_NMOT_THS:
222 case MPU_SLAVE_CONFIG_MOT_DUR:
223 case MPU_SLAVE_CONFIG_NMOT_DUR:
224 case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
225 case MPU_SLAVE_CONFIG_IRQ_RESUME:
226 default:
227 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
228 };
229
230 return ML_SUCCESS;
231}
232
233struct ext_slave_descr ak8975_descr = {
234 /*.init = */ NULL,
235 /*.exit = */ NULL,
236 /*.suspend = */ ak8975_suspend,
237 /*.resume = */ ak8975_resume,
238 /*.read = */ ak8975_read,
239 /*.config = */ ak8975_config,
240 /*.get_config = */ ak8975_get_config,
241 /*.name = */ "ak8975",
242 /*.type = */ EXT_SLAVE_TYPE_COMPASS,
243 /*.id = */ COMPASS_ID_AKM,
244 /*.reg = */ 0x01,
245 /*.len = */ 9,
246 /*.endian = */ EXT_SLAVE_LITTLE_ENDIAN,
247 /*.range = */ {9830, 4000}
248};
249
250struct ext_slave_descr *ak8975_get_slave_descr(void)
251{
252 return &ak8975_descr;
253}
254EXPORT_SYMBOL(ak8975_get_slave_descr);
255
256/**
257 * @}
258 */
diff --git a/drivers/misc/mpu3050/log.h b/drivers/misc/mpu3050/log.h
new file mode 100644
index 00000000000..f2f9ea7ece8
--- /dev/null
+++ b/drivers/misc/mpu3050/log.h
@@ -0,0 +1,306 @@
1/*
2 * Copyright (C) 2010 InvenSense Inc
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 * C/C++ logging functions. See the logging documentation for API details.
19 *
20 * We'd like these to be available from C code (in case we import some from
21 * somewhere), so this has a C interface.
22 *
23 * The output will be correct when the log file is shared between multiple
24 * threads and/or multiple processes so long as the operating system
25 * supports O_APPEND. These calls have mutex-protected data structures
26 * and so are NOT reentrant. Do not use MPL_LOG in a signal handler.
27 */
28#ifndef _LIBS_CUTILS_MPL_LOG_H
29#define _LIBS_CUTILS_MPL_LOG_H
30
31#include <stdarg.h>
32
33#ifdef ANDROID
34#include <utils/Log.h> /* For the LOG macro */
35#endif
36
37#ifdef __KERNEL__
38#include <linux/kernel.h>
39#endif
40
41#ifdef __cplusplus
42extern "C" {
43#endif
44
45/* --------------------------------------------------------------------- */
46
47/*
48 * Normally we strip MPL_LOGV (VERBOSE messages) from release builds.
49 * You can modify this (for example with "#define MPL_LOG_NDEBUG 0"
50 * at the top of your source file) to change that behavior.
51 */
52#ifndef MPL_LOG_NDEBUG
53#ifdef NDEBUG
54#define MPL_LOG_NDEBUG 1
55#else
56#define MPL_LOG_NDEBUG 0
57#endif
58#endif
59
60#ifdef __KERNEL__
61#define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE
62#define MPL_LOG_DEFAULT KERN_DEFAULT
63#define MPL_LOG_VERBOSE KERN_CONT
64#define MPL_LOG_DEBUG KERN_NOTICE
65#define MPL_LOG_INFO KERN_INFO
66#define MPL_LOG_WARN KERN_WARNING
67#define MPL_LOG_ERROR KERN_ERR
68#define MPL_LOG_SILENT MPL_LOG_VERBOSE
69
70#else
71 /* Based off the log priorities in android
72 /system/core/include/android/log.h */
73#define MPL_LOG_UNKNOWN (0)
74#define MPL_LOG_DEFAULT (1)
75#define MPL_LOG_VERBOSE (2)
76#define MPL_LOG_DEBUG (3)
77#define MPL_LOG_INFO (4)
78#define MPL_LOG_WARN (5)
79#define MPL_LOG_ERROR (6)
80#define MPL_LOG_SILENT (8)
81#endif
82
83
84/*
85 * This is the local tag used for the following simplified
86 * logging macros. You can change this preprocessor definition
87 * before using the other macros to change the tag.
88 */
89#ifndef MPL_LOG_TAG
90#ifdef __KERNEL__
91#define MPL_LOG_TAG
92#else
93#define MPL_LOG_TAG NULL
94#endif
95#endif
96
97/* --------------------------------------------------------------------- */
98
99/*
100 * Simplified macro to send a verbose log message using the current MPL_LOG_TAG.
101 */
102#ifndef MPL_LOGV
103#if MPL_LOG_NDEBUG
104#define MPL_LOGV(fmt, ...) \
105 do { \
106 if (0) \
107 MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\
108 } while (0)
109#else
110#define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
111#endif
112#endif
113
114#ifndef CONDITION
115#define CONDITION(cond) ((cond) != 0)
116#endif
117
118#ifndef MPL_LOGV_IF
119#if MPL_LOG_NDEBUG
120#define MPL_LOGV_IF(cond, fmt, ...) \
121 do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0)
122#else
123#define MPL_LOGV_IF(cond, fmt, ...) \
124 ((CONDITION(cond)) \
125 ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
126 : (void)0)
127#endif
128#endif
129
130/*
131 * Simplified macro to send a debug log message using the current MPL_LOG_TAG.
132 */
133#ifndef MPL_LOGD
134#define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
135#endif
136
137#ifndef MPL_LOGD_IF
138#define MPL_LOGD_IF(cond, fmt, ...) \
139 ((CONDITION(cond)) \
140 ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
141 : (void)0)
142#endif
143
144/*
145 * Simplified macro to send an info log message using the current MPL_LOG_TAG.
146 */
147#ifndef MPL_LOGI
148#define MPL_LOGI(fmt, ...) MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
149#endif
150
151#ifndef MPL_LOGI_IF
152#define MPL_LOGI_IF(cond, fmt, ...) \
153 ((CONDITION(cond)) \
154 ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
155 : (void)0)
156#endif
157
158/*
159 * Simplified macro to send a warning log message using the current MPL_LOG_TAG.
160 */
161#ifndef MPL_LOGW
162#ifdef __KERNEL__
163#define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__)
164#else
165#define MPL_LOGW(fmt, ...) MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
166#endif
167#endif
168
169#ifndef MPL_LOGW_IF
170#define MPL_LOGW_IF(cond, fmt, ...) \
171 ((CONDITION(cond)) \
172 ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
173 : (void)0)
174#endif
175
176/*
177 * Simplified macro to send an error log message using the current MPL_LOG_TAG.
178 */
179#ifndef MPL_LOGE
180#ifdef __KERNEL__
181#define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__)
182#else
183#define MPL_LOGE(fmt, ...) MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
184#endif
185#endif
186
187#ifndef MPL_LOGE_IF
188#define MPL_LOGE_IF(cond, fmt, ...) \
189 ((CONDITION(cond)) \
190 ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
191 : (void)0)
192#endif
193
194/* --------------------------------------------------------------------- */
195
196/*
197 * Log a fatal error. If the given condition fails, this stops program
198 * execution like a normal assertion, but also generating the given message.
199 * It is NOT stripped from release builds. Note that the condition test
200 * is -inverted- from the normal assert() semantics.
201 */
202#define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \
203 ((CONDITION(cond)) \
204 ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \
205 fmt, ##__VA_ARGS__)) \
206 : (void)0)
207
208#define MPL_LOG_ALWAYS_FATAL(fmt, ...) \
209 (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__)))
210
211/*
212 * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that
213 * are stripped out of release builds.
214 */
215#if MPL_LOG_NDEBUG
216#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
217 do { \
218 if (0) \
219 MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \
220 } while (0)
221#define MPL_LOG_FATAL(fmt, ...) \
222 do { \
223 if (0) \
224 MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \
225 } while (0)
226#else
227#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
228 MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__)
229#define MPL_LOG_FATAL(fmt, ...) \
230 MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__)
231#endif
232
233/*
234 * Assertion that generates a log message when the assertion fails.
235 * Stripped out of release builds. Uses the current MPL_LOG_TAG.
236 */
237#define MPL_LOG_ASSERT(cond, fmt, ...) \
238 MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__)
239
240/* --------------------------------------------------------------------- */
241
242/*
243 * Basic log message macro.
244 *
245 * Example:
246 * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno);
247 *
248 * The second argument may be NULL or "" to indicate the "global" tag.
249 */
250#ifndef MPL_LOG
251#define MPL_LOG(priority, tag, fmt, ...) \
252 MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__)
253#endif
254
255/*
256 * Log macro that allows you to specify a number for the priority.
257 */
258#ifndef MPL_LOG_PRI
259#ifdef ANDROID
260#define MPL_LOG_PRI(priority, tag, fmt, ...) \
261 LOG(priority, tag, fmt, ##__VA_ARGS__)
262#elif defined __KERNEL__
263#define MPL_LOG_PRI(priority, tag, fmt, ...) \
264 pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__)
265#else
266#define MPL_LOG_PRI(priority, tag, fmt, ...) \
267 _MLPrintLog(MPL_##priority, tag, fmt, ##__VA_ARGS__)
268#endif
269#endif
270
271/*
272 * Log macro that allows you to pass in a varargs ("args" is a va_list).
273 */
274#ifndef MPL_LOG_PRI_VA
275#ifdef ANDROID
276#define MPL_LOG_PRI_VA(priority, tag, fmt, args) \
277 android_vprintLog(priority, NULL, tag, fmt, args)
278#elif defined __KERNEL__
279/* not allowed in the Kernel because there is no dev_dbg that takes a va_list */
280#else
281#define MPL_LOG_PRI_VA(priority, tag, fmt, args) \
282 _MLPrintVaLog(priority, NULL, tag, fmt, args)
283#endif
284#endif
285
286/* --------------------------------------------------------------------- */
287
288/*
289 * ===========================================================================
290 *
291 * The stuff in the rest of this file should not be used directly.
292 */
293
294#ifndef ANDROID
295 int _MLPrintLog(int priority, const char *tag, const char *fmt,
296 ...);
297 int _MLPrintVaLog(int priority, const char *tag, const char *fmt,
298 va_list args);
299/* Final implementation of actual writing to a character device */
300 int _MLWriteLog(const char *buf, int buflen);
301#endif
302
303#ifdef __cplusplus
304}
305#endif
306#endif /* _LIBS_CUTILS_MPL_LOG_H */
diff --git a/drivers/misc/mpu3050/mldl_cfg.c b/drivers/misc/mpu3050/mldl_cfg.c
new file mode 100644
index 00000000000..9cc4cf69038
--- /dev/null
+++ b/drivers/misc/mpu3050/mldl_cfg.c
@@ -0,0 +1,1739 @@
1/*
2 $License:
3 Copyright (C) 2010 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 Files. - */
30/* ------------------ */
31
32#include <stddef.h>
33
34#include "mldl_cfg.h"
35#include "mpu.h"
36
37#include "mlsl.h"
38#include "mlos.h"
39
40#include "log.h"
41#undef MPL_LOG_TAG
42#define MPL_LOG_TAG "mldl_cfg:"
43
44/* --------------------- */
45/* - Variables. - */
46/* --------------------- */
47#ifdef M_HW
48#define SLEEP 0
49#define WAKE_UP 7
50#define RESET 1
51#define STANDBY 1
52#else
53/* licteral significance of all parameters used in MLDLPowerMgmtMPU */
54#define SLEEP 1
55#define WAKE_UP 0
56#define RESET 1
57#define STANDBY 1
58#endif
59
60/*---------------------*/
61/*- Prototypes. -*/
62/*---------------------*/
63
64/*----------------------*/
65/*- Static Functions. -*/
66/*----------------------*/
67
68static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle)
69{
70 unsigned char userCtrlReg;
71 int result;
72
73 if (!mldl_cfg->dmp_is_running)
74 return ML_SUCCESS;
75
76 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
77 MPUREG_USER_CTRL, 1, &userCtrlReg);
78 ERROR_CHECK(result);
79 userCtrlReg = (userCtrlReg & (~BIT_FIFO_EN)) | BIT_FIFO_RST;
80 userCtrlReg = (userCtrlReg & (~BIT_DMP_EN)) | BIT_DMP_RST;
81
82 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
83 MPUREG_USER_CTRL, userCtrlReg);
84 ERROR_CHECK(result);
85 mldl_cfg->dmp_is_running = 0;
86
87 return result;
88
89}
90/**
91 * @brief Starts the DMP running
92 *
93 * @return ML_SUCCESS or non-zero error code
94 */
95static int dmp_start(struct mldl_cfg *pdata, void *mlsl_handle)
96{
97 unsigned char userCtrlReg;
98 int result;
99
100 if (pdata->dmp_is_running == pdata->dmp_enable)
101 return ML_SUCCESS;
102
103 result = MLSLSerialRead(mlsl_handle, pdata->addr,
104 MPUREG_USER_CTRL, 1, &userCtrlReg);
105 ERROR_CHECK(result);
106
107 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
108 MPUREG_USER_CTRL,
109 ((userCtrlReg & (~BIT_FIFO_EN))
110 | BIT_FIFO_RST));
111 ERROR_CHECK(result);
112
113 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
114 MPUREG_USER_CTRL, userCtrlReg);
115 ERROR_CHECK(result);
116
117 result = MLSLSerialRead(mlsl_handle, pdata->addr,
118 MPUREG_USER_CTRL, 1, &userCtrlReg);
119 ERROR_CHECK(result);
120
121 if (pdata->dmp_enable)
122 userCtrlReg |= BIT_DMP_EN;
123 else
124 userCtrlReg &= ~BIT_DMP_EN;
125
126 if (pdata->fifo_enable)
127 userCtrlReg |= BIT_FIFO_EN;
128 else
129 userCtrlReg &= ~BIT_FIFO_EN;
130
131 userCtrlReg |= BIT_DMP_RST;
132
133 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
134 MPUREG_USER_CTRL, userCtrlReg);
135 ERROR_CHECK(result);
136 pdata->dmp_is_running = pdata->dmp_enable;
137
138 return result;
139}
140
141/**
142 * @brief enables/disables the I2C bypass to an external device
143 * connected to MPU's secondary I2C bus.
144 * @param enable
145 * Non-zero to enable pass through.
146 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
147 */
148static int MLDLSetI2CBypass(struct mldl_cfg *mldl_cfg,
149 void *mlsl_handle,
150 unsigned char enable)
151{
152 unsigned char b;
153 int result;
154
155 if ((mldl_cfg->gyro_is_bypassed && enable) ||
156 (!mldl_cfg->gyro_is_bypassed && !enable))
157 return ML_SUCCESS;
158
159 /*---- get current 'USER_CTRL' into b ----*/
160 result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
161 MPUREG_USER_CTRL, 1, &b);
162 ERROR_CHECK(result);
163
164 b &= ~BIT_AUX_IF_EN;
165
166 if (!enable) {
167 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
168 MPUREG_USER_CTRL,
169 (b | BIT_AUX_IF_EN));
170 ERROR_CHECK(result);
171 } else {
172 /* Coming out of I2C is tricky due to several erratta. Do not
173 * modify this algorithm
174 */
175 /*
176 * 1) wait for the right time and send the command to change
177 * the aux i2c slave address to an invalid address that will
178 * get nack'ed
179 *
180 * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
181 */
182 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
183 MPUREG_AUX_SLV_ADDR, 0x7F);
184 ERROR_CHECK(result);
185 /*
186 * 2) wait enough time for a nack to occur, then go into
187 * bypass mode:
188 */
189 MLOSSleep(2);
190 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
191 MPUREG_USER_CTRL, (b));
192 ERROR_CHECK(result);
193 /*
194 * 3) wait for up to one MPU cycle then restore the slave
195 * address
196 */
197 MLOSSleep(SAMPLING_PERIOD_US(mldl_cfg) / 1000);
198 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
199 MPUREG_AUX_SLV_ADDR,
200 mldl_cfg->pdata->
201 accel.address);
202 ERROR_CHECK(result);
203
204 /*
205 * 4) reset the ime interface
206 */
207#ifdef M_HW
208 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
209 MPUREG_USER_CTRL,
210 (b | BIT_I2C_MST_RST));
211
212#else
213 result = MLSLSerialWriteSingle(mlsl_handle, mldl_cfg->addr,
214 MPUREG_USER_CTRL,
215 (b | BIT_AUX_IF_RST));
216#endif
217 ERROR_CHECK(result);
218 MLOSSleep(2);
219 }
220 mldl_cfg->gyro_is_bypassed = enable;
221
222 return result;
223}
224
225struct tsProdRevMap {
226 unsigned char siliconRev;
227 unsigned short sensTrim;
228};
229
230#define NUM_OF_PROD_REVS (DIM(prodRevsMap))
231
232/* NOTE : 'npp' is a non production part */
233#ifdef M_HW
234#define OLDEST_PROD_REV_SUPPORTED 1
235static struct tsProdRevMap prodRevsMap[] = {
236 {0, 0},
237 {MPU_SILICON_REV_A1, 131}, /* 1 A1 (npp) */
238 {MPU_SILICON_REV_A1, 131}, /* 2 A1 (npp) */
239 {MPU_SILICON_REV_A1, 131}, /* 3 A1 (npp) */
240 {MPU_SILICON_REV_A1, 131}, /* 4 A1 (npp) */
241 {MPU_SILICON_REV_A1, 131}, /* 5 A1 (npp) */
242 {MPU_SILICON_REV_A1, 131}, /* 6 A1 (npp) */
243 {MPU_SILICON_REV_A1, 131}, /* 7 A1 (npp) */
244 {MPU_SILICON_REV_A1, 131}, /* 8 A1 (npp) */
245};
246
247#else /* !M_HW */
248#define OLDEST_PROD_REV_SUPPORTED 11
249
250static struct tsProdRevMap prodRevsMap[] = {
251 {0, 0},
252 {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */
253 {MPU_SILICON_REV_A4, 131}, /* 2 | */
254 {MPU_SILICON_REV_A4, 131}, /* 3 V */
255 {MPU_SILICON_REV_A4, 131}, /* 4 */
256 {MPU_SILICON_REV_A4, 131}, /* 5 */
257 {MPU_SILICON_REV_A4, 131}, /* 6 */
258 {MPU_SILICON_REV_A4, 131}, /* 7 */
259 {MPU_SILICON_REV_A4, 131}, /* 8 */
260 {MPU_SILICON_REV_A4, 131}, /* 9 */
261 {MPU_SILICON_REV_A4, 131}, /* 10 */
262 {MPU_SILICON_REV_B1, 131}, /* 11 B1 */
263 {MPU_SILICON_REV_B1, 131}, /* 12 | */
264 {MPU_SILICON_REV_B1, 131}, /* 13 V */
265 {MPU_SILICON_REV_B1, 131}, /* 14 B4 */
266 {MPU_SILICON_REV_B4, 131}, /* 15 | */
267 {MPU_SILICON_REV_B4, 131}, /* 16 V */
268 {MPU_SILICON_REV_B4, 131}, /* 17 */
269 {MPU_SILICON_REV_B4, 131}, /* 18 */
270 {MPU_SILICON_REV_B4, 115}, /* 19 */
271 {MPU_SILICON_REV_B4, 115}, /* 20 */
272 {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */
273 {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */
274 {MPU_SILICON_REV_B6, 0}, /* 23 B6 (npp) */
275 {MPU_SILICON_REV_B6, 0}, /* 24 | (npp) */
276 {MPU_SILICON_REV_B6, 0}, /* 25 V (npp) */
277 {MPU_SILICON_REV_B6, 131}, /* 26 (B6/A11) */
278};
279#endif /* !M_HW */
280
281/**
282 * @internal
283 * @brief Get the silicon revision ID from OTP.
284 * The silicon revision number is in read from OTP bank 0,
285 * ADDR6[7:2]. The corresponding ID is retrieved by lookup
286 * in a map.
287 * @return The silicon revision ID (0 on error).
288 */
289static int MLDLGetSiliconRev(struct mldl_cfg *pdata,
290 void *mlsl_handle)
291{
292 int result;
293 unsigned char index = 0x00;
294 unsigned char bank =
295 (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
296 unsigned short memAddr = ((bank << 8) | 0x06);
297
298 result = MLSLSerialReadMem(mlsl_handle, pdata->addr,
299 memAddr, 1, &index);
300 ERROR_CHECK(result);
301 if (result)
302 return result;
303 index >>= 2;
304
305 /* clean the prefetch and cfg user bank bits */
306 result =
307 MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
308 MPUREG_BANK_SEL, 0);
309 ERROR_CHECK(result);
310 if (result)
311 return result;
312
313 if (index < OLDEST_PROD_REV_SUPPORTED || NUM_OF_PROD_REVS <= index) {
314 pdata->silicon_revision = 0;
315 pdata->trim = 0;
316 MPL_LOGE("Unsupported Product Revision Detected : %d\n", index);
317 return ML_ERROR_INVALID_MODULE;
318 }
319
320 pdata->silicon_revision = prodRevsMap[index].siliconRev;
321 pdata->trim = prodRevsMap[index].sensTrim;
322
323 if (pdata->trim == 0) {
324 MPL_LOGE("sensitivity trim is 0"
325 " - unsupported non production part.\n");
326 return ML_ERROR_INVALID_MODULE;
327 }
328
329 return result;
330}
331
332/**
333 * @brief Enable / Disable the use MPU's secondary I2C interface level
334 * shifters.
335 * When enabled the secondary I2C interface to which the external
336 * device is connected runs at VDD voltage (main supply).
337 * When disabled the 2nd interface runs at VDDIO voltage.
338 * See the device specification for more details.
339 *
340 * @note using this API may produce unpredictable results, depending on how
341 * the MPU and slave device are setup on the target platform.
342 * Use of this API should entirely be restricted to system
343 * integrators. Once the correct value is found, there should be no
344 * need to change the level shifter at runtime.
345 *
346 * @pre Must be called after MLSerialOpen().
347 * @note Typically called before MLDmpOpen().
348 *
349 * @param[in] enable:
350 * 0 to run at VDDIO (default),
351 * 1 to run at VDD.
352 *
353 * @return ML_SUCCESS if successfull, a non-zero error code otherwise.
354 */
355static int MLDLSetLevelShifterBit(struct mldl_cfg *pdata,
356 void *mlsl_handle,
357 unsigned char enable)
358{
359#ifndef M_HW
360 int result;
361 unsigned char reg;
362 unsigned char mask;
363 unsigned char regval;
364
365 if (0 == pdata->silicon_revision)
366 return ML_ERROR_INVALID_PARAMETER;
367
368 /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR --
369 NOTE: this is incompatible with ST accelerometers where the VDDIO
370 bit MUST be set to enable ST's internal logic to autoincrement
371 the register address on burst reads --*/
372 if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) {
373 reg = MPUREG_ACCEL_BURST_ADDR;
374 mask = 0x80;
375 } else {
376 /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 =>
377 the mask is always 0x04 --*/
378 reg = MPUREG_FIFO_EN2;
379 mask = 0x04;
380 }
381
382 result = MLSLSerialRead(mlsl_handle, pdata->addr, reg, 1, &regval);
383 if (result)
384 return result;
385
386 if (enable)
387 regval |= mask;
388 else
389 regval &= ~mask;
390
391 result =
392 MLSLSerialWriteSingle(mlsl_handle, pdata->addr, reg, regval);
393
394 return result;
395#else
396 return ML_SUCCESS;
397#endif
398}
399
400
401#ifdef M_HW
402/**
403 * @internal
404 * @param reset 1 to reset hardware
405 */
406static tMLError mpu60xx_pwr_mgmt(struct mldl_cfg *pdata,
407 void *mlsl_handle,
408 unsigned char reset,
409 unsigned char powerselection)
410{
411 unsigned char b;
412 tMLError result;
413
414 if (powerselection < 0 || powerselection > 7)
415 return ML_ERROR_INVALID_PARAMETER;
416
417 result =
418 MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_1, 1,
419 &b);
420 ERROR_CHECK(result);
421
422 b &= ~(BITS_PWRSEL);
423
424 if (reset) {
425 /* Current sillicon has an errata where the reset will get
426 * nacked. Ignore the error code for now. */
427 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
428 MPUREG_PWR_MGM, b | BIT_H_RESET);
429#define M_HW_RESET_ERRATTA
430#ifndef M_HW_RESET_ERRATTA
431 ERROR_CHECK(result);
432#else
433 MLOSSleep(50);
434#endif
435 }
436
437 b |= (powerselection << 4);
438
439 if (b & BITS_PWRSEL)
440 pdata->gyro_is_suspended = FALSE;
441 else
442 pdata->gyro_is_suspended = TRUE;
443
444 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
445 MPUREG_PWR_MGM, b);
446 ERROR_CHECK(result);
447
448 return ML_SUCCESS;
449}
450
451/**
452 * @internal
453 */
454static tMLError MLDLStandByGyros(struct mldl_cfg *pdata,
455 void *mlsl_handle,
456 unsigned char disable_gx,
457 unsigned char disable_gy,
458 unsigned char disable_gz)
459{
460 unsigned char b;
461 tMLError result;
462
463 result =
464 MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1,
465 &b);
466 ERROR_CHECK(result);
467
468 b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
469 b |= (disable_gx << 2 | disable_gy << 1 | disable_gz);
470
471 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
472 MPUREG_PWR_MGMT_2, b);
473 ERROR_CHECK(result);
474
475 return ML_SUCCESS;
476}
477
478/**
479 * @internal
480 */
481static tMLError MLDLStandByAccels(struct mldl_cfg *pdata,
482 void *mlsl_handle,
483 unsigned char disable_ax,
484 unsigned char disable_ay,
485 unsigned char disable_az)
486{
487 unsigned char b;
488 tMLError result;
489
490 result =
491 MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGMT_2, 1,
492 &b);
493 ERROR_CHECK(result);
494
495 b &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
496 b |= (disable_ax << 2 | disable_ay << 1 | disable_az);
497
498 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
499 MPUREG_PWR_MGMT_2, b);
500 ERROR_CHECK(result);
501
502 return ML_SUCCESS;
503}
504
505#else /* ! M_HW */
506
507/**
508 * @internal
509 * @brief This function controls the power management on the MPU device.
510 * The entire chip can be put to low power sleep mode, or individual
511 * gyros can be turned on/off.
512 *
513 * Putting the device into sleep mode depending upon the changing needs
514 * of the associated applications is a recommended method for reducing
515 * power consuption. It is a safe opearation in that sleep/wake up of
516 * gyros while running will not result in any interruption of data.
517 *
518 * Although it is entirely allowed to put the device into full sleep
519 * while running the DMP, it is not recomended because it will disrupt
520 * the ongoing calculations carried on inside the DMP and consequently
521 * the sensor fusion algorithm. Furthermore, while in sleep mode
522 * read & write operation from the app processor on both registers and
523 * memory are disabled and can only regained by restoring the MPU in
524 * normal power mode.
525 * Disabling any of the gyro axis will reduce the associated power
526 * consuption from the PLL but will not stop the DMP from running
527 * state.
528 *
529 * @param reset
530 * Non-zero to reset the device. Note that this setting
531 * is volatile and the corresponding register bit will
532 * clear itself right after being applied.
533 * @param sleep
534 * Non-zero to put device into full sleep.
535 * @param disable_gx
536 * Non-zero to disable gyro X.
537 * @param disable_gy
538 * Non-zero to disable gyro Y.
539 * @param disable_gz
540 * Non-zero to disable gyro Z.
541 *
542 * @return ML_SUCCESS if successfull; a non-zero error code otherwise.
543 */
544static int MLDLPowerMgmtMPU(struct mldl_cfg *pdata,
545 void *mlsl_handle,
546 unsigned char reset,
547 unsigned char sleep,
548 unsigned char disable_gx,
549 unsigned char disable_gy,
550 unsigned char disable_gz)
551{
552 unsigned char b;
553 int result;
554
555 result =
556 MLSLSerialRead(mlsl_handle, pdata->addr, MPUREG_PWR_MGM, 1,
557 &b);
558 ERROR_CHECK(result);
559
560 /* If we are awake, we need to put it in bypass before resetting */
561 if ((!(b & BIT_SLEEP)) && reset)
562 result = MLDLSetI2CBypass(pdata, mlsl_handle, 1);
563
564 /* If we are awake, we need stop the dmp sleeping */
565 if ((!(b & BIT_SLEEP)) && sleep)
566 dmp_stop(pdata, mlsl_handle);
567
568 /* Reset if requested */
569 if (reset) {
570 MPL_LOGV("Reset MPU3050\n");
571 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
572 MPUREG_PWR_MGM, b | BIT_H_RESET);
573 ERROR_CHECK(result);
574 MLOSSleep(5);
575 pdata->gyro_needs_reset = FALSE;
576 /* Some chips are awake after reset and some are asleep,
577 * check the status */
578 result = MLSLSerialRead(mlsl_handle, pdata->addr,
579 MPUREG_PWR_MGM, 1, &b);
580 ERROR_CHECK(result);
581 }
582
583 /* Update the suspended state just in case we return early */
584 if (b & BIT_SLEEP)
585 pdata->gyro_is_suspended = TRUE;
586 else
587 pdata->gyro_is_suspended = FALSE;
588
589 /* if power status match requested, nothing else's left to do */
590 if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
591 (((sleep != 0) * BIT_SLEEP) |
592 ((disable_gx != 0) * BIT_STBY_XG) |
593 ((disable_gy != 0) * BIT_STBY_YG) |
594 ((disable_gz != 0) * BIT_STBY_ZG))) {
595 return ML_SUCCESS;
596 }
597
598 /*
599 * This specific transition between states needs to be reinterpreted:
600 * (1,1,1,1) -> (0,1,1,1) has to become
601 * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1)
602 * where
603 * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1)
604 */
605 if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
606 (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
607 && ((!sleep) && disable_gx && disable_gy && disable_gz)) {
608 result = MLDLPowerMgmtMPU(pdata, mlsl_handle, 0, 1, 0, 0, 0);
609 if (result)
610 return result;
611 b |= BIT_SLEEP;
612 b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
613 }
614
615 if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) {
616 if (sleep) {
617 result = MLDLSetI2CBypass(pdata, mlsl_handle, 1);
618 ERROR_CHECK(result);
619 b |= BIT_SLEEP;
620 result =
621 MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
622 MPUREG_PWR_MGM, b);
623 ERROR_CHECK(result);
624 pdata->gyro_is_suspended = TRUE;
625 } else {
626 b &= ~BIT_SLEEP;
627 result =
628 MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
629 MPUREG_PWR_MGM, b);
630 ERROR_CHECK(result);
631 pdata->gyro_is_suspended = FALSE;
632 MLOSSleep(5);
633 }
634 }
635 /*---
636 WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE
637 1) put one axis at a time in stand-by
638 ---*/
639 if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) {
640 b ^= BIT_STBY_XG;
641 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
642 MPUREG_PWR_MGM, b);
643 ERROR_CHECK(result);
644 }
645 if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) {
646 b ^= BIT_STBY_YG;
647 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
648 MPUREG_PWR_MGM, b);
649 ERROR_CHECK(result);
650 }
651 if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) {
652 b ^= BIT_STBY_ZG;
653 result = MLSLSerialWriteSingle(mlsl_handle, pdata->addr,
654 MPUREG_PWR_MGM, b);
655 ERROR_CHECK(result);
656 }
657
658 return ML_SUCCESS;
659}
660#endif /* M_HW */
661
662
663void mpu_print_cfg(struct mldl_cfg *mldl_cfg)
664{
665 struct mpu3050_platform_data *pdata = mldl_cfg->pdata;
666 struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel;
667 struct ext_slave_platform_data *compass =
668 &mldl_cfg->pdata->compass;
669 struct ext_slave_platform_data *pressure =
670 &mldl_cfg->pdata->pressure;
671
672 MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr);
673 MPL_LOGD("mldl_cfg.int_config = %02x\n",
674 mldl_cfg->int_config);
675 MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync);
676 MPL_LOGD("mldl_cfg.full_scale = %02x\n",
677 mldl_cfg->full_scale);
678 MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf);
679 MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src);
680 MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider);
681 MPL_LOGD("mldl_cfg.dmp_enable = %02x\n",
682 mldl_cfg->dmp_enable);
683 MPL_LOGD("mldl_cfg.fifo_enable = %02x\n",
684 mldl_cfg->fifo_enable);
685 MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1);
686 MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2);
687 MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n",
688 mldl_cfg->offset_tc[0]);
689 MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n",
690 mldl_cfg->offset_tc[1]);
691 MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n",
692 mldl_cfg->offset_tc[2]);
693 MPL_LOGD("mldl_cfg.silicon_revision = %02x\n",
694 mldl_cfg->silicon_revision);
695 MPL_LOGD("mldl_cfg.product_id = %02x\n",
696 mldl_cfg->product_id);
697 MPL_LOGD("mldl_cfg.trim = %02x\n", mldl_cfg->trim);
698 MPL_LOGD("mldl_cfg.requested_sensors= %04lx\n",
699 mldl_cfg->requested_sensors);
700
701 if (mldl_cfg->accel) {
702 MPL_LOGD("slave_accel->suspend = %02x\n",
703 (int) mldl_cfg->accel->suspend);
704 MPL_LOGD("slave_accel->resume = %02x\n",
705 (int) mldl_cfg->accel->resume);
706 MPL_LOGD("slave_accel->read = %02x\n",
707 (int) mldl_cfg->accel->read);
708 MPL_LOGD("slave_accel->type = %02x\n",
709 mldl_cfg->accel->type);
710 MPL_LOGD("slave_accel->reg = %02x\n",
711 mldl_cfg->accel->reg);
712 MPL_LOGD("slave_accel->len = %02x\n",
713 mldl_cfg->accel->len);
714 MPL_LOGD("slave_accel->endian = %02x\n",
715 mldl_cfg->accel->endian);
716 MPL_LOGD("slave_accel->range.mantissa= %02lx\n",
717 mldl_cfg->accel->range.mantissa);
718 MPL_LOGD("slave_accel->range.fraction= %02lx\n",
719 mldl_cfg->accel->range.fraction);
720 } else {
721 MPL_LOGD("slave_accel = NULL\n");
722 }
723
724 if (mldl_cfg->compass) {
725 MPL_LOGD("slave_compass->suspend = %02x\n",
726 (int) mldl_cfg->compass->suspend);
727 MPL_LOGD("slave_compass->resume = %02x\n",
728 (int) mldl_cfg->compass->resume);
729 MPL_LOGD("slave_compass->read = %02x\n",
730 (int) mldl_cfg->compass->read);
731 MPL_LOGD("slave_compass->type = %02x\n",
732 mldl_cfg->compass->type);
733 MPL_LOGD("slave_compass->reg = %02x\n",
734 mldl_cfg->compass->reg);
735 MPL_LOGD("slave_compass->len = %02x\n",
736 mldl_cfg->compass->len);
737 MPL_LOGD("slave_compass->endian = %02x\n",
738 mldl_cfg->compass->endian);
739 MPL_LOGD("slave_compass->range.mantissa= %02lx\n",
740 mldl_cfg->compass->range.mantissa);
741 MPL_LOGD("slave_compass->range.fraction= %02lx\n",
742 mldl_cfg->compass->range.fraction);
743
744 } else {
745 MPL_LOGD("slave_compass = NULL\n");
746 }
747
748 if (mldl_cfg->pressure) {
749 MPL_LOGD("slave_pressure->suspend = %02x\n",
750 (int) mldl_cfg->pressure->suspend);
751 MPL_LOGD("slave_pressure->resume = %02x\n",
752 (int) mldl_cfg->pressure->resume);
753 MPL_LOGD("slave_pressure->read = %02x\n",
754 (int) mldl_cfg->pressure->read);
755 MPL_LOGD("slave_pressure->type = %02x\n",
756 mldl_cfg->pressure->type);
757 MPL_LOGD("slave_pressure->reg = %02x\n",
758 mldl_cfg->pressure->reg);
759 MPL_LOGD("slave_pressure->len = %02x\n",
760 mldl_cfg->pressure->len);
761 MPL_LOGD("slave_pressure->endian = %02x\n",
762 mldl_cfg->pressure->endian);
763 MPL_LOGD("slave_pressure->range.mantissa= %02lx\n",
764 mldl_cfg->pressure->range.mantissa);
765 MPL_LOGD("slave_pressure->range.fraction= %02lx\n",
766 mldl_cfg->pressure->range.fraction);
767
768 } else {
769 MPL_LOGD("slave_pressure = NULL\n");
770 }
771 MPL_LOGD("accel->get_slave_descr = %x\n",
772 (unsigned int) accel->get_slave_descr);
773 MPL_LOGD("accel->irq = %02x\n", accel->irq);
774 MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num);
775 MPL_LOGD("accel->bus = %02x\n", accel->bus);
776 MPL_LOGD("accel->address = %02x\n", accel->address);
777 MPL_LOGD("accel->orientation =\n"
778 " %2d %2d %2d\n"
779 " %2d %2d %2d\n"
780 " %2d %2d %2d\n",
781 accel->orientation[0], accel->orientation[1],
782 accel->orientation[2], accel->orientation[3],
783 accel->orientation[4], accel->orientation[5],
784 accel->orientation[6], accel->orientation[7],
785 accel->orientation[8]);
786 MPL_LOGD("compass->get_slave_descr = %x\n",
787 (unsigned int) compass->get_slave_descr);
788 MPL_LOGD("compass->irq = %02x\n", compass->irq);
789 MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num);
790 MPL_LOGD("compass->bus = %02x\n", compass->bus);
791 MPL_LOGD("compass->address = %02x\n", compass->address);
792 MPL_LOGD("compass->orientation =\n"
793 " %2d %2d %2d\n"
794 " %2d %2d %2d\n"
795 " %2d %2d %2d\n",
796 compass->orientation[0], compass->orientation[1],
797 compass->orientation[2], compass->orientation[3],
798 compass->orientation[4], compass->orientation[5],
799 compass->orientation[6], compass->orientation[7],
800 compass->orientation[8]);
801 MPL_LOGD("pressure->get_slave_descr = %x\n",
802 (unsigned int) pressure->get_slave_descr);
803 MPL_LOGD("pressure->irq = %02x\n", pressure->irq);
804 MPL_LOGD("pressure->adapt_num = %02x\n", pressure->adapt_num);
805 MPL_LOGD("pressure->bus = %02x\n", pressure->bus);
806 MPL_LOGD("pressure->address = %02x\n", pressure->address);
807 MPL_LOGD("pressure->orientation =\n"
808 " %2d %2d %2d\n"
809 " %2d %2d %2d\n"
810 " %2d %2d %2d\n",
811 pressure->orientation[0], pressure->orientation[1],
812 pressure->orientation[2], pressure->orientation[3],
813 pressure->orientation[4], pressure->orientation[5],
814 pressure->orientation[6], pressure->orientation[7],
815 pressure->orientation[8]);
816
817 MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config);
818 MPL_LOGD("pdata->level_shifter = %02x\n",
819 pdata->level_shifter);
820 MPL_LOGD("pdata->orientation =\n"
821 " %2d %2d %2d\n"
822 " %2d %2d %2d\n"
823 " %2d %2d %2d\n",
824 pdata->orientation[0], pdata->orientation[1],
825 pdata->orientation[2], pdata->orientation[3],
826 pdata->orientation[4], pdata->orientation[5],
827 pdata->orientation[6], pdata->orientation[7],
828 pdata->orientation[8]);
829
830 MPL_LOGD("Struct sizes: mldl_cfg: %d, "
831 "ext_slave_descr:%d, "
832 "mpu3050_platform_data:%d: RamOffset: %d\n",
833 sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr),
834 sizeof(struct mpu3050_platform_data),
835 offsetof(struct mldl_cfg, ram));
836}
837
838int mpu_set_slave(struct mldl_cfg *mldl_cfg,
839 void *gyro_handle,
840 struct ext_slave_descr *slave,
841 struct ext_slave_platform_data *slave_pdata)
842{
843 int result;
844 unsigned char reg;
845 unsigned char slave_reg;
846 unsigned char slave_len;
847 unsigned char slave_endian;
848 unsigned char slave_address;
849
850 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE);
851
852 if (NULL == slave || NULL == slave_pdata) {
853 slave_reg = 0;
854 slave_len = 0;
855 slave_endian = 0;
856 slave_address = 0;
857 } else {
858 slave_reg = slave->reg;
859 slave_len = slave->len;
860 slave_endian = slave->endian;
861 slave_address = slave_pdata->address;
862 }
863
864 /* Address */
865 result = MLSLSerialWriteSingle(gyro_handle,
866 mldl_cfg->addr,
867 MPUREG_AUX_SLV_ADDR,
868 slave_address);
869 ERROR_CHECK(result);
870 /* Register */
871 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
872 MPUREG_ACCEL_BURST_ADDR, 1,
873 &reg);
874 ERROR_CHECK(result);
875 reg = ((reg & 0x80) | slave_reg);
876 result = MLSLSerialWriteSingle(gyro_handle,
877 mldl_cfg->addr,
878 MPUREG_ACCEL_BURST_ADDR,
879 reg);
880 ERROR_CHECK(result);
881
882#ifdef M_HW
883 /* Length, byte swapping, grouping & enable */
884 if (slave_len > BITS_SLV_LENG) {
885 MPL_LOGW("Limiting slave burst read length to "
886 "the allowed maximum (15B, req. %d)\n",
887 slave_len);
888 slave_len = BITS_SLV_LENG;
889 }
890 reg = slave_len;
891 if (slave_endian == EXT_SLAVE_LITTLE_ENDIAN)
892 reg |= BIT_SLV_BYTE_SW;
893 reg |= BIT_SLV_GRP;
894 reg |= BIT_SLV_ENABLE;
895
896 result = MLSLSerialWriteSingle(gyro_handle,
897 mldl_cfg->addr,
898 MPUREG_I2C_SLV0_CTRL,
899 reg);
900#else
901 /* Length */
902 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
903 MPUREG_USER_CTRL, 1, &reg);
904 ERROR_CHECK(result);
905 reg = (reg & ~BIT_AUX_RD_LENG);
906 result = MLSLSerialWriteSingle(gyro_handle,
907 mldl_cfg->addr,
908 MPUREG_USER_CTRL, reg);
909 ERROR_CHECK(result);
910#endif
911
912 if (slave_address) {
913 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, FALSE);
914 ERROR_CHECK(result);
915 }
916 return result;
917}
918
919/**
920 * Check to see if the gyro was reset by testing a couple of registers known
921 * to change on reset.
922 *
923 * @param mldl_cfg mldl configuration structure
924 * @param gyro_handle handle used to communicate with the gyro
925 *
926 * @return ML_SUCCESS or non-zero error code
927 */
928static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle)
929{
930 int result = ML_SUCCESS;
931 unsigned char reg;
932
933 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
934 MPUREG_DMP_CFG_2, 1, &reg);
935 ERROR_CHECK(result);
936
937 if (mldl_cfg->dmp_cfg2 != reg)
938 return TRUE;
939
940 if (0 != mldl_cfg->dmp_cfg1)
941 return FALSE;
942
943 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
944 MPUREG_SMPLRT_DIV, 1, &reg);
945 ERROR_CHECK(result);
946 if (reg != mldl_cfg->divider)
947 return TRUE;
948
949 if (0 != mldl_cfg->divider)
950 return FALSE;
951
952 /* Inconclusive assume it was reset */
953 return TRUE;
954}
955
956static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle)
957{
958 int result;
959 int ii;
960 int jj;
961 unsigned char reg;
962 unsigned char regs[7];
963
964 /* Wake up the part */
965#ifdef M_HW
966 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, RESET,
967 WAKE_UP);
968 ERROR_CHECK(result);
969
970 /* Configure the MPU */
971 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1);
972 ERROR_CHECK(result);
973 /* setting int_config with the propert flag BIT_BYPASS_EN
974 should be done by the setup functions */
975 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
976 MPUREG_INT_PIN_CFG,
977 (mldl_cfg->pdata->int_config |
978 BIT_BYPASS_EN));
979 ERROR_CHECK(result);
980 /* temporary: masking out higher bits to avoid switching
981 intelligence */
982 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
983 MPUREG_INT_ENABLE,
984 (mldl_cfg->int_config));
985 ERROR_CHECK(result);
986#else
987 result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 0, 0,
988 mldl_cfg->gyro_power & BIT_STBY_XG,
989 mldl_cfg->gyro_power & BIT_STBY_YG,
990 mldl_cfg->gyro_power & BIT_STBY_ZG);
991
992 if (!mldl_cfg->gyro_needs_reset &&
993 !mpu_was_reset(mldl_cfg, gyro_handle)) {
994 return ML_SUCCESS;
995 }
996
997 result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle, 1, 0,
998 mldl_cfg->gyro_power & BIT_STBY_XG,
999 mldl_cfg->gyro_power & BIT_STBY_YG,
1000 mldl_cfg->gyro_power & BIT_STBY_ZG);
1001 ERROR_CHECK(result);
1002 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1003 MPUREG_INT_CFG,
1004 (mldl_cfg->int_config |
1005 mldl_cfg->pdata->int_config));
1006 ERROR_CHECK(result);
1007#endif
1008
1009 result = MLSLSerialRead(gyro_handle, mldl_cfg->addr,
1010 MPUREG_PWR_MGM, 1, &reg);
1011 ERROR_CHECK(result);
1012 reg &= ~BITS_CLKSEL;
1013 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1014 MPUREG_PWR_MGM,
1015 mldl_cfg->clk_src | reg);
1016 ERROR_CHECK(result);
1017 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1018 MPUREG_SMPLRT_DIV,
1019 mldl_cfg->divider);
1020 ERROR_CHECK(result);
1021
1022#ifdef M_HW
1023 reg = DLPF_FS_SYNC_VALUE(0, mldl_cfg->full_scale, 0);
1024 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1025 MPUREG_GYRO_CONFIG, reg);
1026 reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync, 0, mldl_cfg->lpf);
1027 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1028 MPUREG_CONFIG, reg);
1029#else
1030 reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync,
1031 mldl_cfg->full_scale, mldl_cfg->lpf);
1032 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1033 MPUREG_DLPF_FS_SYNC, reg);
1034#endif
1035 ERROR_CHECK(result);
1036 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1037 MPUREG_DMP_CFG_1,
1038 mldl_cfg->dmp_cfg1);
1039 ERROR_CHECK(result);
1040 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1041 MPUREG_DMP_CFG_2,
1042 mldl_cfg->dmp_cfg2);
1043 ERROR_CHECK(result);
1044
1045 /* Write and verify memory */
1046 for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) {
1047 unsigned char read[MPU_MEM_BANK_SIZE];
1048
1049 result = MLSLSerialWriteMem(gyro_handle,
1050 mldl_cfg->addr,
1051 ((ii << 8) | 0x00),
1052 MPU_MEM_BANK_SIZE,
1053 mldl_cfg->ram[ii]);
1054 ERROR_CHECK(result);
1055 result = MLSLSerialReadMem(gyro_handle, mldl_cfg->addr,
1056 ((ii << 8) | 0x00),
1057 MPU_MEM_BANK_SIZE, read);
1058 ERROR_CHECK(result);
1059
1060#ifdef M_HW
1061#define ML_SKIP_CHECK 38
1062#else
1063#define ML_SKIP_CHECK 20
1064#endif
1065 for (jj = 0; jj < MPU_MEM_BANK_SIZE; jj++) {
1066 /* skip the register memory locations */
1067 if (ii == 0 && jj < ML_SKIP_CHECK)
1068 continue;
1069 if (mldl_cfg->ram[ii][jj] != read[jj]) {
1070 result = ML_ERROR_SERIAL_WRITE;
1071 break;
1072 }
1073 }
1074 ERROR_CHECK(result);
1075 }
1076
1077 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1078 MPUREG_XG_OFFS_TC,
1079 mldl_cfg->offset_tc[0]);
1080 ERROR_CHECK(result);
1081 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1082 MPUREG_YG_OFFS_TC,
1083 mldl_cfg->offset_tc[1]);
1084 ERROR_CHECK(result);
1085 result = MLSLSerialWriteSingle(gyro_handle, mldl_cfg->addr,
1086 MPUREG_ZG_OFFS_TC,
1087 mldl_cfg->offset_tc[2]);
1088 ERROR_CHECK(result);
1089
1090 regs[0] = MPUREG_X_OFFS_USRH;
1091 for (ii = 0; ii < DIM(mldl_cfg->offset); ii++) {
1092 regs[1 + ii * 2] =
1093 (unsigned char)(mldl_cfg->offset[ii] >> 8)
1094 & 0xff;
1095 regs[1 + ii * 2 + 1] =
1096 (unsigned char)(mldl_cfg->offset[ii] & 0xff);
1097 }
1098 result = MLSLSerialWrite(gyro_handle, mldl_cfg->addr, 7, regs);
1099 ERROR_CHECK(result);
1100
1101 /* Configure slaves */
1102 result = MLDLSetLevelShifterBit(mldl_cfg, gyro_handle,
1103 mldl_cfg->pdata->level_shifter);
1104 ERROR_CHECK(result);
1105 return result;
1106}
1107/*******************************************************************************
1108 *******************************************************************************
1109 * Exported functions
1110 *******************************************************************************
1111 ******************************************************************************/
1112
1113/**
1114 * Initializes the pdata structure to defaults.
1115 *
1116 * Opens the device to read silicon revision, product id and whoami.
1117 *
1118 * @param mldl_cfg
1119 * The internal device configuration data structure.
1120 * @param mlsl_handle
1121 * The serial communication handle.
1122 *
1123 * @return ML_SUCCESS if silicon revision, product id and woami are supported
1124 * by this software.
1125 */
1126int mpu3050_open(struct mldl_cfg *mldl_cfg,
1127 void *mlsl_handle,
1128 void *accel_handle,
1129 void *compass_handle,
1130 void *pressure_handle)
1131{
1132 int result;
1133 /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
1134 mldl_cfg->ignore_system_suspend = FALSE;
1135 mldl_cfg->int_config = BIT_INT_ANYRD_2CLEAR | BIT_DMP_INT_EN;
1136 mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
1137 mldl_cfg->lpf = MPU_FILTER_42HZ;
1138 mldl_cfg->full_scale = MPU_FS_2000DPS;
1139 mldl_cfg->divider = 4;
1140 mldl_cfg->dmp_enable = 1;
1141 mldl_cfg->fifo_enable = 1;
1142 mldl_cfg->ext_sync = 0;
1143 mldl_cfg->dmp_cfg1 = 0;
1144 mldl_cfg->dmp_cfg2 = 0;
1145 mldl_cfg->gyro_power = 0;
1146 mldl_cfg->gyro_is_bypassed = TRUE;
1147 mldl_cfg->dmp_is_running = FALSE;
1148 mldl_cfg->gyro_is_suspended = TRUE;
1149 mldl_cfg->accel_is_suspended = TRUE;
1150 mldl_cfg->compass_is_suspended = TRUE;
1151 mldl_cfg->pressure_is_suspended = TRUE;
1152 mldl_cfg->gyro_needs_reset = FALSE;
1153 if (mldl_cfg->addr == 0) {
1154#ifdef __KERNEL__
1155 return ML_ERROR_INVALID_PARAMETER;
1156#else
1157 mldl_cfg->addr = 0x68;
1158#endif
1159 }
1160
1161 /*
1162 * Reset,
1163 * Take the DMP out of sleep, and
1164 * read the product_id, sillicon rev and whoami
1165 */
1166#ifdef M_HW
1167 result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle,
1168 RESET, WAKE_UP);
1169#else
1170 result = MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, RESET, 0, 0, 0, 0);
1171#endif
1172 ERROR_CHECK(result);
1173
1174 result = MLDLGetSiliconRev(mldl_cfg, mlsl_handle);
1175 ERROR_CHECK(result);
1176#ifndef M_HW
1177 result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
1178 MPUREG_PRODUCT_ID, 1,
1179 &mldl_cfg->product_id);
1180 ERROR_CHECK(result);
1181#endif
1182
1183 /* Get the factory temperature compensation offsets */
1184 result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
1185 MPUREG_XG_OFFS_TC, 1,
1186 &mldl_cfg->offset_tc[0]);
1187 ERROR_CHECK(result);
1188 result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
1189 MPUREG_YG_OFFS_TC, 1,
1190 &mldl_cfg->offset_tc[1]);
1191 ERROR_CHECK(result);
1192 result = MLSLSerialRead(mlsl_handle, mldl_cfg->addr,
1193 MPUREG_ZG_OFFS_TC, 1,
1194 &mldl_cfg->offset_tc[2]);
1195 ERROR_CHECK(result);
1196
1197 /* Configure the MPU */
1198#ifdef M_HW
1199 result = mpu60xx_pwr_mgmt(mldl_cfg, mlsl_handle,
1200 FALSE, SLEEP);
1201#else
1202 result =
1203 MLDLPowerMgmtMPU(mldl_cfg, mlsl_handle, 0, SLEEP, 0, 0, 0);
1204#endif
1205 ERROR_CHECK(result);
1206
1207 if (mldl_cfg->accel && mldl_cfg->accel->init) {
1208 result = mldl_cfg->accel->init(accel_handle,
1209 mldl_cfg->accel,
1210 &mldl_cfg->pdata->accel);
1211 ERROR_CHECK(result);
1212 }
1213
1214 if (mldl_cfg->compass && mldl_cfg->compass->init) {
1215 result = mldl_cfg->compass->init(compass_handle,
1216 mldl_cfg->compass,
1217 &mldl_cfg->pdata->compass);
1218 if (ML_SUCCESS != result) {
1219 MPL_LOGE("mldl_cfg->compass->init returned %d\n",
1220 result);
1221 goto out_accel;
1222 }
1223 }
1224 if (mldl_cfg->pressure && mldl_cfg->pressure->init) {
1225 result = mldl_cfg->pressure->init(pressure_handle,
1226 mldl_cfg->pressure,
1227 &mldl_cfg->pdata->pressure);
1228 if (ML_SUCCESS != result) {
1229 MPL_LOGE("mldl_cfg->pressure->init returned %d\n",
1230 result);
1231 goto out_compass;
1232 }
1233 }
1234
1235 mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO;
1236 if (mldl_cfg->accel && mldl_cfg->accel->resume)
1237 mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL;
1238
1239 if (mldl_cfg->compass && mldl_cfg->compass->resume)
1240 mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS;
1241
1242 if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
1243 mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE;
1244
1245 return result;
1246
1247out_compass:
1248 if (mldl_cfg->compass->init)
1249 mldl_cfg->compass->exit(compass_handle,
1250 mldl_cfg->compass,
1251 &mldl_cfg->pdata->compass);
1252out_accel:
1253 if (mldl_cfg->accel->init)
1254 mldl_cfg->accel->exit(accel_handle,
1255 mldl_cfg->accel,
1256 &mldl_cfg->pdata->accel);
1257 return result;
1258
1259}
1260
1261/**
1262 * Close the mpu3050 interface
1263 *
1264 * @param mldl_cfg pointer to the configuration structure
1265 * @param mlsl_handle pointer to the serial layer handle
1266 *
1267 * @return ML_SUCCESS or non-zero error code
1268 */
1269int mpu3050_close(struct mldl_cfg *mldl_cfg,
1270 void *mlsl_handle,
1271 void *accel_handle,
1272 void *compass_handle,
1273 void *pressure_handle)
1274{
1275 int result = ML_SUCCESS;
1276 int ret_result = ML_SUCCESS;
1277
1278 if (mldl_cfg->accel && mldl_cfg->accel->exit) {
1279 result = mldl_cfg->accel->exit(accel_handle,
1280 mldl_cfg->accel,
1281 &mldl_cfg->pdata->accel);
1282 if (ML_SUCCESS != result)
1283 MPL_LOGE("Accel exit failed %d\n", result);
1284 ret_result = result;
1285 }
1286 if (ML_SUCCESS == ret_result)
1287 ret_result = result;
1288
1289 if (mldl_cfg->compass && mldl_cfg->compass->exit) {
1290 result = mldl_cfg->compass->exit(compass_handle,
1291 mldl_cfg->compass,
1292 &mldl_cfg->pdata->compass);
1293 if (ML_SUCCESS != result)
1294 MPL_LOGE("Compass exit failed %d\n", result);
1295 }
1296 if (ML_SUCCESS == ret_result)
1297 ret_result = result;
1298
1299 if (mldl_cfg->pressure && mldl_cfg->pressure->exit) {
1300 result = mldl_cfg->pressure->exit(pressure_handle,
1301 mldl_cfg->pressure,
1302 &mldl_cfg->pdata->pressure);
1303 if (ML_SUCCESS != result)
1304 MPL_LOGE("Pressure exit failed %d\n", result);
1305 }
1306 if (ML_SUCCESS == ret_result)
1307 ret_result = result;
1308
1309 return ret_result;
1310}
1311
1312/**
1313 * @brief resume the MPU3050 device and all the other sensor
1314 * devices from their low power state.
1315 *
1316 * @param mldl_cfg
1317 * pointer to the configuration structure
1318 * @param gyro_handle
1319 * the main file handle to the MPU3050 device.
1320 * @param accel_handle
1321 * an handle to the accelerometer device, if sitting
1322 * onto a separate bus. Can match mlsl_handle if
1323 * the accelerometer device operates on the same
1324 * primary bus of MPU.
1325 * @param compass_handle
1326 * an handle to the compass device, if sitting
1327 * onto a separate bus. Can match mlsl_handle if
1328 * the compass device operates on the same
1329 * primary bus of MPU.
1330 * @param pressure_handle
1331 * an handle to the pressure sensor device, if sitting
1332 * onto a separate bus. Can match mlsl_handle if
1333 * the pressure sensor device operates on the same
1334 * primary bus of MPU.
1335 * @param resume_gyro
1336 * whether resuming the gyroscope device is
1337 * actually needed (if the device supports low power
1338 * mode of some sort).
1339 * @param resume_accel
1340 * whether resuming the accelerometer device is
1341 * actually needed (if the device supports low power
1342 * mode of some sort).
1343 * @param resume_compass
1344 * whether resuming the compass device is
1345 * actually needed (if the device supports low power
1346 * mode of some sort).
1347 * @param resume_pressure
1348 * whether resuming the pressure sensor device is
1349 * actually needed (if the device supports low power
1350 * mode of some sort).
1351 * @return ML_SUCCESS or a non-zero error code.
1352 */
1353int mpu3050_resume(struct mldl_cfg *mldl_cfg,
1354 void *gyro_handle,
1355 void *accel_handle,
1356 void *compass_handle,
1357 void *pressure_handle,
1358 bool resume_gyro,
1359 bool resume_accel,
1360 bool resume_compass,
1361 bool resume_pressure)
1362{
1363 int result = ML_SUCCESS;
1364
1365#ifdef CONFIG_MPU_SENSORS_DEBUG
1366 mpu_print_cfg(mldl_cfg);
1367#endif
1368
1369 if (resume_accel &&
1370 ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume)))
1371 return ML_ERROR_INVALID_PARAMETER;
1372 if (resume_compass &&
1373 ((!mldl_cfg->compass) || (!mldl_cfg->compass->resume)))
1374 return ML_ERROR_INVALID_PARAMETER;
1375 if (resume_pressure &&
1376 ((!mldl_cfg->pressure) || (!mldl_cfg->pressure->resume)))
1377 return ML_ERROR_INVALID_PARAMETER;
1378
1379 if (resume_gyro && mldl_cfg->gyro_is_suspended) {
1380 result = gyro_resume(mldl_cfg, gyro_handle);
1381 ERROR_CHECK(result);
1382 }
1383
1384 if (resume_accel && mldl_cfg->accel_is_suspended) {
1385 if (!mldl_cfg->gyro_is_suspended &&
1386 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
1387 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE);
1388 ERROR_CHECK(result);
1389 }
1390 result = mldl_cfg->accel->resume(accel_handle,
1391 mldl_cfg->accel,
1392 &mldl_cfg->pdata->accel);
1393 ERROR_CHECK(result);
1394 mldl_cfg->accel_is_suspended = FALSE;
1395 }
1396
1397 if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->accel_is_suspended &&
1398 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
1399 result = mpu_set_slave(mldl_cfg,
1400 gyro_handle,
1401 mldl_cfg->accel,
1402 &mldl_cfg->pdata->accel);
1403 ERROR_CHECK(result);
1404 }
1405
1406 if (resume_compass && mldl_cfg->compass_is_suspended) {
1407 if (!mldl_cfg->gyro_is_suspended &&
1408 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
1409 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE);
1410 ERROR_CHECK(result);
1411 }
1412 result = mldl_cfg->compass->resume(compass_handle,
1413 mldl_cfg->compass,
1414 &mldl_cfg->pdata->
1415 compass);
1416 ERROR_CHECK(result);
1417 mldl_cfg->compass_is_suspended = FALSE;
1418 }
1419
1420 if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->compass_is_suspended &&
1421 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
1422 result = mpu_set_slave(mldl_cfg,
1423 gyro_handle,
1424 mldl_cfg->compass,
1425 &mldl_cfg->pdata->compass);
1426 ERROR_CHECK(result);
1427 }
1428
1429 if (resume_pressure && mldl_cfg->pressure_is_suspended) {
1430 if (!mldl_cfg->gyro_is_suspended &&
1431 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
1432 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, TRUE);
1433 ERROR_CHECK(result);
1434 }
1435 result = mldl_cfg->pressure->resume(pressure_handle,
1436 mldl_cfg->pressure,
1437 &mldl_cfg->pdata->
1438 pressure);
1439 ERROR_CHECK(result);
1440 mldl_cfg->pressure_is_suspended = FALSE;
1441 }
1442
1443 if (!mldl_cfg->gyro_is_suspended && !mldl_cfg->pressure_is_suspended &&
1444 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
1445 result = mpu_set_slave(mldl_cfg,
1446 gyro_handle,
1447 mldl_cfg->pressure,
1448 &mldl_cfg->pdata->pressure);
1449 ERROR_CHECK(result);
1450 }
1451
1452 /* Now start */
1453 if (resume_gyro) {
1454 result = dmp_start(mldl_cfg, gyro_handle);
1455 ERROR_CHECK(result);
1456 }
1457
1458 return result;
1459}
1460
1461/**
1462 * @brief suspend the MPU3050 device and all the other sensor
1463 * devices into their low power state.
1464 * @param gyro_handle
1465 * the main file handle to the MPU3050 device.
1466 * @param accel_handle
1467 * an handle to the accelerometer device, if sitting
1468 * onto a separate bus. Can match gyro_handle if
1469 * the accelerometer device operates on the same
1470 * primary bus of MPU.
1471 * @param compass_handle
1472 * an handle to the compass device, if sitting
1473 * onto a separate bus. Can match gyro_handle if
1474 * the compass device operates on the same
1475 * primary bus of MPU.
1476 * @param pressure_handle
1477 * an handle to the pressure sensor device, if sitting
1478 * onto a separate bus. Can match gyro_handle if
1479 * the pressure sensor device operates on the same
1480 * primary bus of MPU.
1481 * @param accel
1482 * whether suspending the accelerometer device is
1483 * actually needed (if the device supports low power
1484 * mode of some sort).
1485 * @param compass
1486 * whether suspending the compass device is
1487 * actually needed (if the device supports low power
1488 * mode of some sort).
1489 * @param pressure
1490 * whether suspending the pressure sensor device is
1491 * actually needed (if the device supports low power
1492 * mode of some sort).
1493 * @return ML_SUCCESS or a non-zero error code.
1494 */
1495int mpu3050_suspend(struct mldl_cfg *mldl_cfg,
1496 void *gyro_handle,
1497 void *accel_handle,
1498 void *compass_handle,
1499 void *pressure_handle,
1500 bool suspend_gyro,
1501 bool suspend_accel,
1502 bool suspend_compass,
1503 bool suspend_pressure)
1504{
1505 int result = ML_SUCCESS;
1506
1507 if (suspend_gyro && !mldl_cfg->gyro_is_suspended) {
1508#ifdef M_HW
1509 return ML_SUCCESS;
1510 /* This puts the bus into bypass mode */
1511 result = MLDLSetI2CBypass(mldl_cfg, gyro_handle, 1);
1512 ERROR_CHECK(result);
1513 result = mpu60xx_pwr_mgmt(mldl_cfg, gyro_handle, 0, SLEEP);
1514#else
1515 result = MLDLPowerMgmtMPU(mldl_cfg, gyro_handle,
1516 0, SLEEP, 0, 0, 0);
1517#endif
1518 ERROR_CHECK(result);
1519 }
1520
1521 if (!mldl_cfg->accel_is_suspended && suspend_accel &&
1522 mldl_cfg->accel && mldl_cfg->accel->suspend) {
1523 if (!mldl_cfg->gyro_is_suspended &&
1524 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
1525 result = mpu_set_slave(mldl_cfg, gyro_handle,
1526 NULL, NULL);
1527 ERROR_CHECK(result);
1528 }
1529 result = mldl_cfg->accel->suspend(accel_handle,
1530 mldl_cfg->accel,
1531 &mldl_cfg->pdata->accel);
1532 ERROR_CHECK(result);
1533 mldl_cfg->accel_is_suspended = TRUE;
1534 }
1535
1536 if (!mldl_cfg->compass_is_suspended && suspend_compass &&
1537 mldl_cfg->compass && mldl_cfg->compass->suspend) {
1538 if (!mldl_cfg->gyro_is_suspended &&
1539 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
1540 result = mpu_set_slave(mldl_cfg, gyro_handle,
1541 NULL, NULL);
1542 ERROR_CHECK(result);
1543 }
1544 result = mldl_cfg->compass->suspend(compass_handle,
1545 mldl_cfg->compass,
1546 &mldl_cfg->
1547 pdata->compass);
1548 ERROR_CHECK(result);
1549 mldl_cfg->compass_is_suspended = TRUE;
1550 }
1551
1552 if (!mldl_cfg->pressure_is_suspended && suspend_pressure &&
1553 mldl_cfg->pressure && mldl_cfg->pressure->suspend) {
1554 if (!mldl_cfg->gyro_is_suspended &&
1555 EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
1556 result = mpu_set_slave(mldl_cfg, gyro_handle,
1557 NULL, NULL);
1558 ERROR_CHECK(result);
1559 }
1560 result = mldl_cfg->pressure->suspend(pressure_handle,
1561 mldl_cfg->pressure,
1562 &mldl_cfg->
1563 pdata->pressure);
1564 ERROR_CHECK(result);
1565 mldl_cfg->pressure_is_suspended = TRUE;
1566 }
1567 return result;
1568}
1569
1570
1571/**
1572 * @brief read raw sensor data from the accelerometer device
1573 * in use.
1574 * @param mldl_cfg
1575 * A pointer to the struct mldl_cfg data structure.
1576 * @param accel_handle
1577 * The handle to the device the accelerometer is connected to.
1578 * @param data
1579 * a buffer to store the raw sensor data.
1580 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
1581 */
1582int mpu3050_read_accel(struct mldl_cfg *mldl_cfg,
1583 void *accel_handle, unsigned char *data)
1584{
1585 if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->read)
1586 if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus)
1587 && (!mldl_cfg->gyro_is_bypassed))
1588 return ML_ERROR_FEATURE_NOT_ENABLED;
1589 else
1590 return mldl_cfg->accel->read(accel_handle,
1591 mldl_cfg->accel,
1592 &mldl_cfg->pdata->accel,
1593 data);
1594 else
1595 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1596}
1597
1598/**
1599 * @brief read raw sensor data from the compass device
1600 * in use.
1601 * @param mldl_cfg
1602 * A pointer to the struct mldl_cfg data structure.
1603 * @param compass_handle
1604 * The handle to the device the compass is connected to.
1605 * @param data
1606 * a buffer to store the raw sensor data.
1607 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
1608 */
1609int mpu3050_read_compass(struct mldl_cfg *mldl_cfg,
1610 void *compass_handle, unsigned char *data)
1611{
1612 if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->read)
1613 if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus)
1614 && (!mldl_cfg->gyro_is_bypassed))
1615 return ML_ERROR_FEATURE_NOT_ENABLED;
1616 else
1617 return mldl_cfg->compass->read(compass_handle,
1618 mldl_cfg->compass,
1619 &mldl_cfg->pdata->compass,
1620 data);
1621 else
1622 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1623}
1624
1625/**
1626 * @brief read raw sensor data from the pressure device
1627 * in use.
1628 * @param mldl_cfg
1629 * A pointer to the struct mldl_cfg data structure.
1630 * @param pressure_handle
1631 * The handle to the device the pressure sensor is connected to.
1632 * @param data
1633 * a buffer to store the raw sensor data.
1634 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
1635 */
1636int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg,
1637 void *pressure_handle, unsigned char *data)
1638{
1639 if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->read)
1640 if ((EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus)
1641 && (!mldl_cfg->gyro_is_bypassed))
1642 return ML_ERROR_FEATURE_NOT_ENABLED;
1643 else
1644 return mldl_cfg->pressure->read(
1645 pressure_handle,
1646 mldl_cfg->pressure,
1647 &mldl_cfg->pdata->pressure,
1648 data);
1649 else
1650 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1651}
1652
1653int mpu3050_config_accel(struct mldl_cfg *mldl_cfg,
1654 void *accel_handle,
1655 struct ext_slave_config *data)
1656{
1657 if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->config)
1658 return mldl_cfg->accel->config(accel_handle,
1659 mldl_cfg->accel,
1660 &mldl_cfg->pdata->accel,
1661 data);
1662 else
1663 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1664
1665}
1666
1667int mpu3050_config_compass(struct mldl_cfg *mldl_cfg,
1668 void *compass_handle,
1669 struct ext_slave_config *data)
1670{
1671 if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->config)
1672 return mldl_cfg->compass->config(compass_handle,
1673 mldl_cfg->compass,
1674 &mldl_cfg->pdata->compass,
1675 data);
1676 else
1677 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1678
1679}
1680
1681int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg,
1682 void *pressure_handle,
1683 struct ext_slave_config *data)
1684{
1685 if (NULL != mldl_cfg->pressure && NULL != mldl_cfg->pressure->config)
1686 return mldl_cfg->pressure->config(pressure_handle,
1687 mldl_cfg->pressure,
1688 &mldl_cfg->pdata->pressure,
1689 data);
1690 else
1691 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1692}
1693
1694int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg,
1695 void *accel_handle,
1696 struct ext_slave_config *data)
1697{
1698 if (NULL != mldl_cfg->accel && NULL != mldl_cfg->accel->get_config)
1699 return mldl_cfg->accel->get_config(accel_handle,
1700 mldl_cfg->accel,
1701 &mldl_cfg->pdata->accel,
1702 data);
1703 else
1704 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1705
1706}
1707
1708int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg,
1709 void *compass_handle,
1710 struct ext_slave_config *data)
1711{
1712 if (NULL != mldl_cfg->compass && NULL != mldl_cfg->compass->get_config)
1713 return mldl_cfg->compass->get_config(compass_handle,
1714 mldl_cfg->compass,
1715 &mldl_cfg->pdata->compass,
1716 data);
1717 else
1718 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1719
1720}
1721
1722int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg,
1723 void *pressure_handle,
1724 struct ext_slave_config *data)
1725{
1726 if (NULL != mldl_cfg->pressure &&
1727 NULL != mldl_cfg->pressure->get_config)
1728 return mldl_cfg->pressure->get_config(pressure_handle,
1729 mldl_cfg->pressure,
1730 &mldl_cfg->pdata->pressure,
1731 data);
1732 else
1733 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
1734}
1735
1736
1737/**
1738 *@}
1739 */
diff --git a/drivers/misc/mpu3050/mldl_cfg.h b/drivers/misc/mpu3050/mldl_cfg.h
new file mode 100644
index 00000000000..ad6a211c5d8
--- /dev/null
+++ b/drivers/misc/mpu3050/mldl_cfg.h
@@ -0,0 +1,199 @@
1/*
2 $License:
3 Copyright (C) 2010 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/* ------------------ */
32/* - Include Files. - */
33/* ------------------ */
34
35#include "mlsl.h"
36#include "mpu.h"
37
38/* --------------------- */
39/* - Defines. - */
40/* --------------------- */
41
42 /*************************************************************************/
43 /* Sensors */
44 /*************************************************************************/
45
46#define ML_X_GYRO (0x0001)
47#define ML_Y_GYRO (0x0002)
48#define ML_Z_GYRO (0x0004)
49#define ML_DMP_PROCESSOR (0x0008)
50
51#define ML_X_ACCEL (0x0010)
52#define ML_Y_ACCEL (0x0020)
53#define ML_Z_ACCEL (0x0040)
54
55#define ML_X_COMPASS (0x0080)
56#define ML_Y_COMPASS (0x0100)
57#define ML_Z_COMPASS (0x0200)
58
59#define ML_X_PRESSURE (0x0300)
60#define ML_Y_PRESSURE (0x0800)
61#define ML_Z_PRESSURE (0x1000)
62
63#define ML_TEMPERATURE (0x2000)
64#define ML_TIME (0x4000)
65
66#define ML_THREE_AXIS_GYRO (0x000F)
67#define ML_THREE_AXIS_ACCEL (0x0070)
68#define ML_THREE_AXIS_COMPASS (0x0380)
69#define ML_THREE_AXIS_PRESSURE (0x1C00)
70
71#define ML_FIVE_AXIS (0x007B)
72#define ML_SIX_AXIS_GYRO_ACCEL (0x007F)
73#define ML_SIX_AXIS_ACCEL_COMPASS (0x03F0)
74#define ML_NINE_AXIS (0x03FF)
75#define ML_ALL_SENSORS (0x7FFF)
76
77#define SAMPLING_RATE_HZ(mldl_cfg) \
78 ((((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \
79 ? (8000) \
80 : (1000)) \
81 / ((mldl_cfg)->divider + 1))
82
83#define SAMPLING_PERIOD_US(mldl_cfg) \
84 ((1000000L * ((mldl_cfg)->divider + 1)) / \
85 (((((mldl_cfg)->lpf) == 0) || (((mldl_cfg)->lpf) == 7)) \
86 ? (8000) \
87 : (1000)))
88/* --------------------- */
89/* - Variables. - */
90/* --------------------- */
91
92/* Platform data for the MPU */
93struct mldl_cfg {
94 /* MPU related configuration */
95 unsigned long requested_sensors;
96 unsigned char ignore_system_suspend;
97 unsigned char addr;
98 unsigned char int_config;
99 unsigned char ext_sync;
100 unsigned char full_scale;
101 unsigned char lpf;
102 unsigned char clk_src;
103 unsigned char divider;
104 unsigned char dmp_enable;
105 unsigned char fifo_enable;
106 unsigned char dmp_cfg1;
107 unsigned char dmp_cfg2;
108 unsigned char gyro_power;
109 unsigned char offset_tc[MPU_NUM_AXES];
110 unsigned short offset[MPU_NUM_AXES];
111 unsigned char ram[MPU_MEM_NUM_RAM_BANKS][MPU_MEM_BANK_SIZE];
112
113 /* MPU Related stored status and info */
114 unsigned char silicon_revision;
115 unsigned char product_id;
116 unsigned short trim;
117
118 /* Driver/Kernel related state information */
119 int gyro_is_bypassed;
120 int dmp_is_running;
121 int gyro_is_suspended;
122 int accel_is_suspended;
123 int compass_is_suspended;
124 int pressure_is_suspended;
125 int gyro_needs_reset;
126
127 /* Slave related information */
128 struct ext_slave_descr *accel;
129 struct ext_slave_descr *compass;
130 struct ext_slave_descr *pressure;
131
132 /* Platform Data */
133 struct mpu3050_platform_data *pdata;
134};
135
136
137int mpu3050_open(struct mldl_cfg *mldl_cfg,
138 void *mlsl_handle,
139 void *accel_handle,
140 void *compass_handle,
141 void *pressure_handle);
142int mpu3050_close(struct mldl_cfg *mldl_cfg,
143 void *mlsl_handle,
144 void *accel_handle,
145 void *compass_handle,
146 void *pressure_handle);
147int mpu3050_resume(struct mldl_cfg *mldl_cfg,
148 void *gyro_handle,
149 void *accel_handle,
150 void *compass_handle,
151 void *pressure_handle,
152 bool resume_gyro,
153 bool resume_accel,
154 bool resume_compass,
155 bool resume_pressure);
156int mpu3050_suspend(struct mldl_cfg *mldl_cfg,
157 void *gyro_handle,
158 void *accel_handle,
159 void *compass_handle,
160 void *pressure_handle,
161 bool suspend_gyro,
162 bool suspend_accel,
163 bool suspend_compass,
164 bool suspend_pressure);
165int mpu3050_read_accel(struct mldl_cfg *mldl_cfg,
166 void *accel_handle,
167 unsigned char *data);
168int mpu3050_read_compass(struct mldl_cfg *mldl_cfg,
169 void *compass_handle,
170 unsigned char *data);
171int mpu3050_read_pressure(struct mldl_cfg *mldl_cfg, void *mlsl_handle,
172 unsigned char *data);
173
174int mpu3050_config_accel(struct mldl_cfg *mldl_cfg,
175 void *accel_handle,
176 struct ext_slave_config *data);
177int mpu3050_config_compass(struct mldl_cfg *mldl_cfg,
178 void *compass_handle,
179 struct ext_slave_config *data);
180int mpu3050_config_pressure(struct mldl_cfg *mldl_cfg,
181 void *pressure_handle,
182 struct ext_slave_config *data);
183
184int mpu3050_get_config_accel(struct mldl_cfg *mldl_cfg,
185 void *accel_handle,
186 struct ext_slave_config *data);
187int mpu3050_get_config_compass(struct mldl_cfg *mldl_cfg,
188 void *compass_handle,
189 struct ext_slave_config *data);
190int mpu3050_get_config_pressure(struct mldl_cfg *mldl_cfg,
191 void *pressure_handle,
192 struct ext_slave_config *data);
193
194
195#endif /* __MLDL_CFG_H__ */
196
197/**
198 *@}
199 */
diff --git a/drivers/misc/mpu3050/mlos-kernel.c b/drivers/misc/mpu3050/mlos-kernel.c
new file mode 100644
index 00000000000..ced9ba4f6f3
--- /dev/null
+++ b/drivers/misc/mpu3050/mlos-kernel.c
@@ -0,0 +1,89 @@
1/*
2 $License:
3 Copyright (C) 2010 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 * @defgroup
21 * @brief
22 *
23 * @{
24 * @file mlos-kernel.c
25 * @brief
26 *
27 *
28 */
29
30#include "mlos.h"
31#include <linux/delay.h>
32#include <linux/slab.h>
33
34void *MLOSMalloc(unsigned int numBytes)
35{
36 return kmalloc(numBytes, GFP_KERNEL);
37}
38
39tMLError MLOSFree(void *ptr)
40{
41 kfree(ptr);
42 return ML_SUCCESS;
43}
44
45tMLError MLOSCreateMutex(HANDLE *mutex)
46{
47 /* @todo implement if needed */
48 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
49}
50
51tMLError MLOSLockMutex(HANDLE mutex)
52{
53 /* @todo implement if needed */
54 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
55}
56
57tMLError MLOSUnlockMutex(HANDLE mutex)
58{
59 /* @todo implement if needed */
60 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
61}
62
63tMLError MLOSDestroyMutex(HANDLE handle)
64{
65 /* @todo implement if needed */
66 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
67}
68
69FILE *MLOSFOpen(char *filename)
70{
71 /* @todo implement if needed */
72 return NULL;
73}
74
75void MLOSFClose(FILE *fp)
76{
77 /* @todo implement if needed */
78}
79
80void MLOSSleep(int mSecs)
81{
82 msleep(mSecs);
83}
84
85unsigned long MLOSGetTickCount(void)
86{
87 /* @todo implement if needed */
88 return ML_ERROR_FEATURE_NOT_IMPLEMENTED;
89}
diff --git a/drivers/misc/mpu3050/mlos.h b/drivers/misc/mpu3050/mlos.h
new file mode 100644
index 00000000000..4ebb86c9fa5
--- /dev/null
+++ b/drivers/misc/mpu3050/mlos.h
@@ -0,0 +1,73 @@
1/*
2 $License:
3 Copyright (C) 2010 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 _MLOS_H
21#define _MLOS_H
22
23#ifndef __KERNEL__
24#include <stdio.h>
25#endif
26
27#include "mltypes.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33 /* ------------ */
34 /* - Defines. - */
35 /* ------------ */
36
37 /* - MLOSCreateFile defines. - */
38
39#define MLOS_GENERIC_READ ((unsigned int)0x80000000)
40#define MLOS_GENERIC_WRITE ((unsigned int)0x40000000)
41#define MLOS_FILE_SHARE_READ ((unsigned int)0x00000001)
42#define MLOS_FILE_SHARE_WRITE ((unsigned int)0x00000002)
43#define MLOS_OPEN_EXISTING ((unsigned int)0x00000003)
44
45 /* ---------- */
46 /* - Enums. - */
47 /* ---------- */
48
49 /* --------------- */
50 /* - Structures. - */
51 /* --------------- */
52
53 /* --------------------- */
54 /* - Function p-types. - */
55 /* --------------------- */
56
57 void *MLOSMalloc(unsigned int numBytes);
58 tMLError MLOSFree(void *ptr);
59 tMLError MLOSCreateMutex(HANDLE *mutex);
60 tMLError MLOSLockMutex(HANDLE mutex);
61 tMLError MLOSUnlockMutex(HANDLE mutex);
62 FILE *MLOSFOpen(char *filename);
63 void MLOSFClose(FILE *fp);
64
65 tMLError MLOSDestroyMutex(HANDLE handle);
66
67 void MLOSSleep(int mSecs);
68 unsigned long MLOSGetTickCount(void);
69
70#ifdef __cplusplus
71}
72#endif
73#endif /* _MLOS_H */
diff --git a/drivers/misc/mpu3050/mlsl-kernel.c b/drivers/misc/mpu3050/mlsl-kernel.c
new file mode 100644
index 00000000000..cb1605131cb
--- /dev/null
+++ b/drivers/misc/mpu3050/mlsl-kernel.c
@@ -0,0 +1,331 @@
1/*
2 $License:
3 Copyright (C) 2010 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 "mpu-i2c.h"
22
23/* ------------ */
24/* - Defines. - */
25/* ------------ */
26
27/* ---------------------- */
28/* - Types definitions. - */
29/* ---------------------- */
30
31/* --------------------- */
32/* - Function p-types. - */
33/* --------------------- */
34
35/**
36 * @brief used to open the I2C or SPI serial port.
37 * This port is used to send and receive data to the MPU device.
38 * @param portNum
39 * The COM port number associated with the device in use.
40 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
41 */
42tMLError MLSLSerialOpen(char const *port, void **sl_handle)
43{
44 return ML_SUCCESS;
45}
46
47/**
48 * @brief used to reset any buffering the driver may be doing
49 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
50 */
51tMLError MLSLSerialReset(void *sl_handle)
52{
53 return ML_SUCCESS;
54}
55
56/**
57 * @brief used to close the I2C or SPI serial port.
58 * This port is used to send and receive data to the MPU device.
59 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
60 */
61tMLError MLSLSerialClose(void *sl_handle)
62{
63 return ML_SUCCESS;
64}
65
66/**
67 * @brief used to read a single byte of data.
68 * This should be sent by I2C or SPI.
69 *
70 * @param slaveAddr I2C slave address of device.
71 * @param registerAddr Register address to read.
72 * @param data Single byte of data to read.
73 *
74 * @return ML_SUCCESS if the command is successful, an error code otherwise.
75 */
76tMLError MLSLSerialWriteSingle(void *sl_handle,
77 unsigned char slaveAddr,
78 unsigned char registerAddr,
79 unsigned char data)
80{
81 return sensor_i2c_write_register((struct i2c_adapter *) sl_handle,
82 slaveAddr, registerAddr, data);
83}
84
85
86/**
87 * @brief used to write multiple bytes of data from registers.
88 * This should be sent by I2C.
89 *
90 * @param slaveAddr I2C slave address of device.
91 * @param registerAddr Register address to write.
92 * @param length Length of burst of data.
93 * @param data Pointer to block of data.
94 *
95 * @return ML_SUCCESS if successful, a non-zero error code otherwise.
96 */
97tMLError MLSLSerialWrite(void *sl_handle,
98 unsigned char slaveAddr,
99 unsigned short length,
100 unsigned char const *data)
101{
102 tMLError result;
103 const unsigned short dataLength = length - 1;
104 const unsigned char startRegAddr = data[0];
105 unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1];
106 unsigned short bytesWritten = 0;
107
108 while (bytesWritten < dataLength) {
109 unsigned short thisLen = min(SERIAL_MAX_TRANSFER_SIZE,
110 dataLength - bytesWritten);
111 if (bytesWritten == 0) {
112 result = sensor_i2c_write((struct i2c_adapter *)
113 sl_handle, slaveAddr,
114 1 + thisLen, data);
115 } else {
116 /* manually increment register addr between chunks */
117 i2cWrite[0] = startRegAddr + bytesWritten;
118 memcpy(&i2cWrite[1], &data[1 + bytesWritten],
119 thisLen);
120 result = sensor_i2c_write((struct i2c_adapter *)
121 sl_handle, slaveAddr,
122 1 + thisLen, i2cWrite);
123 }
124 if (ML_SUCCESS != result)
125 return result;
126 bytesWritten += thisLen;
127 }
128 return ML_SUCCESS;
129}
130
131
132/**
133 * @brief used to read multiple bytes of data from registers.
134 * This should be sent by I2C.
135 *
136 * @param slaveAddr I2C slave address of device.
137 * @param registerAddr Register address to read.
138 * @param length Length of burst of data.
139 * @param data Pointer to block of data.
140 *
141 * @return Zero if successful; an error code otherwise
142 */
143tMLError MLSLSerialRead(void *sl_handle,
144 unsigned char slaveAddr,
145 unsigned char registerAddr,
146 unsigned short length, unsigned char *data)
147{
148 tMLError result;
149 unsigned short bytesRead = 0;
150
151 if (registerAddr == MPUREG_FIFO_R_W
152 || registerAddr == MPUREG_MEM_R_W) {
153 return ML_ERROR_INVALID_PARAMETER;
154 }
155 while (bytesRead < length) {
156 unsigned short thisLen =
157 min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead);
158 result =
159 sensor_i2c_read((struct i2c_adapter *) sl_handle,
160 slaveAddr, registerAddr + bytesRead,
161 thisLen, &data[bytesRead]);
162 if (ML_SUCCESS != result)
163 return result;
164 bytesRead += thisLen;
165 }
166 return ML_SUCCESS;
167}
168
169
170/**
171 * @brief used to write multiple bytes of data to the memory.
172 * This should be sent by I2C.
173 *
174 * @param slaveAddr I2C slave address of device.
175 * @param memAddr The location in the memory to write to.
176 * @param length Length of burst data.
177 * @param data Pointer to block of data.
178 *
179 * @return Zero if successful; an error code otherwise
180 */
181tMLError MLSLSerialWriteMem(void *sl_handle,
182 unsigned char slaveAddr,
183 unsigned short memAddr,
184 unsigned short length,
185 unsigned char const *data)
186{
187 tMLError result;
188 unsigned short bytesWritten = 0;
189
190 if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
191 pr_err("memory read length (%d B) extends beyond its"
192 " limits (%d) if started at location %d\n", length,
193 MPU_MEM_BANK_SIZE, memAddr & 0xFF);
194 return ML_ERROR_INVALID_PARAMETER;
195 }
196 while (bytesWritten < length) {
197 unsigned short thisLen =
198 min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten);
199 result =
200 mpu_memory_write((struct i2c_adapter *) sl_handle,
201 slaveAddr, memAddr + bytesWritten,
202 thisLen, &data[bytesWritten]);
203 if (ML_SUCCESS != result)
204 return result;
205 bytesWritten += thisLen;
206 }
207 return ML_SUCCESS;
208}
209
210
211/**
212 * @brief used to read multiple bytes of data from the memory.
213 * This should be sent by I2C.
214 *
215 * @param slaveAddr I2C slave address of device.
216 * @param memAddr The location in the memory to read from.
217 * @param length Length of burst data.
218 * @param data Pointer to block of data.
219 *
220 * @return Zero if successful; an error code otherwise
221 */
222tMLError MLSLSerialReadMem(void *sl_handle,
223 unsigned char slaveAddr,
224 unsigned short memAddr,
225 unsigned short length, unsigned char *data)
226{
227 tMLError result;
228 unsigned short bytesRead = 0;
229
230 if ((memAddr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
231 printk
232 ("memory read length (%d B) extends beyond its limits (%d) "
233 "if started at location %d\n", length,
234 MPU_MEM_BANK_SIZE, memAddr & 0xFF);
235 return ML_ERROR_INVALID_PARAMETER;
236 }
237 while (bytesRead < length) {
238 unsigned short thisLen =
239 min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead);
240 result =
241 mpu_memory_read((struct i2c_adapter *) sl_handle,
242 slaveAddr, memAddr + bytesRead,
243 thisLen, &data[bytesRead]);
244 if (ML_SUCCESS != result)
245 return result;
246 bytesRead += thisLen;
247 }
248 return ML_SUCCESS;
249}
250
251
252/**
253 * @brief used to write multiple bytes of data to the fifo.
254 * This should be sent by I2C.
255 *
256 * @param slaveAddr I2C slave address of device.
257 * @param length Length of burst of data.
258 * @param data Pointer to block of data.
259 *
260 * @return Zero if successful; an error code otherwise
261 */
262tMLError MLSLSerialWriteFifo(void *sl_handle,
263 unsigned char slaveAddr,
264 unsigned short length,
265 unsigned char const *data)
266{
267 tMLError result;
268 unsigned char i2cWrite[SERIAL_MAX_TRANSFER_SIZE + 1];
269 unsigned short bytesWritten = 0;
270
271 if (length > FIFO_HW_SIZE) {
272 printk(KERN_ERR
273 "maximum fifo write length is %d\n", FIFO_HW_SIZE);
274 return ML_ERROR_INVALID_PARAMETER;
275 }
276 while (bytesWritten < length) {
277 unsigned short thisLen =
278 min(SERIAL_MAX_TRANSFER_SIZE, length - bytesWritten);
279 i2cWrite[0] = MPUREG_FIFO_R_W;
280 memcpy(&i2cWrite[1], &data[bytesWritten], thisLen);
281 result = sensor_i2c_write((struct i2c_adapter *) sl_handle,
282 slaveAddr, thisLen + 1,
283 i2cWrite);
284 if (ML_SUCCESS != result)
285 return result;
286 bytesWritten += thisLen;
287 }
288 return ML_SUCCESS;
289}
290
291
292/**
293 * @brief used to read multiple bytes of data from the fifo.
294 * This should be sent by I2C.
295 *
296 * @param slaveAddr I2C slave address of device.
297 * @param length Length of burst of data.
298 * @param data Pointer to block of data.
299 *
300 * @return Zero if successful; an error code otherwise
301 */
302tMLError MLSLSerialReadFifo(void *sl_handle,
303 unsigned char slaveAddr,
304 unsigned short length, unsigned char *data)
305{
306 tMLError result;
307 unsigned short bytesRead = 0;
308
309 if (length > FIFO_HW_SIZE) {
310 printk(KERN_ERR
311 "maximum fifo read length is %d\n", FIFO_HW_SIZE);
312 return ML_ERROR_INVALID_PARAMETER;
313 }
314 while (bytesRead < length) {
315 unsigned short thisLen =
316 min(SERIAL_MAX_TRANSFER_SIZE, length - bytesRead);
317 result =
318 sensor_i2c_read((struct i2c_adapter *) sl_handle,
319 slaveAddr, MPUREG_FIFO_R_W, thisLen,
320 &data[bytesRead]);
321 if (ML_SUCCESS != result)
322 return result;
323 bytesRead += thisLen;
324 }
325
326 return ML_SUCCESS;
327}
328
329/**
330 * @}
331 */
diff --git a/drivers/misc/mpu3050/mlsl.h b/drivers/misc/mpu3050/mlsl.h
new file mode 100644
index 00000000000..76f69c77ba9
--- /dev/null
+++ b/drivers/misc/mpu3050/mlsl.h
@@ -0,0 +1,103 @@
1/*
2 $License:
3 Copyright (C) 2010 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 __MSSL_H__
21#define __MSSL_H__
22
23#include "mltypes.h"
24#include "mpu.h"
25
26#ifdef __cplusplus
27extern "C" {
28#endif
29
30/* ------------ */
31/* - Defines. - */
32/* ------------ */
33
34/*
35 * NOTE : to properly support Yamaha compass reads,
36 * the max transfer size should be at least 9 B.
37 * Length in bytes, typically a power of 2 >= 2
38 */
39#define SERIAL_MAX_TRANSFER_SIZE 128
40
41/* ---------------------- */
42/* - Types definitions. - */
43/* ---------------------- */
44
45/* --------------------- */
46/* - Function p-types. - */
47/* --------------------- */
48
49 tMLError MLSLSerialOpen(char const *port,
50 void **sl_handle);
51 tMLError MLSLSerialReset(void *sl_handle);
52 tMLError MLSLSerialClose(void *sl_handle);
53
54 tMLError MLSLSerialWriteSingle(void *sl_handle,
55 unsigned char slaveAddr,
56 unsigned char registerAddr,
57 unsigned char data);
58
59 tMLError MLSLSerialRead(void *sl_handle,
60 unsigned char slaveAddr,
61 unsigned char registerAddr,
62 unsigned short length,
63 unsigned char *data);
64
65 tMLError MLSLSerialWrite(void *sl_handle,
66 unsigned char slaveAddr,
67 unsigned short length,
68 unsigned char const *data);
69
70 tMLError MLSLSerialReadMem(void *sl_handle,
71 unsigned char slaveAddr,
72 unsigned short memAddr,
73 unsigned short length,
74 unsigned char *data);
75
76 tMLError MLSLSerialWriteMem(void *sl_handle,
77 unsigned char slaveAddr,
78 unsigned short memAddr,
79 unsigned short length,
80 unsigned char const *data);
81
82 tMLError MLSLSerialReadFifo(void *sl_handle,
83 unsigned char slaveAddr,
84 unsigned short length,
85 unsigned char *data);
86
87 tMLError MLSLSerialWriteFifo(void *sl_handle,
88 unsigned char slaveAddr,
89 unsigned short length,
90 unsigned char const *data);
91
92 tMLError MLSLWriteCal(unsigned char *cal, unsigned int len);
93 tMLError MLSLReadCal(unsigned char *cal, unsigned int len);
94 tMLError MLSLGetCalLength(unsigned int *len);
95
96#ifdef __cplusplus
97}
98#endif
99
100/**
101 * @}
102 */
103#endif /* MLSL_H */
diff --git a/drivers/misc/mpu3050/mltypes.h b/drivers/misc/mpu3050/mltypes.h
new file mode 100644
index 00000000000..d0b27fa89e7
--- /dev/null
+++ b/drivers/misc/mpu3050/mltypes.h
@@ -0,0 +1,227 @@
1/*
2 $License:
3 Copyright (C) 2010 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 Motion Library - Error definitions.
23 * Definition of the error codes used within the MPL and returned
24 * to the user.
25 * Every function tries to return a meaningful error code basing
26 * on the occuring error condition. The error code is numeric.
27 *
28 * The available error codes and their associated values are:
29 * - (0) ML_SUCCESS
30 * - (1) ML_ERROR
31 * - (2) ML_ERROR_INVALID_PARAMETER
32 * - (3) ML_ERROR_FEATURE_NOT_ENABLED
33 * - (4) ML_ERROR_FEATURE_NOT_IMPLEMENTED
34 * - (6) ML_ERROR_DMP_NOT_STARTED
35 * - (7) ML_ERROR_DMP_STARTED
36 * - (8) ML_ERROR_NOT_OPENED
37 * - (9) ML_ERROR_OPENED
38 * - (10) ML_ERROR_INVALID_MODULE
39 * - (11) ML_ERROR_MEMORY_EXAUSTED
40 * - (12) ML_ERROR_DIVIDE_BY_ZERO
41 * - (13) ML_ERROR_ASSERTION_FAILURE
42 * - (14) ML_ERROR_FILE_OPEN
43 * - (15) ML_ERROR_FILE_READ
44 * - (16) ML_ERROR_FILE_WRITE
45 * - (20) ML_ERROR_SERIAL_CLOSED
46 * - (21) ML_ERROR_SERIAL_OPEN_ERROR
47 * - (22) ML_ERROR_SERIAL_READ
48 * - (23) ML_ERROR_SERIAL_WRITE
49 * - (24) ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
50 * - (25) ML_ERROR_SM_TRANSITION
51 * - (26) ML_ERROR_SM_IMPROPER_STATE
52 * - (30) ML_ERROR_FIFO_OVERFLOW
53 * - (31) ML_ERROR_FIFO_FOOTER
54 * - (32) ML_ERROR_FIFO_READ_COUNT
55 * - (33) ML_ERROR_FIFO_READ_DATA
56 * - (40) ML_ERROR_MEMORY_SET
57 * - (50) ML_ERROR_LOG_MEMORY_ERROR
58 * - (51) ML_ERROR_LOG_OUTPUT_ERROR
59 * - (60) ML_ERROR_OS_BAD_PTR
60 * - (61) ML_ERROR_OS_BAD_HANDLE
61 * - (62) ML_ERROR_OS_CREATE_FAILED
62 * - (63) ML_ERROR_OS_LOCK_FAILED
63 * - (70) ML_ERROR_COMPASS_DATA_OVERFLOW
64 * - (71) ML_ERROR_COMPASS_DATA_UNDERFLOW
65 * - (72) ML_ERROR_COMPASS_DATA_NOT_READY
66 * - (73) ML_ERROR_COMPASS_DATA_ERROR
67 * - (75) ML_ERROR_CALIBRATION_LOAD
68 * - (76) ML_ERROR_CALIBRATION_STORE
69 * - (77) ML_ERROR_CALIBRATION_LEN
70 * - (78) ML_ERROR_CALIBRATION_CHECKSUM
71 *
72 * @{
73 * @file mltypes.h
74 * @}
75 */
76
77#ifndef MLTYPES_H
78#define MLTYPES_H
79
80#ifdef __KERNEL__
81#include <linux/types.h>
82#else
83#include "stdint_invensense.h"
84#endif
85#include "log.h"
86
87/*---------------------------
88 ML Types
89---------------------------*/
90
91/**
92 * @struct tMLError mltypes.h "mltypes"
93 * @brief The MPL Error Code return type.
94 *
95 * @code
96 * typedef unsigned char tMLError;
97 * @endcode
98 */
99typedef unsigned char tMLError;
100
101#if defined(LINUX) || defined(__KERNEL__)
102typedef unsigned int HANDLE;
103#endif
104
105#ifdef __KERNEL__
106typedef HANDLE FILE;
107#endif
108
109#ifndef __cplusplus
110#ifndef __KERNEL__
111typedef int_fast8_t bool;
112#endif
113#endif
114
115/*---------------------------
116 ML Defines
117---------------------------*/
118
119#ifndef NULL
120#define NULL 0
121#endif
122
123#ifndef TRUE
124#define TRUE 1
125#endif
126
127#ifndef FALSE
128#define FALSE 0
129#endif
130
131/* Dimension of an array */
132#ifndef DIM
133#define DIM(array) (sizeof(array)/sizeof((array)[0]))
134#endif
135
136/* - ML Errors. - */
137#define ERROR_NAME(x) (#x)
138#define ERROR_CHECK(x) \
139 do { \
140 if (ML_SUCCESS != x) { \
141 MPL_LOGE("%s|%s|%d returning %d\n", \
142 __FILE__, __func__, __LINE__, x); \
143 return x; \
144 } \
145 } while (0)
146
147#define ERROR_CHECK_FIRST(first, x) \
148 { if (ML_SUCCESS == first) first = x; }
149
150#define ML_SUCCESS (0)
151/* Generic Error code. Proprietary Error Codes only */
152#define ML_ERROR (1)
153
154/* Compatibility and other generic error codes */
155#define ML_ERROR_INVALID_PARAMETER (2)
156#define ML_ERROR_FEATURE_NOT_ENABLED (3)
157#define ML_ERROR_FEATURE_NOT_IMPLEMENTED (4)
158#define ML_ERROR_DMP_NOT_STARTED (6)
159#define ML_ERROR_DMP_STARTED (7)
160#define ML_ERROR_NOT_OPENED (8)
161#define ML_ERROR_OPENED (9)
162#define ML_ERROR_INVALID_MODULE (10)
163#define ML_ERROR_MEMORY_EXAUSTED (11)
164#define ML_ERROR_DIVIDE_BY_ZERO (12)
165#define ML_ERROR_ASSERTION_FAILURE (13)
166#define ML_ERROR_FILE_OPEN (14)
167#define ML_ERROR_FILE_READ (15)
168#define ML_ERROR_FILE_WRITE (16)
169#define ML_ERROR_INVALID_CONFIGURATION (17)
170
171/* Serial Communication */
172#define ML_ERROR_SERIAL_CLOSED (20)
173#define ML_ERROR_SERIAL_OPEN_ERROR (21)
174#define ML_ERROR_SERIAL_READ (22)
175#define ML_ERROR_SERIAL_WRITE (23)
176#define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (24)
177
178/* SM = State Machine */
179#define ML_ERROR_SM_TRANSITION (25)
180#define ML_ERROR_SM_IMPROPER_STATE (26)
181
182/* Fifo */
183#define ML_ERROR_FIFO_OVERFLOW (30)
184#define ML_ERROR_FIFO_FOOTER (31)
185#define ML_ERROR_FIFO_READ_COUNT (32)
186#define ML_ERROR_FIFO_READ_DATA (33)
187
188/* Memory & Registers, Set & Get */
189#define ML_ERROR_MEMORY_SET (40)
190
191#define ML_ERROR_LOG_MEMORY_ERROR (50)
192#define ML_ERROR_LOG_OUTPUT_ERROR (51)
193
194/* OS interface errors */
195#define ML_ERROR_OS_BAD_PTR (60)
196#define ML_ERROR_OS_BAD_HANDLE (61)
197#define ML_ERROR_OS_CREATE_FAILED (62)
198#define ML_ERROR_OS_LOCK_FAILED (63)
199
200/* Compass errors */
201#define ML_ERROR_COMPASS_DATA_OVERFLOW (70)
202#define ML_ERROR_COMPASS_DATA_UNDERFLOW (71)
203#define ML_ERROR_COMPASS_DATA_NOT_READY (72)
204#define ML_ERROR_COMPASS_DATA_ERROR (73)
205
206/* Load/Store calibration */
207#define ML_ERROR_CALIBRATION_LOAD (75)
208#define ML_ERROR_CALIBRATION_STORE (76)
209#define ML_ERROR_CALIBRATION_LEN (77)
210#define ML_ERROR_CALIBRATION_CHECKSUM (78)
211
212/* Accel errors */
213#define ML_ERROR_ACCEL_DATA_OVERFLOW (79)
214#define ML_ERROR_ACCEL_DATA_UNDERFLOW (80)
215#define ML_ERROR_ACCEL_DATA_NOT_READY (81)
216#define ML_ERROR_ACCEL_DATA_ERROR (82)
217
218/* For Linux coding compliance */
219#ifndef __KERNEL__
220#define EXPORT_SYMBOL(x)
221#endif
222
223/*---------------------------
224 p-Types
225---------------------------*/
226
227#endif /* MLTYPES_H */
diff --git a/drivers/misc/mpu3050/mpu-dev.c b/drivers/misc/mpu3050/mpu-dev.c
new file mode 100644
index 00000000000..4dba44d4548
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-dev.c
@@ -0,0 +1,1310 @@
1/*
2 mpu-dev.c - mpu3050 char device interface 2 $License:
3
4 Copyright (C) 1995-97 Simon G. Vogl
5 Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
6 Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
7
8 Copyright (C) 2010 InvenSense Corporation, All Rights Reserved.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23#include <linux/i2c.h>
24#include <linux/i2c-dev.h>
25#include <linux/interrupt.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/kernel.h>
29#include <linux/init.h>
30#include <linux/stat.h>
31#include <linux/irq.h>
32#include <linux/gpio.h>
33#include <linux/signal.h>
34#include <linux/miscdevice.h>
35#include <linux/slab.h>
36#include <linux/version.h>
37#include <linux/pm.h>
38#include <linux/mutex.h>
39#include <linux/suspend.h>
40#include <linux/poll.h>
41#ifdef CONFIG_HAS_EARLYSUSPEND
42#include <linux/earlysuspend.h>
43#endif
44
45#include <linux/errno.h>
46#include <linux/fs.h>
47#include <linux/mm.h>
48#include <linux/sched.h>
49#include <linux/wait.h>
50#include <linux/uaccess.h>
51#include <linux/io.h>
52
53#include "mpuirq.h"
54#include "slaveirq.h"
55#include "mlsl.h"
56#include "mpu-i2c.h"
57#include "mldl_cfg.h"
58#include "mpu.h"
59
60#define MPU3050_EARLY_SUSPEND_IN_DRIVER 0
61
62/* Platform data for the MPU */
63struct mpu_private_data {
64 struct mldl_cfg mldl_cfg;
65
66#ifdef CONFIG_HAS_EARLYSUSPEND
67 struct early_suspend early_suspend;
68#endif
69 struct mutex mutex;
70 wait_queue_head_t mpu_event_wait;
71 struct completion completion;
72 struct timer_list timeout;
73 struct notifier_block nb;
74 struct mpuirq_data mpu_pm_event;
75 int response_timeout; /* In seconds */
76 unsigned long event;
77 int pid;
78};
79
80static struct i2c_client *this_client;
81
82
83static void
84mpu_pm_timeout(u_long data)
85{
86 struct mpu_private_data *mpu = (struct mpu_private_data *) data;
87 dev_dbg(&this_client->adapter->dev, "%s\n", __func__);
88 complete(&mpu->completion);
89}
90
91static int mpu_pm_notifier_callback(struct notifier_block *nb,
92 unsigned long event,
93 void *unused)
94{
95 struct mpu_private_data *mpu =
96 container_of(nb, struct mpu_private_data, nb);
97 struct timeval event_time;
98 dev_dbg(&this_client->adapter->dev, "%s: %ld\n", __func__, event);
99
100 /* Prevent the file handle from being closed before we initialize
101 the completion event */
102 mutex_lock(&mpu->mutex);
103 if (!(mpu->pid) ||
104 (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
105 mutex_unlock(&mpu->mutex);
106 return NOTIFY_OK;
107 }
108
109 do_gettimeofday(&event_time);
110 mpu->mpu_pm_event.interruptcount++;
111 mpu->mpu_pm_event.irqtime =
112 (((long long) event_time.tv_sec) << 32) +
113 event_time.tv_usec;
114 mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
115 mpu->mpu_pm_event.data_size = sizeof(unsigned long);
116 mpu->mpu_pm_event.data = &mpu->event;
117
118 if (event == PM_SUSPEND_PREPARE)
119 mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
120 if (event == PM_POST_SUSPEND)
121 mpu->event = MPU_PM_EVENT_POST_SUSPEND;
122
123 if (mpu->response_timeout > 0) {
124 mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
125 add_timer(&mpu->timeout);
126 }
127 INIT_COMPLETION(mpu->completion);
128 mutex_unlock(&mpu->mutex);
129
130 wake_up_interruptible(&mpu->mpu_event_wait);
131 wait_for_completion(&mpu->completion);
132 del_timer_sync(&mpu->timeout);
133 dev_dbg(&this_client->adapter->dev, "%s: %ld DONE\n", __func__, event);
134 return NOTIFY_OK;
135}
136
137static int mpu_open(struct inode *inode, struct file *file)
138{
139 struct mpu_private_data *mpu =
140 (struct mpu_private_data *) i2c_get_clientdata(this_client);
141 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
142 int result;
143 dev_dbg(&this_client->adapter->dev, "mpu_open\n");
144 dev_dbg(&this_client->adapter->dev, "current->pid %d\n",
145 current->pid);
146 mpu->pid = current->pid;
147 file->private_data = this_client;
148 /* we could do some checking on the flags supplied by "open" */
149 /* i.e. O_NONBLOCK */
150 /* -> set some flag to disable interruptible_sleep_on in mpu_read */
151
152 /* Reset the sensors to the default */
153 result = mutex_lock_interruptible(&mpu->mutex);
154 if (result) {
155 dev_err(&this_client->adapter->dev,
156 "%s: mutex_lock_interruptible returned %d\n",
157 __func__, result);
158 return result;
159 }
160 mldl_cfg->requested_sensors = ML_THREE_AXIS_GYRO;
161 if (mldl_cfg->accel && mldl_cfg->accel->resume)
162 mldl_cfg->requested_sensors |= ML_THREE_AXIS_ACCEL;
163
164 if (mldl_cfg->compass && mldl_cfg->compass->resume)
165 mldl_cfg->requested_sensors |= ML_THREE_AXIS_COMPASS;
166
167 if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
168 mldl_cfg->requested_sensors |= ML_THREE_AXIS_PRESSURE;
169 mutex_unlock(&mpu->mutex);
170 return 0;
171}
172
173/* close function - called when the "file" /dev/mpu is closed in userspace */
174static int mpu_release(struct inode *inode, struct file *file)
175{
176 struct i2c_client *client =
177 (struct i2c_client *) file->private_data;
178 struct mpu_private_data *mpu =
179 (struct mpu_private_data *) i2c_get_clientdata(client);
180 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
181 struct i2c_adapter *accel_adapter;
182 struct i2c_adapter *compass_adapter;
183 struct i2c_adapter *pressure_adapter;
184 int result = 0;
185
186 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
187 compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
188 pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
189
190 mutex_lock(&mpu->mutex);
191 result = mpu3050_suspend(mldl_cfg, client->adapter,
192 accel_adapter, compass_adapter,
193 pressure_adapter,
194 TRUE, TRUE, TRUE, TRUE);
195 mpu->pid = 0;
196 mutex_unlock(&mpu->mutex);
197 complete(&mpu->completion);
198 dev_dbg(&this_client->adapter->dev, "mpu_release\n");
199 return result;
200}
201
202/* read function called when from /dev/mpu is read. Read from the FIFO */
203static ssize_t mpu_read(struct file *file,
204 char __user *buf, size_t count, loff_t *offset)
205{
206 struct mpuirq_data local_mpu_pm_event;
207 struct i2c_client *client =
208 (struct i2c_client *) file->private_data;
209 struct mpu_private_data *mpu =
210 (struct mpu_private_data *) i2c_get_clientdata(client);
211 size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
212 int err;
213
214 if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
215 wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
216
217 if (!mpu->event || NULL == buf
218 || count < sizeof(mpu->mpu_pm_event) + sizeof(unsigned long))
219 return 0;
220
221 err = copy_from_user(&local_mpu_pm_event, buf,
222 sizeof(mpu->mpu_pm_event));
223 if (err != 0) {
224 dev_err(&this_client->adapter->dev,
225 "Copy from user returned %d\n", err);
226 return -EFAULT;
227 }
228
229 mpu->mpu_pm_event.data = local_mpu_pm_event.data;
230 err = copy_to_user((unsigned long __user *)local_mpu_pm_event.data,
231 &mpu->event,
232 sizeof(mpu->event));
233 if (err != 0) {
234 dev_err(&this_client->adapter->dev,
235 "Copy to user returned %d\n", err);
236 return -EFAULT;
237 }
238 err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
239 if (err != 0) {
240 dev_err(&this_client->adapter->dev,
241 "Copy to user returned %d\n", err);
242 return -EFAULT;
243 }
244 mpu->event = 0;
245 return len;
246}
247
248static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
249{
250 struct i2c_client *client =
251 (struct i2c_client *) file->private_data;
252 struct mpu_private_data *mpu =
253 (struct mpu_private_data *) i2c_get_clientdata(client);
254 int mask = 0;
255
256 poll_wait(file, &mpu->mpu_event_wait, poll);
257 if (mpu->event)
258 mask |= POLLIN | POLLRDNORM;
259 return mask;
260}
261
262static int
263mpu_ioctl_set_mpu_pdata(struct i2c_client *client, unsigned long arg)
264{
265 int ii;
266 struct mpu_private_data *mpu =
267 (struct mpu_private_data *) i2c_get_clientdata(client);
268 struct mpu3050_platform_data *pdata = mpu->mldl_cfg.pdata;
269 struct mpu3050_platform_data local_pdata;
270
271 if (copy_from_user(&local_pdata, (unsigned char __user *) arg,
272 sizeof(local_pdata)))
273 return -EFAULT;
274
275 pdata->int_config = local_pdata.int_config;
276 for (ii = 0; ii < DIM(pdata->orientation); ii++)
277 pdata->orientation[ii] = local_pdata.orientation[ii];
278 pdata->level_shifter = local_pdata.level_shifter;
279
280 pdata->accel.address = local_pdata.accel.address;
281 for (ii = 0; ii < DIM(pdata->accel.orientation); ii++)
282 pdata->accel.orientation[ii] =
283 local_pdata.accel.orientation[ii];
284
285 pdata->compass.address = local_pdata.compass.address;
286 for (ii = 0; ii < DIM(pdata->compass.orientation); ii++)
287 pdata->compass.orientation[ii] =
288 local_pdata.compass.orientation[ii];
289
290 pdata->pressure.address = local_pdata.pressure.address;
291 for (ii = 0; ii < DIM(pdata->pressure.orientation); ii++)
292 pdata->pressure.orientation[ii] =
293 local_pdata.pressure.orientation[ii];
294
295 dev_dbg(&client->adapter->dev, "%s\n", __func__);
296
297 return ML_SUCCESS;
298}
299
300static int
301mpu_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg)
302{
303 int ii;
304 int result = ML_SUCCESS;
305 struct mpu_private_data *mpu =
306 (struct mpu_private_data *) i2c_get_clientdata(client);
307 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
308 struct mldl_cfg *temp_mldl_cfg;
309
310 dev_dbg(&this_client->adapter->dev, "%s\n", __func__);
311
312 temp_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
313 if (NULL == temp_mldl_cfg)
314 return -ENOMEM;
315
316 /*
317 * User space is not allowed to modify accel compass pressure or
318 * pdata structs, as well as silicon_revision product_id or trim
319 */
320 if (copy_from_user(temp_mldl_cfg, (struct mldl_cfg __user *) arg,
321 offsetof(struct mldl_cfg, silicon_revision))) {
322 result = -EFAULT;
323 goto out;
324 }
325
326 if (mldl_cfg->gyro_is_suspended) {
327 if (mldl_cfg->addr != temp_mldl_cfg->addr)
328 mldl_cfg->gyro_needs_reset = TRUE;
329
330 if (mldl_cfg->int_config != temp_mldl_cfg->int_config)
331 mldl_cfg->gyro_needs_reset = TRUE;
332
333 if (mldl_cfg->ext_sync != temp_mldl_cfg->ext_sync)
334 mldl_cfg->gyro_needs_reset = TRUE;
335
336 if (mldl_cfg->full_scale != temp_mldl_cfg->full_scale)
337 mldl_cfg->gyro_needs_reset = TRUE;
338
339 if (mldl_cfg->lpf != temp_mldl_cfg->lpf)
340 mldl_cfg->gyro_needs_reset = TRUE;
341
342 if (mldl_cfg->clk_src != temp_mldl_cfg->clk_src)
343 mldl_cfg->gyro_needs_reset = TRUE;
344
345 if (mldl_cfg->divider != temp_mldl_cfg->divider)
346 mldl_cfg->gyro_needs_reset = TRUE;
347
348 if (mldl_cfg->dmp_enable != temp_mldl_cfg->dmp_enable)
349 mldl_cfg->gyro_needs_reset = TRUE;
350
351 if (mldl_cfg->fifo_enable != temp_mldl_cfg->fifo_enable)
352 mldl_cfg->gyro_needs_reset = TRUE;
353
354 if (mldl_cfg->dmp_cfg1 != temp_mldl_cfg->dmp_cfg1)
355 mldl_cfg->gyro_needs_reset = TRUE;
356
357 if (mldl_cfg->dmp_cfg2 != temp_mldl_cfg->dmp_cfg2)
358 mldl_cfg->gyro_needs_reset = TRUE;
359
360 if (mldl_cfg->gyro_power != temp_mldl_cfg->gyro_power)
361 mldl_cfg->gyro_needs_reset = TRUE;
362
363 for (ii = 0; ii < MPU_NUM_AXES; ii++)
364 if (mldl_cfg->offset_tc[ii] !=
365 temp_mldl_cfg->offset_tc[ii])
366 mldl_cfg->gyro_needs_reset = TRUE;
367
368 for (ii = 0; ii < MPU_NUM_AXES; ii++)
369 if (mldl_cfg->offset[ii] != temp_mldl_cfg->offset[ii])
370 mldl_cfg->gyro_needs_reset = TRUE;
371
372 if (memcmp(mldl_cfg->ram, temp_mldl_cfg->ram,
373 MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE *
374 sizeof(unsigned char)))
375 mldl_cfg->gyro_needs_reset = TRUE;
376 }
377
378 memcpy(mldl_cfg, temp_mldl_cfg,
379 offsetof(struct mldl_cfg, silicon_revision));
380
381out:
382 kfree(temp_mldl_cfg);
383 return result;
384}
385
386static int
387mpu_ioctl_get_mpu_config(struct i2c_client *client, unsigned long arg)
388{
389 /* Have to be careful as there are 3 pointers in the mldl_cfg
390 * structure */
391 struct mpu_private_data *mpu =
392 (struct mpu_private_data *) i2c_get_clientdata(client);
393 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
394 struct mldl_cfg *local_mldl_cfg;
395 int retval = 0;
396
397 local_mldl_cfg = kzalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
398 if (NULL == local_mldl_cfg)
399 return -ENOMEM;
400
401 retval =
402 copy_from_user(local_mldl_cfg, (struct mldl_cfg __user *) arg,
403 sizeof(struct mldl_cfg));
404 if (retval) {
405 dev_err(&this_client->adapter->dev,
406 "%s|%s:%d: EFAULT on arg\n",
407 __FILE__, __func__, __LINE__);
408 retval = -EFAULT;
409 goto out;
410 }
411
412 /* Fill in the accel, compass, pressure and pdata pointers */
413 if (mldl_cfg->accel) {
414 retval = copy_to_user((void __user *)local_mldl_cfg->accel,
415 mldl_cfg->accel,
416 sizeof(*mldl_cfg->accel));
417 if (retval) {
418 dev_err(&this_client->adapter->dev,
419 "%s|%s:%d: EFAULT on accel\n",
420 __FILE__, __func__, __LINE__);
421 retval = -EFAULT;
422 goto out;
423 }
424 }
425
426 if (mldl_cfg->compass) {
427 retval = copy_to_user((void __user *)local_mldl_cfg->compass,
428 mldl_cfg->compass,
429 sizeof(*mldl_cfg->compass));
430 if (retval) {
431 dev_err(&this_client->adapter->dev,
432 "%s|%s:%d: EFAULT on compass\n",
433 __FILE__, __func__, __LINE__);
434 retval = -EFAULT;
435 goto out;
436 }
437 }
438
439 if (mldl_cfg->pressure) {
440 retval = copy_to_user((void __user *)local_mldl_cfg->pressure,
441 mldl_cfg->pressure,
442 sizeof(*mldl_cfg->pressure));
443 if (retval) {
444 dev_err(&this_client->adapter->dev,
445 "%s|%s:%d: EFAULT on pressure\n",
446 __FILE__, __func__, __LINE__);
447 retval = -EFAULT;
448 goto out;
449 }
450 }
451
452 if (mldl_cfg->pdata) {
453 retval = copy_to_user((void __user *)local_mldl_cfg->pdata,
454 mldl_cfg->pdata,
455 sizeof(*mldl_cfg->pdata));
456 if (retval) {
457 dev_err(&this_client->adapter->dev,
458 "%s|%s:%d: EFAULT on pdata\n",
459 __FILE__, __func__, __LINE__);
460 retval = -EFAULT;
461 goto out;
462 }
463 }
464
465 /* Do not modify the accel, compass, pressure and pdata pointers */
466 retval = copy_to_user((struct mldl_cfg __user *) arg,
467 mldl_cfg, offsetof(struct mldl_cfg, accel));
468
469 if (retval)
470 retval = -EFAULT;
471out:
472 kfree(local_mldl_cfg);
473 return retval;
474}
475
476/**
477 * Pass a requested slave configuration to the slave sensor
478 *
479 * @param adapter the adaptor to use to communicate with the slave
480 * @param mldl_cfg the mldl configuration structuer
481 * @param slave pointer to the slave descriptor
482 * @param usr_config The configuration to pass to the slave sensor
483 *
484 * @return 0 or non-zero error code
485 */
486static int slave_config(void *adapter,
487 struct mldl_cfg *mldl_cfg,
488 struct ext_slave_descr *slave,
489 struct ext_slave_platform_data *pdata,
490 struct ext_slave_config __user *usr_config)
491{
492 int retval = ML_SUCCESS;
493 struct ext_slave_config config;
494 if ((!slave) || (!slave->config))
495 return retval;
496
497 retval = copy_from_user(&config, usr_config, sizeof(config));
498 if (retval)
499 return -EFAULT;
500
501 if (config.len && config.data) {
502 int *data;
503 data = kzalloc(config.len, GFP_KERNEL);
504 if (!data)
505 return ML_ERROR_MEMORY_EXAUSTED;
506
507 retval = copy_from_user(data,
508 (void __user *)config.data,
509 config.len);
510 if (retval) {
511 retval = -EFAULT;
512 kfree(data);
513 return retval;
514 }
515 config.data = data;
516 }
517 retval = slave->config(adapter, slave, pdata, &config);
518 kfree(config.data);
519 return retval;
520}
521
522/**
523 * Get a requested slave configuration from the slave sensor
524 *
525 * @param adapter the adaptor to use to communicate with the slave
526 * @param mldl_cfg the mldl configuration structuer
527 * @param slave pointer to the slave descriptor
528 * @param usr_config The configuration for the slave to fill out
529 *
530 * @return 0 or non-zero error code
531 */
532static int slave_get_config(void *adapter,
533 struct mldl_cfg *mldl_cfg,
534 struct ext_slave_descr *slave,
535 struct ext_slave_platform_data *pdata,
536 struct ext_slave_config __user *usr_config)
537{
538 int retval = ML_SUCCESS;
539 struct ext_slave_config config;
540 void *user_data;
541 if (!(slave) || !(slave->get_config))
542 return ML_SUCCESS;
543
544 retval = copy_from_user(&config, usr_config, sizeof(config));
545 if (retval)
546 return -EFAULT;
547
548 user_data = config.data;
549 if (config.len && config.data) {
550 int *data;
551 data = kzalloc(config.len, GFP_KERNEL);
552 if (!data)
553 return ML_ERROR_MEMORY_EXAUSTED;
554
555 retval = copy_from_user(data,
556 (void __user *)config.data,
557 config.len);
558 if (retval) {
559 retval = -EFAULT;
560 kfree(data);
561 return retval;
562 }
563 config.data = data;
564 }
565 retval = slave->get_config(adapter, slave, pdata, &config);
566 if (retval) {
567 kfree(config.data);
568 return retval;
569 }
570 retval = copy_to_user((unsigned char __user *) user_data,
571 config.data,
572 config.len);
573 kfree(config.data);
574 return retval;
575}
576
577static int mpu_handle_mlsl(void *sl_handle,
578 unsigned char addr,
579 unsigned int cmd,
580 struct mpu_read_write __user *usr_msg)
581{
582 int retval = ML_SUCCESS;
583 struct mpu_read_write msg;
584 unsigned char *user_data;
585 retval = copy_from_user(&msg, usr_msg, sizeof(msg));
586 if (retval)
587 return -EFAULT;
588
589 user_data = msg.data;
590 if (msg.length && msg.data) {
591 unsigned char *data;
592 data = kzalloc(msg.length, GFP_KERNEL);
593 if (!data)
594 return ML_ERROR_MEMORY_EXAUSTED;
595
596 retval = copy_from_user(data,
597 (void __user *)msg.data,
598 msg.length);
599 if (retval) {
600 retval = -EFAULT;
601 kfree(data);
602 return retval;
603 }
604 msg.data = data;
605 } else {
606 return ML_ERROR_INVALID_PARAMETER;
607 }
608
609 switch (cmd) {
610 case MPU_READ:
611 retval = MLSLSerialRead(sl_handle, addr,
612 msg.address, msg.length, msg.data);
613 break;
614 case MPU_WRITE:
615 retval = MLSLSerialWrite(sl_handle, addr,
616 msg.length, msg.data);
617 break;
618 case MPU_READ_MEM:
619 retval = MLSLSerialReadMem(sl_handle, addr,
620 msg.address, msg.length, msg.data);
621 break;
622 case MPU_WRITE_MEM:
623 retval = MLSLSerialWriteMem(sl_handle, addr,
624 msg.address, msg.length, msg.data);
625 break;
626 case MPU_READ_FIFO:
627 retval = MLSLSerialReadFifo(sl_handle, addr,
628 msg.length, msg.data);
629 break;
630 case MPU_WRITE_FIFO:
631 retval = MLSLSerialWriteFifo(sl_handle, addr,
632 msg.length, msg.data);
633 break;
634
635 };
636 retval = copy_to_user((unsigned char __user *) user_data,
637 msg.data,
638 msg.length);
639 kfree(msg.data);
640 return retval;
641}
642
643/* ioctl - I/O control */
644static long mpu_ioctl(struct file *file,
645 unsigned int cmd, unsigned long arg)
646{
647 struct i2c_client *client =
648 (struct i2c_client *) file->private_data;
649 struct mpu_private_data *mpu =
650 (struct mpu_private_data *) i2c_get_clientdata(client);
651 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
652 int retval = 0;
653 struct i2c_adapter *accel_adapter;
654 struct i2c_adapter *compass_adapter;
655 struct i2c_adapter *pressure_adapter;
656
657 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
658 compass_adapter =
659 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
660 pressure_adapter =
661 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
662
663 retval = mutex_lock_interruptible(&mpu->mutex);
664 if (retval) {
665 dev_err(&this_client->adapter->dev,
666 "%s: mutex_lock_interruptible returned %d\n",
667 __func__, retval);
668 return retval;
669 }
670
671 switch (cmd) {
672 case MPU_SET_MPU_CONFIG:
673 retval = mpu_ioctl_set_mpu_config(client, arg);
674 break;
675 case MPU_SET_PLATFORM_DATA:
676 retval = mpu_ioctl_set_mpu_pdata(client, arg);
677 break;
678 case MPU_GET_MPU_CONFIG:
679 retval = mpu_ioctl_get_mpu_config(client, arg);
680 break;
681 case MPU_READ:
682 case MPU_WRITE:
683 case MPU_READ_MEM:
684 case MPU_WRITE_MEM:
685 case MPU_READ_FIFO:
686 case MPU_WRITE_FIFO:
687 retval = mpu_handle_mlsl(client->adapter, mldl_cfg->addr, cmd,
688 (struct mpu_read_write __user *) arg);
689 break;
690 case MPU_CONFIG_ACCEL:
691 retval = slave_config(accel_adapter, mldl_cfg,
692 mldl_cfg->accel,
693 &mldl_cfg->pdata->accel,
694 (struct ext_slave_config __user *) arg);
695 break;
696 case MPU_CONFIG_COMPASS:
697 retval = slave_config(compass_adapter, mldl_cfg,
698 mldl_cfg->compass,
699 &mldl_cfg->pdata->compass,
700 (struct ext_slave_config __user *) arg);
701 break;
702 case MPU_CONFIG_PRESSURE:
703 retval = slave_config(pressure_adapter, mldl_cfg,
704 mldl_cfg->pressure,
705 &mldl_cfg->pdata->pressure,
706 (struct ext_slave_config __user *) arg);
707 break;
708 case MPU_GET_CONFIG_ACCEL:
709 retval = slave_get_config(accel_adapter, mldl_cfg,
710 mldl_cfg->accel,
711 &mldl_cfg->pdata->accel,
712 (struct ext_slave_config __user *) arg);
713 break;
714 case MPU_GET_CONFIG_COMPASS:
715 retval = slave_get_config(compass_adapter, mldl_cfg,
716 mldl_cfg->compass,
717 &mldl_cfg->pdata->compass,
718 (struct ext_slave_config __user *) arg);
719 break;
720 case MPU_GET_CONFIG_PRESSURE:
721 retval = slave_get_config(pressure_adapter, mldl_cfg,
722 mldl_cfg->pressure,
723 &mldl_cfg->pdata->pressure,
724 (struct ext_slave_config __user *) arg);
725 break;
726 case MPU_SUSPEND:
727 {
728 unsigned long sensors;
729 sensors = ~(mldl_cfg->requested_sensors);
730 retval = mpu3050_suspend(mldl_cfg,
731 client->adapter,
732 accel_adapter,
733 compass_adapter,
734 pressure_adapter,
735 ((sensors & ML_THREE_AXIS_GYRO)
736 == ML_THREE_AXIS_GYRO),
737 ((sensors & ML_THREE_AXIS_ACCEL)
738 == ML_THREE_AXIS_ACCEL),
739 ((sensors & ML_THREE_AXIS_COMPASS)
740 == ML_THREE_AXIS_COMPASS),
741 ((sensors & ML_THREE_AXIS_PRESSURE)
742 == ML_THREE_AXIS_PRESSURE));
743 }
744 break;
745 case MPU_RESUME:
746 {
747 unsigned long sensors;
748 sensors = mldl_cfg->requested_sensors;
749 retval = mpu3050_resume(mldl_cfg,
750 client->adapter,
751 accel_adapter,
752 compass_adapter,
753 pressure_adapter,
754 sensors & ML_THREE_AXIS_GYRO,
755 sensors & ML_THREE_AXIS_ACCEL,
756 sensors & ML_THREE_AXIS_COMPASS,
757 sensors & ML_THREE_AXIS_PRESSURE);
758 }
759 break;
760 case MPU_PM_EVENT_HANDLED:
761 dev_dbg(&this_client->adapter->dev,
762 "%s: %d\n", __func__, cmd);
763 complete(&mpu->completion);
764 break;
765 case MPU_READ_ACCEL:
766 {
767 unsigned char data[6];
768 retval = mpu3050_read_accel(mldl_cfg, client->adapter,
769 data);
770 if ((ML_SUCCESS == retval) &&
771 (copy_to_user((unsigned char __user *) arg,
772 data, sizeof(data))))
773 retval = -EFAULT;
774 }
775 break;
776 case MPU_READ_COMPASS:
777 {
778 unsigned char data[6];
779 struct i2c_adapter *compass_adapt =
780 i2c_get_adapter(mldl_cfg->pdata->compass.
781 adapt_num);
782 retval = mpu3050_read_compass(mldl_cfg, compass_adapt,
783 data);
784 if ((ML_SUCCESS == retval) &&
785 (copy_to_user((unsigned char *) arg,
786 data, sizeof(data))))
787 retval = -EFAULT;
788 }
789 break;
790 case MPU_READ_PRESSURE:
791 {
792 unsigned char data[3];
793 struct i2c_adapter *pressure_adapt =
794 i2c_get_adapter(mldl_cfg->pdata->pressure.
795 adapt_num);
796 retval =
797 mpu3050_read_pressure(mldl_cfg, pressure_adapt,
798 data);
799 if ((ML_SUCCESS == retval) &&
800 (copy_to_user((unsigned char __user *) arg,
801 data, sizeof(data))))
802 retval = -EFAULT;
803 }
804 break;
805 default:
806 dev_err(&this_client->adapter->dev,
807 "%s: Unknown cmd %x, arg %lu\n", __func__, cmd,
808 arg);
809 retval = -EINVAL;
810 }
811
812 mutex_unlock(&mpu->mutex);
813 return retval;
814}
815
816#ifdef CONFIG_HAS_EARLYSUSPEND
817void mpu3050_early_suspend(struct early_suspend *h)
818{
819 struct mpu_private_data *mpu = container_of(h,
820 struct
821 mpu_private_data,
822 early_suspend);
823 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
824 struct i2c_adapter *accel_adapter;
825 struct i2c_adapter *compass_adapter;
826 struct i2c_adapter *pressure_adapter;
827
828 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
829 compass_adapter =
830 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
831 pressure_adapter =
832 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
833
834 dev_dbg(&this_client->adapter->dev, "%s: %d, %d\n", __func__,
835 h->level, mpu->mldl_cfg.gyro_is_suspended);
836 if (MPU3050_EARLY_SUSPEND_IN_DRIVER) {
837 mutex_lock(&mpu->mutex);
838 (void) mpu3050_suspend(mldl_cfg, this_client->adapter,
839 accel_adapter, compass_adapter,
840 pressure_adapter, TRUE, TRUE, TRUE, TRUE);
841 mutex_unlock(&mpu->mutex);
842 }
843}
844
845void mpu3050_early_resume(struct early_suspend *h)
846{
847 struct mpu_private_data *mpu = container_of(h,
848 struct
849 mpu_private_data,
850 early_suspend);
851 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
852 struct i2c_adapter *accel_adapter;
853 struct i2c_adapter *compass_adapter;
854 struct i2c_adapter *pressure_adapter;
855
856 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
857 compass_adapter =
858 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
859 pressure_adapter =
860 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
861
862 if (MPU3050_EARLY_SUSPEND_IN_DRIVER) {
863 if (mpu->pid) {
864 unsigned long sensors = mldl_cfg->requested_sensors;
865 mutex_lock(&mpu->mutex);
866 (void) mpu3050_resume(mldl_cfg,
867 this_client->adapter,
868 accel_adapter,
869 compass_adapter,
870 pressure_adapter,
871 sensors & ML_THREE_AXIS_GYRO,
872 sensors & ML_THREE_AXIS_ACCEL,
873 sensors & ML_THREE_AXIS_COMPASS,
874 sensors & ML_THREE_AXIS_PRESSURE);
875 mutex_unlock(&mpu->mutex);
876 dev_dbg(&this_client->adapter->dev,
877 "%s for pid %d\n", __func__, mpu->pid);
878 }
879 }
880 dev_dbg(&this_client->adapter->dev, "%s: %d\n", __func__, h->level);
881}
882#endif
883
884void mpu_shutdown(struct i2c_client *client)
885{
886 struct mpu_private_data *mpu =
887 (struct mpu_private_data *) i2c_get_clientdata(client);
888 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
889 struct i2c_adapter *accel_adapter;
890 struct i2c_adapter *compass_adapter;
891 struct i2c_adapter *pressure_adapter;
892
893 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
894 compass_adapter =
895 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
896 pressure_adapter =
897 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
898
899 mutex_lock(&mpu->mutex);
900 (void) mpu3050_suspend(mldl_cfg, this_client->adapter,
901 accel_adapter, compass_adapter, pressure_adapter,
902 TRUE, TRUE, TRUE, TRUE);
903 mutex_unlock(&mpu->mutex);
904 dev_dbg(&this_client->adapter->dev, "%s\n", __func__);
905}
906
907int mpu_suspend(struct i2c_client *client, pm_message_t mesg)
908{
909 struct mpu_private_data *mpu =
910 (struct mpu_private_data *) i2c_get_clientdata(client);
911 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
912 struct i2c_adapter *accel_adapter;
913 struct i2c_adapter *compass_adapter;
914 struct i2c_adapter *pressure_adapter;
915
916 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
917 compass_adapter =
918 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
919 pressure_adapter =
920 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
921
922 mutex_lock(&mpu->mutex);
923 if (!mldl_cfg->ignore_system_suspend) {
924 dev_dbg(&this_client->adapter->dev,
925 "%s: suspending on event %d\n", __func__,
926 mesg.event);
927 (void) mpu3050_suspend(mldl_cfg, this_client->adapter,
928 accel_adapter, compass_adapter,
929 pressure_adapter,
930 TRUE, TRUE, TRUE, TRUE);
931 } else {
932 dev_dbg(&this_client->adapter->dev,
933 "%s: Already suspended %d\n", __func__,
934 mesg.event);
935 }
936 mutex_unlock(&mpu->mutex);
937 return 0;
938}
939
940int mpu_resume(struct i2c_client *client)
941{
942 struct mpu_private_data *mpu =
943 (struct mpu_private_data *) i2c_get_clientdata(client);
944 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
945 struct i2c_adapter *accel_adapter;
946 struct i2c_adapter *compass_adapter;
947 struct i2c_adapter *pressure_adapter;
948
949 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
950 compass_adapter =
951 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
952 pressure_adapter =
953 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
954
955 mutex_lock(&mpu->mutex);
956 if (mpu->pid && !mldl_cfg->ignore_system_suspend) {
957 unsigned long sensors = mldl_cfg->requested_sensors;
958 (void) mpu3050_resume(mldl_cfg, this_client->adapter,
959 accel_adapter,
960 compass_adapter,
961 pressure_adapter,
962 sensors & ML_THREE_AXIS_GYRO,
963 sensors & ML_THREE_AXIS_ACCEL,
964 sensors & ML_THREE_AXIS_COMPASS,
965 sensors & ML_THREE_AXIS_PRESSURE);
966 dev_dbg(&this_client->adapter->dev,
967 "%s for pid %d\n", __func__, mpu->pid);
968 }
969 mutex_unlock(&mpu->mutex);
970 return 0;
971}
972
973/* define which file operations are supported */
974static const struct file_operations mpu_fops = {
975 .owner = THIS_MODULE,
976 .read = mpu_read,
977 .poll = mpu_poll,
978
979#if HAVE_COMPAT_IOCTL
980 .compat_ioctl = mpu_ioctl,
981#endif
982#if HAVE_UNLOCKED_IOCTL
983 .unlocked_ioctl = mpu_ioctl,
984#endif
985 .open = mpu_open,
986 .release = mpu_release,
987};
988
989static unsigned short normal_i2c[] = { I2C_CLIENT_END };
990
991#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
992I2C_CLIENT_INSMOD;
993#endif
994
995static struct miscdevice i2c_mpu_device = {
996 .minor = MISC_DYNAMIC_MINOR,
997 .name = "mpu", /* Same for both 3050 and 6000 */
998 .fops = &mpu_fops,
999};
1000
1001
1002int mpu3050_probe(struct i2c_client *client,
1003 const struct i2c_device_id *devid)
1004{
1005 struct mpu3050_platform_data *pdata;
1006 struct mpu_private_data *mpu;
1007 struct mldl_cfg *mldl_cfg;
1008 int res = 0;
1009 struct i2c_adapter *accel_adapter = NULL;
1010 struct i2c_adapter *compass_adapter = NULL;
1011 struct i2c_adapter *pressure_adapter = NULL;
1012 dev_dbg(&client->adapter->dev, "%s\n", __func__);
1013
1014 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
1015 res = -ENODEV;
1016 goto out_check_functionality_failed;
1017 }
1018
1019 mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
1020 if (!mpu) {
1021 res = -ENOMEM;
1022 goto out_alloc_data_failed;
1023 }
1024
1025 i2c_set_clientdata(client, mpu);
1026 this_client = client;
1027 mldl_cfg = &mpu->mldl_cfg;
1028
1029 init_waitqueue_head(&mpu->mpu_event_wait);
1030
1031 mutex_init(&mpu->mutex);
1032 init_completion(&mpu->completion);
1033
1034 mpu->response_timeout = 60; /* Seconds */
1035 mpu->timeout.function = mpu_pm_timeout;
1036 mpu->timeout.data = (u_long) mpu;
1037 init_timer(&mpu->timeout);
1038
1039 /* FIXME:
1040 * Do not register the pm_notifier as it causes
1041 * issues with resume sequence as there is no response
1042 * from user-space for power notifications for approx
1043 * 60 sec. Refer NV bug 858630 for more details.
1044 */
1045#if 0
1046 mpu->nb.notifier_call = mpu_pm_notifier_callback;
1047 mpu->nb.priority = 0;
1048 register_pm_notifier(&mpu->nb);
1049#endif
1050
1051 pdata = (struct mpu3050_platform_data *) client->dev.platform_data;
1052 if (!pdata) {
1053 dev_warn(&this_client->adapter->dev,
1054 "Missing platform data for mpu3050\n");
1055 } else {
1056 mldl_cfg->pdata = pdata;
1057
1058#if defined(CONFIG_MPU_SENSORS_MPU3050_MODULE) || \
1059 defined(CONFIG_MPU_SENSORS_MPU6000_MODULE)
1060 pdata->accel.get_slave_descr = get_accel_slave_descr;
1061 pdata->compass.get_slave_descr = get_compass_slave_descr;
1062 pdata->pressure.get_slave_descr = get_pressure_slave_descr;
1063#endif
1064
1065 if (pdata->accel.get_slave_descr) {
1066 mldl_cfg->accel =
1067 pdata->accel.get_slave_descr();
1068 dev_info(&this_client->adapter->dev,
1069 "%s: +%s\n", MPU_NAME,
1070 mldl_cfg->accel->name);
1071 accel_adapter =
1072 i2c_get_adapter(pdata->accel.adapt_num);
1073 if (pdata->accel.irq > 0) {
1074 dev_info(&this_client->adapter->dev,
1075 "Installing Accel irq using %d\n",
1076 pdata->accel.irq);
1077 res = slaveirq_init(accel_adapter,
1078 &pdata->accel,
1079 "accelirq");
1080 if (res)
1081 goto out_accelirq_failed;
1082 } else {
1083 dev_warn(&this_client->adapter->dev,
1084 "WARNING: Accel irq not assigned\n");
1085 }
1086 } else {
1087 dev_warn(&this_client->adapter->dev,
1088 "%s: No Accel Present\n", MPU_NAME);
1089 }
1090
1091 if (pdata->compass.get_slave_descr) {
1092 mldl_cfg->compass =
1093 pdata->compass.get_slave_descr();
1094 dev_info(&this_client->adapter->dev,
1095 "%s: +%s\n", MPU_NAME,
1096 mldl_cfg->compass->name);
1097 compass_adapter =
1098 i2c_get_adapter(pdata->compass.adapt_num);
1099 if (pdata->compass.irq > 0) {
1100 dev_info(&this_client->adapter->dev,
1101 "Installing Compass irq using %d\n",
1102 pdata->compass.irq);
1103 res = slaveirq_init(compass_adapter,
1104 &pdata->compass,
1105 "compassirq");
1106 if (res)
1107 goto out_compassirq_failed;
1108 } else {
1109 dev_warn(&this_client->adapter->dev,
1110 "WARNING: Compass irq not assigned\n");
1111 }
1112 } else {
1113 dev_warn(&this_client->adapter->dev,
1114 "%s: No Compass Present\n", MPU_NAME);
1115 }
1116
1117 if (pdata->pressure.get_slave_descr) {
1118 mldl_cfg->pressure =
1119 pdata->pressure.get_slave_descr();
1120 dev_info(&this_client->adapter->dev,
1121 "%s: +%s\n", MPU_NAME,
1122 mldl_cfg->pressure->name);
1123 pressure_adapter =
1124 i2c_get_adapter(pdata->pressure.adapt_num);
1125
1126 if (pdata->pressure.irq > 0) {
1127 dev_info(&this_client->adapter->dev,
1128 "Installing Pressure irq using %d\n",
1129 pdata->pressure.irq);
1130 res = slaveirq_init(pressure_adapter,
1131 &pdata->pressure,
1132 "pressureirq");
1133 if (res)
1134 goto out_pressureirq_failed;
1135 } else {
1136 dev_warn(&this_client->adapter->dev,
1137 "WARNING: Pressure irq not assigned\n");
1138 }
1139 } else {
1140 dev_warn(&this_client->adapter->dev,
1141 "%s: No Pressure Present\n", MPU_NAME);
1142 }
1143 }
1144
1145 mldl_cfg->addr = client->addr;
1146 res = mpu3050_open(&mpu->mldl_cfg, client->adapter,
1147 accel_adapter, compass_adapter, pressure_adapter);
1148
1149 if (res) {
1150 dev_err(&this_client->adapter->dev,
1151 "Unable to open %s %d\n", MPU_NAME, res);
1152 res = -ENODEV;
1153 goto out_whoami_failed;
1154 }
1155
1156 res = misc_register(&i2c_mpu_device);
1157 if (res < 0) {
1158 dev_err(&this_client->adapter->dev,
1159 "ERROR: misc_register returned %d\n", res);
1160 goto out_misc_register_failed;
1161 }
1162
1163 if (this_client->irq > 0) {
1164 dev_info(&this_client->adapter->dev,
1165 "Installing irq using %d\n", this_client->irq);
1166 res = mpuirq_init(this_client);
1167 if (res)
1168 goto out_mpuirq_failed;
1169 } else {
1170 dev_warn(&this_client->adapter->dev,
1171 "Missing %s IRQ\n", MPU_NAME);
1172 }
1173
1174
1175#ifdef CONFIG_HAS_EARLYSUSPEND
1176 mpu->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
1177 mpu->early_suspend.suspend = mpu3050_early_suspend;
1178 mpu->early_suspend.resume = mpu3050_early_resume;
1179 register_early_suspend(&mpu->early_suspend);
1180#endif
1181 return res;
1182
1183out_mpuirq_failed:
1184 misc_deregister(&i2c_mpu_device);
1185out_misc_register_failed:
1186 mpu3050_close(&mpu->mldl_cfg, client->adapter,
1187 accel_adapter, compass_adapter, pressure_adapter);
1188out_whoami_failed:
1189 if (pdata &&
1190 pdata->pressure.get_slave_descr &&
1191 pdata->pressure.irq)
1192 slaveirq_exit(&pdata->pressure);
1193out_pressureirq_failed:
1194 if (pdata &&
1195 pdata->compass.get_slave_descr &&
1196 pdata->compass.irq)
1197 slaveirq_exit(&pdata->compass);
1198out_compassirq_failed:
1199 if (pdata &&
1200 pdata->accel.get_slave_descr &&
1201 pdata->accel.irq)
1202 slaveirq_exit(&pdata->accel);
1203out_accelirq_failed:
1204 kfree(mpu);
1205out_alloc_data_failed:
1206out_check_functionality_failed:
1207 dev_err(&this_client->adapter->dev, "%s failed %d\n", __func__,
1208 res);
1209 return res;
1210
1211}
1212
1213static int mpu3050_remove(struct i2c_client *client)
1214{
1215 struct mpu_private_data *mpu = i2c_get_clientdata(client);
1216 struct i2c_adapter *accel_adapter;
1217 struct i2c_adapter *compass_adapter;
1218 struct i2c_adapter *pressure_adapter;
1219 struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
1220 struct mpu3050_platform_data *pdata = mldl_cfg->pdata;
1221
1222 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
1223 compass_adapter =
1224 i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
1225 pressure_adapter =
1226 i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
1227
1228 dev_dbg(&client->adapter->dev, "%s\n", __func__);
1229
1230#ifdef CONFIG_HAS_EARLYSUSPEND
1231 unregister_early_suspend(&mpu->early_suspend);
1232#endif
1233 mpu3050_close(mldl_cfg, client->adapter,
1234 accel_adapter, compass_adapter, pressure_adapter);
1235
1236 if (client->irq)
1237 mpuirq_exit();
1238
1239 if (pdata &&
1240 pdata->pressure.get_slave_descr &&
1241 pdata->pressure.irq)
1242 slaveirq_exit(&pdata->pressure);
1243
1244 if (pdata &&
1245 pdata->compass.get_slave_descr &&
1246 pdata->compass.irq)
1247 slaveirq_exit(&pdata->compass);
1248
1249 if (pdata &&
1250 pdata->accel.get_slave_descr &&
1251 pdata->accel.irq)
1252 slaveirq_exit(&pdata->accel);
1253
1254 misc_deregister(&i2c_mpu_device);
1255 kfree(mpu);
1256
1257 return 0;
1258}
1259
1260static const struct i2c_device_id mpu3050_id[] = {
1261 {MPU_NAME, 0},
1262 {}
1263};
1264
1265MODULE_DEVICE_TABLE(i2c, mpu3050_id);
1266
1267static struct i2c_driver mpu3050_driver = {
1268 .class = I2C_CLASS_HWMON,
1269 .probe = mpu3050_probe,
1270 .remove = mpu3050_remove,
1271 .id_table = mpu3050_id,
1272 .driver = {
1273 .owner = THIS_MODULE,
1274 .name = MPU_NAME,
1275 },
1276#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
1277 .address_data = &addr_data,
1278#else
1279 .address_list = normal_i2c,
1280#endif
1281
1282 .shutdown = mpu_shutdown, /* optional */
1283 .suspend = mpu_suspend, /* optional */
1284 .resume = mpu_resume, /* optional */
1285
1286};
1287
1288static int __init mpu_init(void)
1289{
1290 int res = i2c_add_driver(&mpu3050_driver);
1291 pr_debug("%s\n", __func__);
1292 if (res)
1293 pr_err("%s failed\n",
1294 __func__);
1295 return res;
1296}
1297
1298static void __exit mpu_exit(void)
1299{
1300 pr_debug("%s\n", __func__);
1301 i2c_del_driver(&mpu3050_driver);
1302}
1303
1304module_init(mpu_init);
1305module_exit(mpu_exit);
1306
1307MODULE_AUTHOR("Invensense Corporation");
1308MODULE_DESCRIPTION("User space character device interface for MPU3050");
1309MODULE_LICENSE("GPL");
1310MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/mpu3050/mpu-i2c.c b/drivers/misc/mpu3050/mpu-i2c.c
new file mode 100644
index 00000000000..b1298d313ab
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-i2c.c
@@ -0,0 +1,196 @@
1/*
2 $License:
3 Copyright (C) 2010 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 mpu-i2c.c
26 * @brief
27 *
28 */
29
30#include <linux/i2c.h>
31#include "mpu.h"
32
33int sensor_i2c_write(struct i2c_adapter *i2c_adap,
34 unsigned char address,
35 unsigned int len, unsigned char const *data)
36{
37 struct i2c_msg msgs[1];
38 int res;
39
40 if (NULL == data || NULL == i2c_adap)
41 return -EINVAL;
42
43 msgs[0].addr = address;
44 msgs[0].flags = 0; /* write */
45 msgs[0].buf = (unsigned char *) data;
46 msgs[0].len = len;
47
48 res = i2c_transfer(i2c_adap, msgs, 1);
49 if (res < 1)
50 return res;
51 else
52 return 0;
53}
54
55int sensor_i2c_write_register(struct i2c_adapter *i2c_adap,
56 unsigned char address,
57 unsigned char reg, unsigned char value)
58{
59 unsigned char data[2];
60
61 data[0] = reg;
62 data[1] = value;
63 return sensor_i2c_write(i2c_adap, address, 2, data);
64}
65
66int sensor_i2c_read(struct i2c_adapter *i2c_adap,
67 unsigned char address,
68 unsigned char reg,
69 unsigned int len, unsigned char *data)
70{
71 struct i2c_msg msgs[2];
72 int res;
73
74 if (NULL == data || NULL == i2c_adap)
75 return -EINVAL;
76
77 msgs[0].addr = address;
78 msgs[0].flags = 0; /* write */
79 msgs[0].buf = &reg;
80 msgs[0].len = 1;
81
82 msgs[1].addr = address;
83 msgs[1].flags = I2C_M_RD;
84 msgs[1].buf = data;
85 msgs[1].len = len;
86
87 res = i2c_transfer(i2c_adap, msgs, 2);
88 if (res < 2)
89 return res;
90 else
91 return 0;
92}
93
94int mpu_memory_read(struct i2c_adapter *i2c_adap,
95 unsigned char mpu_addr,
96 unsigned short mem_addr,
97 unsigned int len, unsigned char *data)
98{
99 unsigned char bank[2];
100 unsigned char addr[2];
101 unsigned char buf;
102
103 struct i2c_msg msgs[4];
104 int ret;
105
106 if (NULL == data || NULL == i2c_adap)
107 return -EINVAL;
108
109 bank[0] = MPUREG_BANK_SEL;
110 bank[1] = mem_addr >> 8;
111
112 addr[0] = MPUREG_MEM_START_ADDR;
113 addr[1] = mem_addr & 0xFF;
114
115 buf = MPUREG_MEM_R_W;
116
117 /* Write Message */
118 msgs[0].addr = mpu_addr;
119 msgs[0].flags = 0;
120 msgs[0].buf = bank;
121 msgs[0].len = sizeof(bank);
122
123 msgs[1].addr = mpu_addr;
124 msgs[1].flags = 0;
125 msgs[1].buf = addr;
126 msgs[1].len = sizeof(addr);
127
128 msgs[2].addr = mpu_addr;
129 msgs[2].flags = 0;
130 msgs[2].buf = &buf;
131 msgs[2].len = 1;
132
133 msgs[3].addr = mpu_addr;
134 msgs[3].flags = I2C_M_RD;
135 msgs[3].buf = data;
136 msgs[3].len = len;
137
138 ret = i2c_transfer(i2c_adap, msgs, 4);
139 if (ret != 4)
140 return ret;
141 else
142 return 0;
143}
144
145int mpu_memory_write(struct i2c_adapter *i2c_adap,
146 unsigned char mpu_addr,
147 unsigned short mem_addr,
148 unsigned int len, unsigned char const *data)
149{
150 unsigned char bank[2];
151 unsigned char addr[2];
152 unsigned char buf[513];
153
154 struct i2c_msg msgs[3];
155 int ret;
156
157 if (NULL == data || NULL == i2c_adap)
158 return -EINVAL;
159 if (len >= (sizeof(buf) - 1))
160 return -ENOMEM;
161
162 bank[0] = MPUREG_BANK_SEL;
163 bank[1] = mem_addr >> 8;
164
165 addr[0] = MPUREG_MEM_START_ADDR;
166 addr[1] = mem_addr & 0xFF;
167
168 buf[0] = MPUREG_MEM_R_W;
169 memcpy(buf + 1, data, len);
170
171 /* Write Message */
172 msgs[0].addr = mpu_addr;
173 msgs[0].flags = 0;
174 msgs[0].buf = bank;
175 msgs[0].len = sizeof(bank);
176
177 msgs[1].addr = mpu_addr;
178 msgs[1].flags = 0;
179 msgs[1].buf = addr;
180 msgs[1].len = sizeof(addr);
181
182 msgs[2].addr = mpu_addr;
183 msgs[2].flags = 0;
184 msgs[2].buf = (unsigned char *) buf;
185 msgs[2].len = len + 1;
186
187 ret = i2c_transfer(i2c_adap, msgs, 3);
188 if (ret != 3)
189 return ret;
190 else
191 return 0;
192}
193
194/**
195 * @}
196 */
diff --git a/drivers/misc/mpu3050/mpu-i2c.h b/drivers/misc/mpu3050/mpu-i2c.h
new file mode 100644
index 00000000000..0bbc8c64594
--- /dev/null
+++ b/drivers/misc/mpu3050/mpu-i2c.h
@@ -0,0 +1,58 @@
1/*
2 $License:
3 Copyright (C) 2010 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 * @defgroup
21 * @brief
22 *
23 * @{
24 * @file mpu-i2c.c
25 * @brief
26 *
27 *
28 */
29
30#ifndef __MPU_I2C_H__
31#define __MPU_I2C_H__
32
33#include <linux/i2c.h>
34
35int sensor_i2c_write(struct i2c_adapter *i2c_adap,
36 unsigned char address,
37 unsigned int len, unsigned char const *data);
38
39int sensor_i2c_write_register(struct i2c_adapter *i2c_adap,
40 unsigned char address,
41 unsigned char reg, unsigned char value);
42
43int sensor_i2c_read(struct i2c_adapter *i2c_adap,
44 unsigned char address,
45 unsigned char reg,
46 unsigned int len, unsigned char *data);
47
48int mpu_memory_read(struct i2c_adapter *i2c_adap,
49 unsigned char mpu_addr,
50 unsigned short mem_addr,
51 unsigned int len, unsigned char *data);
52
53int mpu_memory_write(struct i2c_adapter *i2c_adap,
54 unsigned char mpu_addr,
55 unsigned short mem_addr,
56 unsigned int len, unsigned char const *data);
57
58#endif /* __MPU_I2C_H__ */
diff --git a/drivers/misc/mpu3050/mpuirq.c b/drivers/misc/mpu3050/mpuirq.c
new file mode 100644
index 00000000000..ce1ad409cbf
--- /dev/null
+++ b/drivers/misc/mpu3050/mpuirq.c
@@ -0,0 +1,319 @@
1/*
2 $License:
3 Copyright (C) 2010 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/workqueue.h>
31#include <linux/poll.h>
32
33#include <linux/errno.h>
34#include <linux/fs.h>
35#include <linux/mm.h>
36#include <linux/sched.h>
37#include <linux/wait.h>
38#include <linux/uaccess.h>
39#include <linux/io.h>
40
41#include "mpu.h"
42#include "mpuirq.h"
43#include "mldl_cfg.h"
44#include "mpu-i2c.h"
45
46#define MPUIRQ_NAME "mpuirq"
47
48/* function which gets accel data and sends it to MPU */
49
50DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait);
51
52struct mpuirq_dev_data {
53 struct work_struct work;
54 struct i2c_client *mpu_client;
55 struct miscdevice *dev;
56 int irq;
57 int pid;
58 int accel_divider;
59 int data_ready;
60 int timeout;
61};
62
63static struct mpuirq_dev_data mpuirq_dev_data;
64static struct mpuirq_data mpuirq_data;
65static char *interface = MPUIRQ_NAME;
66
67static void mpu_accel_data_work_fcn(struct work_struct *work);
68
69static int mpuirq_open(struct inode *inode, struct file *file)
70{
71 dev_dbg(mpuirq_dev_data.dev->this_device,
72 "%s current->pid %d\n", __func__, current->pid);
73 mpuirq_dev_data.pid = current->pid;
74 file->private_data = &mpuirq_dev_data;
75 return 0;
76}
77
78/* close function - called when the "file" /dev/mpuirq is closed in userspace */
79static int mpuirq_release(struct inode *inode, struct file *file)
80{
81 dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n");
82 return 0;
83}
84
85/* read function called when from /dev/mpuirq is read */
86static ssize_t mpuirq_read(struct file *file,
87 char *buf, size_t count, loff_t *ppos)
88{
89 int len, err;
90 struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
91
92 if (!mpuirq_dev_data.data_ready &&
93 mpuirq_dev_data.timeout &&
94 (!(file->f_flags & O_NONBLOCK))) {
95 wait_event_interruptible_timeout(mpuirq_wait,
96 mpuirq_dev_data.
97 data_ready,
98 mpuirq_dev_data.timeout);
99 }
100
101 if (mpuirq_dev_data.data_ready && NULL != buf
102 && count >= sizeof(mpuirq_data)) {
103 err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data));
104 mpuirq_data.data_type = 0;
105 } else {
106 return 0;
107 }
108 if (err != 0) {
109 dev_err(p_mpuirq_dev_data->dev->this_device,
110 "Copy to user returned %d\n", err);
111 return -EFAULT;
112 }
113 mpuirq_dev_data.data_ready = 0;
114 len = sizeof(mpuirq_data);
115 return len;
116}
117
118unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll)
119{
120 int mask = 0;
121
122 poll_wait(file, &mpuirq_wait, poll);
123 if (mpuirq_dev_data.data_ready)
124 mask |= POLLIN | POLLRDNORM;
125 return mask;
126}
127
128/* ioctl - I/O control */
129static long mpuirq_ioctl(struct file *file,
130 unsigned int cmd, unsigned long arg)
131{
132 int retval = 0;
133 int data;
134
135 switch (cmd) {
136 case MPUIRQ_SET_TIMEOUT:
137 mpuirq_dev_data.timeout = arg;
138 break;
139
140 case MPUIRQ_GET_INTERRUPT_CNT:
141 data = mpuirq_data.interruptcount - 1;
142 if (mpuirq_data.interruptcount > 1)
143 mpuirq_data.interruptcount = 1;
144
145 if (copy_to_user((int *) arg, &data, sizeof(int)))
146 return -EFAULT;
147 break;
148 case MPUIRQ_GET_IRQ_TIME:
149 if (copy_to_user((int *) arg, &mpuirq_data.irqtime,
150 sizeof(mpuirq_data.irqtime)))
151 return -EFAULT;
152 mpuirq_data.irqtime = 0;
153 break;
154 case MPUIRQ_SET_FREQUENCY_DIVIDER:
155 mpuirq_dev_data.accel_divider = arg;
156 break;
157 default:
158 retval = -EINVAL;
159 }
160 return retval;
161}
162
163static void mpu_accel_data_work_fcn(struct work_struct *work)
164{
165 struct mpuirq_dev_data *mpuirq_dev_data =
166 (struct mpuirq_dev_data *) work;
167 struct mldl_cfg *mldl_cfg =
168 (struct mldl_cfg *)
169 i2c_get_clientdata(mpuirq_dev_data->mpu_client);
170 struct i2c_adapter *accel_adapter;
171 unsigned char wbuff[16];
172 unsigned char rbuff[16];
173 int ii;
174
175 accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
176 mldl_cfg->accel->read(accel_adapter,
177 mldl_cfg->accel,
178 &mldl_cfg->pdata->accel, rbuff);
179
180
181 /* @todo add other data formats here as well */
182 if (EXT_SLAVE_BIG_ENDIAN == mldl_cfg->accel->endian) {
183 for (ii = 0; ii < 3; ii++) {
184 wbuff[2 * ii + 1] = rbuff[2 * ii + 1];
185 wbuff[2 * ii + 2] = rbuff[2 * ii + 0];
186 }
187 } else {
188 memcpy(wbuff + 1, rbuff, mldl_cfg->accel->len);
189 }
190
191 wbuff[7] = 0;
192 wbuff[8] = 1; /*set semaphore */
193
194 mpu_memory_write(mpuirq_dev_data->mpu_client->adapter,
195 mldl_cfg->addr, 0x0108, 8, wbuff);
196}
197
198static irqreturn_t mpuirq_handler(int irq, void *dev_id)
199{
200 static int mycount;
201 struct timeval irqtime;
202 mycount++;
203
204 mpuirq_data.interruptcount++;
205
206 /* wake up (unblock) for reading data from userspace */
207 /* and ignore first interrupt generated in module init */
208 mpuirq_dev_data.data_ready = 1;
209
210 do_gettimeofday(&irqtime);
211 mpuirq_data.irqtime = (((long long) irqtime.tv_sec) << 32);
212 mpuirq_data.irqtime += irqtime.tv_usec;
213
214 if ((mpuirq_dev_data.accel_divider >= 0) &&
215 (0 == (mycount % (mpuirq_dev_data.accel_divider + 1)))) {
216 schedule_work((struct work_struct
217 *) (&mpuirq_dev_data));
218 }
219
220 wake_up_interruptible(&mpuirq_wait);
221
222 return IRQ_HANDLED;
223
224}
225
226/* define which file operations are supported */
227const struct file_operations mpuirq_fops = {
228 .owner = THIS_MODULE,
229 .read = mpuirq_read,
230 .poll = mpuirq_poll,
231
232#if HAVE_COMPAT_IOCTL
233 .compat_ioctl = mpuirq_ioctl,
234#endif
235#if HAVE_UNLOCKED_IOCTL
236 .unlocked_ioctl = mpuirq_ioctl,
237#endif
238 .open = mpuirq_open,
239 .release = mpuirq_release,
240};
241
242static struct miscdevice mpuirq_device = {
243 .minor = MISC_DYNAMIC_MINOR,
244 .name = MPUIRQ_NAME,
245 .fops = &mpuirq_fops,
246};
247
248int mpuirq_init(struct i2c_client *mpu_client)
249{
250
251 int res;
252 struct mldl_cfg *mldl_cfg =
253 (struct mldl_cfg *) i2c_get_clientdata(mpu_client);
254
255 /* work_struct initialization */
256 INIT_WORK((struct work_struct *) &mpuirq_dev_data,
257 mpu_accel_data_work_fcn);
258 mpuirq_dev_data.mpu_client = mpu_client;
259
260 dev_info(&mpu_client->adapter->dev,
261 "Module Param interface = %s\n", interface);
262
263 mpuirq_dev_data.irq = mpu_client->irq;
264 mpuirq_dev_data.pid = 0;
265 mpuirq_dev_data.accel_divider = -1;
266 mpuirq_dev_data.data_ready = 0;
267 mpuirq_dev_data.timeout = 0;
268 mpuirq_dev_data.dev = &mpuirq_device;
269
270 if (mpuirq_dev_data.irq) {
271 unsigned long flags;
272 if (BIT_ACTL_LOW ==
273 ((mldl_cfg->pdata->int_config) & BIT_ACTL))
274 flags = IRQF_TRIGGER_FALLING;
275 else
276 flags = IRQF_TRIGGER_RISING;
277
278 res =
279 request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags,
280 interface, &mpuirq_dev_data.irq);
281 if (res) {
282 dev_err(&mpu_client->adapter->dev,
283 "myirqtest: cannot register IRQ %d\n",
284 mpuirq_dev_data.irq);
285 } else {
286 res = misc_register(&mpuirq_device);
287 if (res < 0) {
288 dev_err(&mpu_client->adapter->dev,
289 "misc_register returned %d\n",
290 res);
291 free_irq(mpuirq_dev_data.irq,
292 &mpuirq_dev_data.irq);
293 }
294 }
295
296 } else {
297 res = 0;
298 }
299
300 return res;
301}
302
303void mpuirq_exit(void)
304{
305 /* Free the IRQ first before flushing the work */
306 if (mpuirq_dev_data.irq > 0)
307 free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq);
308
309 flush_scheduled_work();
310
311 dev_info(mpuirq_device.this_device, "Unregistering %s\n",
312 MPUIRQ_NAME);
313 misc_deregister(&mpuirq_device);
314
315 return;
316}
317
318module_param(interface, charp, S_IRUGO | S_IWUSR);
319MODULE_PARM_DESC(interface, "The Interface name");
diff --git a/drivers/misc/mpu3050/mpuirq.h b/drivers/misc/mpu3050/mpuirq.h
new file mode 100644
index 00000000000..a71c79c75e8
--- /dev/null
+++ b/drivers/misc/mpu3050/mpuirq.h
@@ -0,0 +1,42 @@
1/*
2 $License:
3 Copyright (C) 2010 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#ifdef __KERNEL__
24#include <linux/i2c-dev.h>
25#include <linux/time.h>
26#else
27#include <sys/time.h>
28#endif
29
30#define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long)
31#define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long)
32#define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval)
33#define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long)
34
35#ifdef __KERNEL__
36
37void mpuirq_exit(void);
38int mpuirq_init(struct i2c_client *mpu_client);
39
40#endif
41
42#endif
diff --git a/drivers/misc/mpu3050/slaveirq.c b/drivers/misc/mpu3050/slaveirq.c
new file mode 100644
index 00000000000..a3c7bfec4b4
--- /dev/null
+++ b/drivers/misc/mpu3050/slaveirq.c
@@ -0,0 +1,273 @@
1/*
2 $License:
3 Copyright (C) 2010 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 "mpu.h"
43#include "slaveirq.h"
44#include "mldl_cfg.h"
45#include "mpu-i2c.h"
46
47/* function which gets slave data and sends it to SLAVE */
48
49struct slaveirq_dev_data {
50 struct miscdevice dev;
51 struct i2c_client *slave_client;
52 struct mpuirq_data data;
53 wait_queue_head_t slaveirq_wait;
54 int irq;
55 int pid;
56 int data_ready;
57 int timeout;
58};
59
60/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
61 * drivers: misc: pass miscdevice pointer via file private data
62 */
63static int slaveirq_open(struct inode *inode, struct file *file)
64{
65 /* Device node is availabe in the file->private_data, this is
66 * exactly what we want so we leave it there */
67 struct slaveirq_dev_data *data =
68 container_of(file->private_data, struct slaveirq_dev_data, dev);
69
70 dev_dbg(data->dev.this_device,
71 "%s current->pid %d\n", __func__, current->pid);
72 data->pid = current->pid;
73 return 0;
74}
75
76static int slaveirq_release(struct inode *inode, struct file *file)
77{
78 struct slaveirq_dev_data *data =
79 container_of(file->private_data, struct slaveirq_dev_data, dev);
80 dev_dbg(data->dev.this_device, "slaveirq_release\n");
81 return 0;
82}
83
84/* read function called when from /dev/slaveirq is read */
85static ssize_t slaveirq_read(struct file *file,
86 char *buf, size_t count, loff_t *ppos)
87{
88 int len, err;
89 struct slaveirq_dev_data *data =
90 container_of(file->private_data, struct slaveirq_dev_data, dev);
91
92 if (!data->data_ready &&
93 data->timeout &&
94 !(file->f_flags & O_NONBLOCK)) {
95 wait_event_interruptible_timeout(data->slaveirq_wait,
96 data->data_ready,
97 data->timeout);
98 }
99
100 if (data->data_ready && NULL != buf
101 && count >= sizeof(data->data)) {
102 err = copy_to_user(buf, &data->data, sizeof(data->data));
103 data->data.data_type = 0;
104 } else {
105 return 0;
106 }
107 if (err != 0) {
108 dev_err(data->dev.this_device,
109 "Copy to user returned %d\n", err);
110 return -EFAULT;
111 }
112 data->data_ready = 0;
113 len = sizeof(data->data);
114 return len;
115}
116
117static unsigned int slaveirq_poll(struct file *file,
118 struct poll_table_struct *poll)
119{
120 int mask = 0;
121 struct slaveirq_dev_data *data =
122 container_of(file->private_data, struct slaveirq_dev_data, dev);
123
124 poll_wait(file, &data->slaveirq_wait, poll);
125 if (data->data_ready)
126 mask |= POLLIN | POLLRDNORM;
127 return mask;
128}
129
130/* ioctl - I/O control */
131static long slaveirq_ioctl(struct file *file,
132 unsigned int cmd, unsigned long arg)
133{
134 int retval = 0;
135 int tmp;
136 struct slaveirq_dev_data *data =
137 container_of(file->private_data, struct slaveirq_dev_data, dev);
138
139 switch (cmd) {
140 case SLAVEIRQ_SET_TIMEOUT:
141 data->timeout = arg;
142 break;
143
144 case SLAVEIRQ_GET_INTERRUPT_CNT:
145 tmp = data->data.interruptcount - 1;
146 if (data->data.interruptcount > 1)
147 data->data.interruptcount = 1;
148
149 if (copy_to_user((int *) arg, &tmp, sizeof(int)))
150 return -EFAULT;
151 break;
152 case SLAVEIRQ_GET_IRQ_TIME:
153 if (copy_to_user((int *) arg, &data->data.irqtime,
154 sizeof(data->data.irqtime)))
155 return -EFAULT;
156 data->data.irqtime = 0;
157 break;
158 default:
159 retval = -EINVAL;
160 }
161 return retval;
162}
163
164static irqreturn_t slaveirq_handler(int irq, void *dev_id)
165{
166 struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id;
167 static int mycount;
168 struct timeval irqtime;
169 mycount++;
170
171 data->data.interruptcount++;
172
173 /* wake up (unblock) for reading data from userspace */
174 data->data_ready = 1;
175
176 do_gettimeofday(&irqtime);
177 data->data.irqtime = (((long long) irqtime.tv_sec) << 32);
178 data->data.irqtime += irqtime.tv_usec;
179 data->data.data_type |= 1;
180
181 wake_up_interruptible(&data->slaveirq_wait);
182
183 return IRQ_HANDLED;
184
185}
186
187/* define which file operations are supported */
188static const struct file_operations slaveirq_fops = {
189 .owner = THIS_MODULE,
190 .read = slaveirq_read,
191 .poll = slaveirq_poll,
192
193#if HAVE_COMPAT_IOCTL
194 .compat_ioctl = slaveirq_ioctl,
195#endif
196#if HAVE_UNLOCKED_IOCTL
197 .unlocked_ioctl = slaveirq_ioctl,
198#endif
199 .open = slaveirq_open,
200 .release = slaveirq_release,
201};
202
203int slaveirq_init(struct i2c_adapter *slave_adapter,
204 struct ext_slave_platform_data *pdata,
205 char *name)
206{
207
208 int res;
209 struct slaveirq_dev_data *data;
210
211 if (!pdata->irq)
212 return -EINVAL;
213
214 pdata->irq_data = kzalloc(sizeof(*data),
215 GFP_KERNEL);
216 data = (struct slaveirq_dev_data *) pdata->irq_data;
217 if (!data)
218 return -ENOMEM;
219
220 data->dev.minor = MISC_DYNAMIC_MINOR;
221 data->dev.name = name;
222 data->dev.fops = &slaveirq_fops;
223 data->irq = pdata->irq;
224 data->pid = 0;
225 data->data_ready = 0;
226 data->timeout = 0;
227
228 init_waitqueue_head(&data->slaveirq_wait);
229
230 res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING,
231 data->dev.name, data);
232
233 if (res) {
234 dev_err(&slave_adapter->dev,
235 "myirqtest: cannot register IRQ %d\n",
236 data->irq);
237 goto out_request_irq;
238 }
239
240 res = misc_register(&data->dev);
241 if (res < 0) {
242 dev_err(&slave_adapter->dev,
243 "misc_register returned %d\n",
244 res);
245 goto out_misc_register;
246 }
247
248 return res;
249
250out_misc_register:
251 free_irq(data->irq, data);
252out_request_irq:
253 kfree(pdata->irq_data);
254 pdata->irq_data = NULL;
255
256 return res;
257}
258
259void slaveirq_exit(struct ext_slave_platform_data *pdata)
260{
261 struct slaveirq_dev_data *data = pdata->irq_data;
262
263 if (!pdata->irq_data || data->irq <= 0)
264 return;
265
266 dev_info(data->dev.this_device, "Unregistering %s\n",
267 data->dev.name);
268
269 free_irq(data->irq, data);
270 misc_deregister(&data->dev);
271 kfree(pdata->irq_data);
272 pdata->irq_data = NULL;
273}
diff --git a/drivers/misc/mpu3050/slaveirq.h b/drivers/misc/mpu3050/slaveirq.h
new file mode 100644
index 00000000000..b4e1115f1b0
--- /dev/null
+++ b/drivers/misc/mpu3050/slaveirq.h
@@ -0,0 +1,43 @@
1/*
2 $License:
3 Copyright (C) 2010 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#ifdef __KERNEL__
24#include <linux/i2c-dev.h>
25#endif
26
27#include "mpu.h"
28#include "mpuirq.h"
29
30#define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long)
31#define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long)
32#define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long)
33
34#ifdef __KERNEL__
35
36void slaveirq_exit(struct ext_slave_platform_data *pdata);
37int slaveirq_init(struct i2c_adapter *slave_adapter,
38 struct ext_slave_platform_data *pdata,
39 char *name);
40
41#endif
42
43#endif
diff --git a/drivers/misc/mpu3050/timerirq.c b/drivers/misc/mpu3050/timerirq.c
new file mode 100644
index 00000000000..41c3ac98101
--- /dev/null
+++ b/drivers/misc/mpu3050/timerirq.c
@@ -0,0 +1,299 @@
1/*
2 $License:
3 Copyright (C) 2010 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 "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 &&
164 data->timeout &&
165 !(file->f_flags & O_NONBLOCK)) {
166 wait_event_interruptible_timeout(data->timerirq_wait,
167 data->data_ready,
168 data->timeout);
169 }
170
171 if (data->data_ready && NULL != buf
172 && count >= sizeof(data->data)) {
173 err = copy_to_user(buf, &data->data, sizeof(data->data));
174 data->data.data_type = 0;
175 } else {
176 return 0;
177 }
178 if (err != 0) {
179 dev_err(data->dev->this_device,
180 "Copy to user returned %d\n", err);
181 return -EFAULT;
182 }
183 data->data_ready = 0;
184 len = sizeof(data->data);
185 return len;
186}
187
188static unsigned int timerirq_poll(struct file *file,
189 struct poll_table_struct *poll)
190{
191 int mask = 0;
192 struct timerirq_data *data = file->private_data;
193
194 poll_wait(file, &data->timerirq_wait, poll);
195 if (data->data_ready)
196 mask |= POLLIN | POLLRDNORM;
197 return mask;
198}
199
200/* ioctl - I/O control */
201static long timerirq_ioctl(struct file *file,
202 unsigned int cmd, unsigned long arg)
203{
204 int retval = 0;
205 int tmp;
206 struct timerirq_data *data = file->private_data;
207
208 dev_dbg(data->dev->this_device,
209 "%s current->pid %d, %d, %ld\n",
210 __func__, current->pid, cmd, arg);
211
212 if (!data)
213 return -EFAULT;
214
215 switch (cmd) {
216 case TIMERIRQ_SET_TIMEOUT:
217 data->timeout = arg;
218 break;
219 case TIMERIRQ_GET_INTERRUPT_CNT:
220 tmp = data->data.interruptcount - 1;
221 if (data->data.interruptcount > 1)
222 data->data.interruptcount = 1;
223
224 if (copy_to_user((int *) arg, &tmp, sizeof(int)))
225 return -EFAULT;
226 break;
227 case TIMERIRQ_START:
228 data->period = arg;
229 retval = start_timerirq(data);
230 break;
231 case TIMERIRQ_STOP:
232 retval = stop_timerirq(data);
233 break;
234 default:
235 retval = -EINVAL;
236 }
237 return retval;
238}
239
240/* define which file operations are supported */
241static const struct file_operations timerirq_fops = {
242 .owner = THIS_MODULE,
243 .read = timerirq_read,
244 .poll = timerirq_poll,
245
246#if HAVE_COMPAT_IOCTL
247 .compat_ioctl = timerirq_ioctl,
248#endif
249#if HAVE_UNLOCKED_IOCTL
250 .unlocked_ioctl = timerirq_ioctl,
251#endif
252 .open = timerirq_open,
253 .release = timerirq_release,
254};
255
256static int __init timerirq_init(void)
257{
258
259 int res;
260 static struct miscdevice *data;
261
262 data = kzalloc(sizeof(*data), GFP_KERNEL);
263 if (!data)
264 return -ENOMEM;
265 timerirq_dev_data = data;
266 data->minor = MISC_DYNAMIC_MINOR;
267 data->name = "timerirq";
268 data->fops = &timerirq_fops;
269
270 res = misc_register(data);
271 if (res < 0) {
272 dev_err(data->this_device,
273 "misc_register returned %d\n",
274 res);
275 return res;
276 }
277
278 return res;
279}
280module_init(timerirq_init);
281
282static void __exit timerirq_exit(void)
283{
284 struct miscdevice *data = timerirq_dev_data;
285
286 dev_info(data->this_device, "Unregistering %s\n",
287 data->name);
288
289 misc_deregister(data);
290 kfree(data);
291
292 timerirq_dev_data = NULL;
293}
294module_exit(timerirq_exit);
295
296MODULE_AUTHOR("Invensense Corporation");
297MODULE_DESCRIPTION("Timer IRQ device driver.");
298MODULE_LICENSE("GPL");
299MODULE_ALIAS("timerirq");
diff --git a/drivers/misc/mpu3050/timerirq.h b/drivers/misc/mpu3050/timerirq.h
new file mode 100644
index 00000000000..ec2c1e29f08
--- /dev/null
+++ b/drivers/misc/mpu3050/timerirq.h
@@ -0,0 +1,30 @@
1/*
2 $License:
3 Copyright (C) 2010 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 "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
diff --git a/drivers/misc/nct1008.c b/drivers/misc/nct1008.c
new file mode 100644
index 00000000000..3a8aa6b9dbd
--- /dev/null
+++ b/drivers/misc/nct1008.c
@@ -0,0 +1,944 @@
1/*
2 * drivers/misc/nct1008.c
3 *
4 * Driver for NCT1008, temperature monitoring device from ON Semiconductors
5 *
6 * Copyright (c) 2010-2011, NVIDIA Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23
24#include <linux/interrupt.h>
25#include <linux/module.h>
26#include <linux/i2c.h>
27#include <linux/slab.h>
28#include <linux/err.h>
29#include <linux/gpio.h>
30#include <linux/device.h>
31#include <linux/nct1008.h>
32#include <linux/delay.h>
33#include <linux/regulator/consumer.h>
34
35#define DRIVER_NAME "nct1008"
36
37/* Register Addresses */
38#define LOCAL_TEMP_RD 0x00
39#define EXT_TEMP_RD_HI 0x01
40#define EXT_TEMP_RD_LO 0x10
41#define STATUS_RD 0x02
42#define CONFIG_RD 0x03
43
44#define LOCAL_TEMP_HI_LIMIT_RD 0x05
45#define LOCAL_TEMP_LO_LIMIT_RD 0x06
46
47#define EXT_TEMP_HI_LIMIT_HI_BYTE_RD 0x07
48#define EXT_TEMP_LO_LIMIT_HI_BYTE_RD 0x08
49
50#define CONFIG_WR 0x09
51#define CONV_RATE_WR 0x0A
52#define LOCAL_TEMP_HI_LIMIT_WR 0x0B
53#define LOCAL_TEMP_LO_LIMIT_WR 0x0C
54#define EXT_TEMP_HI_LIMIT_HI_BYTE_WR 0x0D
55#define EXT_TEMP_LO_LIMIT_HI_BYTE_WR 0x0E
56#define ONE_SHOT 0x0F
57#define OFFSET_WR 0x11
58#define OFFSET_QUARTER_WR 0x12
59#define EXT_THERM_LIMIT_WR 0x19
60#define LOCAL_THERM_LIMIT_WR 0x20
61#define THERM_HYSTERESIS_WR 0x21
62
63/* Configuration Register Bits */
64#define EXTENDED_RANGE_BIT BIT(2)
65#define THERM2_BIT BIT(5)
66#define STANDBY_BIT BIT(6)
67#define ALERT_BIT BIT(7)
68
69/* Max Temperature Measurements */
70#define EXTENDED_RANGE_OFFSET 64U
71#define STANDARD_RANGE_MAX 127U
72#define EXTENDED_RANGE_MAX (150U + EXTENDED_RANGE_OFFSET)
73
74#define NCT1008_MIN_TEMP -64
75#define NCT1008_MAX_TEMP 191
76
77#define MAX_STR_PRINT 50
78
79#define MAX_CONV_TIME_ONESHOT_MS (52)
80#define CELSIUS_TO_MILLICELSIUS(x) ((x)*1000)
81#define MILLICELSIUS_TO_CELSIUS(x) ((x)/1000)
82
83
84static int conv_period_ms_table[] =
85 {16000, 8000, 4000, 2000, 1000, 500, 250, 125, 63, 32, 16};
86
87static inline s8 value_to_temperature(bool extended, u8 value)
88{
89 return extended ? (s8)(value - EXTENDED_RANGE_OFFSET) : (s8)value;
90}
91
92static inline u8 temperature_to_value(bool extended, s8 temp)
93{
94 return extended ? (u8)(temp + EXTENDED_RANGE_OFFSET) : (u8)temp;
95}
96
97static int nct1008_get_temp(struct device *dev, long *pTemp)
98{
99 struct i2c_client *client = to_i2c_client(dev);
100 struct nct1008_platform_data *pdata = client->dev.platform_data;
101 s8 temp_local;
102 u8 temp_ext_lo;
103 s8 temp_ext_hi;
104 long temp_ext_milli;
105 long temp_local_milli;
106 u8 value;
107
108 /* Read Local Temp */
109 value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
110 if (value < 0)
111 goto error;
112 temp_local = value_to_temperature(pdata->ext_range, value);
113 temp_local_milli = CELSIUS_TO_MILLICELSIUS(temp_local);
114
115 /* Read External Temp */
116 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO);
117 if (value < 0)
118 goto error;
119 temp_ext_lo = (value >> 6);
120
121 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
122 if (value < 0)
123 goto error;
124 temp_ext_hi = value_to_temperature(pdata->ext_range, value);
125
126 temp_ext_milli = CELSIUS_TO_MILLICELSIUS(temp_ext_hi) +
127 temp_ext_lo * 250;
128
129 /* Return max between Local and External Temp */
130 *pTemp = max(temp_local_milli, temp_ext_milli);
131
132 dev_dbg(dev, "\n %s: ret temp=%ldC ", __func__, *pTemp);
133 return 0;
134error:
135 dev_err(&client->dev, "\n error in file=: %s %s() line=%d: "
136 "error=%d ", __FILE__, __func__, __LINE__, value);
137 return value;
138}
139
140static ssize_t nct1008_show_temp(struct device *dev,
141 struct device_attribute *attr, char *buf)
142{
143 struct i2c_client *client = to_i2c_client(dev);
144 struct nct1008_platform_data *pdata = client->dev.platform_data;
145 s8 temp1 = 0;
146 s8 temp = 0;
147 u8 temp2 = 0;
148 int value = 0;
149
150 if (!dev || !buf || !attr)
151 return -EINVAL;
152
153 value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
154 if (value < 0)
155 goto error;
156 temp1 = value_to_temperature(pdata->ext_range, value);
157
158 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO);
159 if (value < 0)
160 goto error;
161 temp2 = (value >> 6);
162 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
163 if (value < 0)
164 goto error;
165 temp = value_to_temperature(pdata->ext_range, value);
166
167 return snprintf(buf, MAX_STR_PRINT, "%d %d.%d\n",
168 temp1, temp, temp2 * 25);
169
170error:
171 return snprintf(buf, MAX_STR_PRINT,
172 "Error read local/ext temperature\n");
173}
174
175static ssize_t nct1008_show_temp_overheat(struct device *dev,
176 struct device_attribute *attr,
177 char *buf)
178{
179 struct i2c_client *client = to_i2c_client(dev);
180 struct nct1008_platform_data *pdata = client->dev.platform_data;
181 int value;
182 s8 temp, temp2;
183
184 /* Local temperature h/w shutdown limit */
185 value = i2c_smbus_read_byte_data(client, LOCAL_THERM_LIMIT_WR);
186 if (value < 0)
187 goto error;
188 temp = value_to_temperature(pdata->ext_range, value);
189
190 /* External temperature h/w shutdown limit */
191 value = i2c_smbus_read_byte_data(client, EXT_THERM_LIMIT_WR);
192 if (value < 0)
193 goto error;
194 temp2 = value_to_temperature(pdata->ext_range, value);
195
196 return snprintf(buf, MAX_STR_PRINT, "%d %d\n", temp, temp2);
197error:
198 dev_err(dev, "%s: failed to read temperature-overheat "
199 "\n", __func__);
200 return snprintf(buf, MAX_STR_PRINT, " Rd overheat Error\n");
201}
202
203static ssize_t nct1008_set_temp_overheat(struct device *dev,
204 struct device_attribute *attr,
205 const char *buf, size_t count)
206{
207 long int num;
208 int err;
209 u8 temp;
210 long currTemp;
211 struct i2c_client *client = to_i2c_client(dev);
212 struct nct1008_platform_data *pdata = client->dev.platform_data;
213 char bufTemp[MAX_STR_PRINT];
214 char bufOverheat[MAX_STR_PRINT];
215 unsigned int ret;
216
217 if (strict_strtol(buf, 0, &num)) {
218 dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
219 __LINE__, __func__);
220 return -EINVAL;
221 }
222 if (((int)num < NCT1008_MIN_TEMP) || ((int)num >= NCT1008_MAX_TEMP)) {
223 dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
224 __LINE__, __func__);
225 return -EINVAL;
226 }
227 /* check for system power down */
228 err = nct1008_get_temp(dev, &currTemp);
229 if (err)
230 goto error;
231
232 currTemp = MILLICELSIUS_TO_CELSIUS(currTemp);
233
234 if (currTemp >= (int)num) {
235 ret = nct1008_show_temp(dev, attr, bufTemp);
236 ret = nct1008_show_temp_overheat(dev, attr, bufOverheat);
237 dev_err(dev, "\nCurrent temp: %s ", bufTemp);
238 dev_err(dev, "\nOld overheat limit: %s ", bufOverheat);
239 dev_err(dev, "\nReset from overheat: curr temp=%ld, "
240 "new overheat temp=%d\n\n", currTemp, (int)num);
241 }
242
243 /* External temperature h/w shutdown limit */
244 temp = temperature_to_value(pdata->ext_range, (s8)num);
245 err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, temp);
246 if (err < 0)
247 goto error;
248
249 /* Local temperature h/w shutdown limit */
250 temp = temperature_to_value(pdata->ext_range, (s8)num);
251 err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, temp);
252 if (err < 0)
253 goto error;
254 return count;
255error:
256 dev_err(dev, " %s: failed to set temperature-overheat\n", __func__);
257 return err;
258}
259
260static ssize_t nct1008_show_temp_alert(struct device *dev,
261 struct device_attribute *attr,
262 char *buf)
263{
264 struct i2c_client *client = to_i2c_client(dev);
265 struct nct1008_platform_data *pdata = client->dev.platform_data;
266 int value;
267 s8 temp_hi, temp_lo;
268 /* External Temperature Throttling hi-limit */
269 value = i2c_smbus_read_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_RD);
270 if (value < 0)
271 goto error;
272 temp_hi = value_to_temperature(pdata->ext_range, value);
273
274 /* External Temperature Throttling lo-limit */
275 value = i2c_smbus_read_byte_data(client, EXT_TEMP_LO_LIMIT_HI_BYTE_RD);
276 if (value < 0)
277 goto error;
278 temp_lo = value_to_temperature(pdata->ext_range, value);
279
280 return snprintf(buf, MAX_STR_PRINT, "lo:%d hi:%d\n", temp_lo, temp_hi);
281error:
282 dev_err(dev, "%s: failed to read temperature-alert\n", __func__);
283 return snprintf(buf, MAX_STR_PRINT, " Rd alert Error\n");
284}
285
286static ssize_t nct1008_set_temp_alert(struct device *dev,
287 struct device_attribute *attr,
288 const char *buf, size_t count)
289{
290 long int num;
291 int value;
292 int err;
293 struct i2c_client *client = to_i2c_client(dev);
294 struct nct1008_platform_data *pdata = client->dev.platform_data;
295
296 if (strict_strtol(buf, 0, &num)) {
297 dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
298 __LINE__, __func__);
299 return -EINVAL;
300 }
301 if (((int)num < NCT1008_MIN_TEMP) || ((int)num >= NCT1008_MAX_TEMP)) {
302 dev_err(dev, "\n file: %s, line=%d return %s() ", __FILE__,
303 __LINE__, __func__);
304 return -EINVAL;
305 }
306
307 /* External Temperature Throttling limit */
308 value = temperature_to_value(pdata->ext_range, (s8)num);
309 err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR,
310 value);
311 if (err < 0)
312 goto error;
313
314 /* Local Temperature Throttling limit */
315 err = i2c_smbus_write_byte_data(client, LOCAL_TEMP_HI_LIMIT_WR,
316 value);
317 if (err < 0)
318 goto error;
319
320 return count;
321error:
322 dev_err(dev, "%s: failed to set temperature-alert "
323 "\n", __func__);
324 return err;
325}
326
327static ssize_t nct1008_show_ext_temp(struct device *dev,
328 struct device_attribute *attr, char *buf)
329{
330 struct i2c_client *client = to_i2c_client(dev);
331 struct nct1008_platform_data *pdata = client->dev.platform_data;
332 s8 temp_value;
333 int data = 0;
334 int data_lo;
335
336 if (!dev || !buf || !attr)
337 return -EINVAL;
338
339 /* When reading the full external temperature value, read the
340 * LSB first. This causes the MSB to be locked (that is, the
341 * ADC does not write to it) until it is read */
342 data_lo = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO);
343 if (data_lo < 0) {
344 dev_err(&client->dev, "%s: failed to read "
345 "ext_temperature, i2c error=%d\n", __func__, data_lo);
346 goto error;
347 }
348
349 data = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
350 if (data < 0) {
351 dev_err(&client->dev, "%s: failed to read "
352 "ext_temperature, i2c error=%d\n", __func__, data);
353 goto error;
354 }
355
356 temp_value = value_to_temperature(pdata->ext_range, data);
357
358 return snprintf(buf, MAX_STR_PRINT, "%d.%d\n", temp_value,
359 (25 * (data_lo >> 6)));
360error:
361 return snprintf(buf, MAX_STR_PRINT, "Error read ext temperature\n");
362}
363
364static DEVICE_ATTR(temperature, S_IRUGO, nct1008_show_temp, NULL);
365static DEVICE_ATTR(temperature_overheat, (S_IRUGO | (S_IWUSR | S_IWGRP)),
366 nct1008_show_temp_overheat, nct1008_set_temp_overheat);
367static DEVICE_ATTR(temperature_alert, (S_IRUGO | (S_IWUSR | S_IWGRP)),
368 nct1008_show_temp_alert, nct1008_set_temp_alert);
369static DEVICE_ATTR(ext_temperature, S_IRUGO, nct1008_show_ext_temp, NULL);
370
371static struct attribute *nct1008_attributes[] = {
372 &dev_attr_temperature.attr,
373 &dev_attr_temperature_overheat.attr,
374 &dev_attr_temperature_alert.attr,
375 &dev_attr_ext_temperature.attr,
376 NULL
377};
378
379static const struct attribute_group nct1008_attr_group = {
380 .attrs = nct1008_attributes,
381};
382
383#ifdef CONFIG_DEBUG_FS
384#include <linux/debugfs.h>
385#include <linux/seq_file.h>
386static void print_reg(const char *reg_name, struct seq_file *s,
387 int offset)
388{
389 struct nct1008_data *nct_data = s->private;
390 int ret;
391
392 ret = i2c_smbus_read_byte_data(nct_data->client,
393 offset);
394 if (ret >= 0)
395 seq_printf(s, "Reg %s Addr = 0x%02x Reg 0x%02x "
396 "Value 0x%02x\n", reg_name,
397 nct_data->client->addr,
398 offset, ret);
399 else
400 seq_printf(s, "%s: line=%d, i2c read error=%d\n",
401 __func__, __LINE__, ret);
402}
403
404static int dbg_nct1008_show(struct seq_file *s, void *unused)
405{
406 seq_printf(s, "nct1008 Registers\n");
407 seq_printf(s, "------------------\n");
408 print_reg("Local Temp Value ", s, 0x00);
409 print_reg("Ext Temp Value Hi ", s, 0x01);
410 print_reg("Status ", s, 0x02);
411 print_reg("Configuration ", s, 0x03);
412 print_reg("Conversion Rate ", s, 0x04);
413 print_reg("Local Temp Hi Limit ", s, 0x05);
414 print_reg("Local Temp Lo Limit ", s, 0x06);
415 print_reg("Ext Temp Hi Limit Hi", s, 0x07);
416 print_reg("Ext Temp Hi Limit Lo", s, 0x13);
417 print_reg("Ext Temp Lo Limit Hi", s, 0x08);
418 print_reg("Ext Temp Lo Limit Lo", s, 0x14);
419 print_reg("Ext Temp Value Lo ", s, 0x10);
420 print_reg("Ext Temp Offset Hi ", s, 0x11);
421 print_reg("Ext Temp Offset Lo ", s, 0x12);
422 print_reg("Ext THERM Limit ", s, 0x19);
423 print_reg("Local THERM Limit ", s, 0x20);
424 print_reg("THERM Hysteresis ", s, 0x21);
425 print_reg("Consecutive ALERT ", s, 0x22);
426 return 0;
427}
428
429static int dbg_nct1008_open(struct inode *inode, struct file *file)
430{
431 return single_open(file, dbg_nct1008_show, inode->i_private);
432}
433
434static const struct file_operations debug_fops = {
435 .open = dbg_nct1008_open,
436 .read = seq_read,
437 .llseek = seq_lseek,
438 .release = single_release,
439};
440
441static int __init nct1008_debuginit(struct nct1008_data *nct)
442{
443 int err = 0;
444 struct dentry *d;
445 d = debugfs_create_file("nct1008", S_IRUGO, NULL,
446 (void *)nct, &debug_fops);
447 if ((!d) || IS_ERR(d)) {
448 dev_err(&nct->client->dev, "Error: %s debugfs_create_file"
449 " returned an error\n", __func__);
450 err = -ENOENT;
451 goto end;
452 }
453 if (d == ERR_PTR(-ENODEV)) {
454 dev_err(&nct->client->dev, "Error: %s debugfs not supported "
455 "error=-ENODEV\n", __func__);
456 err = -ENODEV;
457 } else {
458 nct->dent = d;
459 }
460end:
461 return err;
462}
463#else
464static int __init nct1008_debuginit(struct nct1008_data *nct)
465{
466 return 0;
467}
468#endif
469
470static int nct1008_enable(struct i2c_client *client)
471{
472 struct nct1008_data *data = i2c_get_clientdata(client);
473 int err;
474
475 err = i2c_smbus_write_byte_data(client, CONFIG_WR,
476 data->config & ~STANDBY_BIT);
477 if (err < 0)
478 dev_err(&client->dev, "%s, line=%d, i2c write error=%d\n",
479 __func__, __LINE__, err);
480 return err;
481}
482
483static int nct1008_disable(struct i2c_client *client)
484{
485 struct nct1008_data *data = i2c_get_clientdata(client);
486 int err;
487
488 err = i2c_smbus_write_byte_data(client, CONFIG_WR,
489 data->config | STANDBY_BIT);
490 if (err < 0)
491 dev_err(&client->dev, "%s, line=%d, i2c write error=%d\n",
492 __func__, __LINE__, err);
493 return err;
494}
495
496static int nct1008_within_limits(struct nct1008_data *data)
497{
498 int intr_status;
499
500 intr_status = i2c_smbus_read_byte_data(data->client, STATUS_RD);
501
502 return !(intr_status & (BIT(3) | BIT(4)));
503}
504
505static void nct1008_work_func(struct work_struct *work)
506{
507 struct nct1008_data *data = container_of(work, struct nct1008_data,
508 work);
509 int intr_status;
510 struct timespec ts;
511
512 nct1008_disable(data->client);
513
514 if (data->alert_func)
515 if (!nct1008_within_limits(data))
516 data->alert_func(data->alert_data);
517
518 /* Initiate one-shot conversion */
519 i2c_smbus_write_byte_data(data->client, ONE_SHOT, 0x1);
520
521 /* Give hardware necessary time to finish conversion */
522 ts = ns_to_timespec(MAX_CONV_TIME_ONESHOT_MS * 1000 * 1000);
523 hrtimer_nanosleep(&ts, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
524
525 intr_status = i2c_smbus_read_byte_data(data->client, STATUS_RD);
526
527 nct1008_enable(data->client);
528
529 enable_irq(data->client->irq);
530}
531
532static irqreturn_t nct1008_irq(int irq, void *dev_id)
533{
534 struct nct1008_data *data = dev_id;
535
536 disable_irq_nosync(irq);
537 queue_work(data->workqueue, &data->work);
538
539 return IRQ_HANDLED;
540}
541
542static void nct1008_power_control(struct nct1008_data *data, bool is_enable)
543{
544 int ret;
545 if (!data->nct_reg) {
546 data->nct_reg = regulator_get(&data->client->dev, "vdd");
547 if (IS_ERR_OR_NULL(data->nct_reg)) {
548 dev_warn(&data->client->dev, "Error [%d] in"
549 "getting the regulator handle for vdd "
550 "of %s\n", (int)data->nct_reg,
551 dev_name(&data->client->dev));
552 data->nct_reg = NULL;
553 return;
554 }
555 }
556 if (is_enable)
557 ret = regulator_enable(data->nct_reg);
558 else
559 ret = regulator_disable(data->nct_reg);
560
561 if (ret < 0)
562 dev_err(&data->client->dev, "Error in %s rail vdd_nct1008, "
563 "error %d\n", (is_enable) ? "enabling" : "disabling",
564 ret);
565 else
566 dev_info(&data->client->dev, "success in %s rail vdd_nct1008\n",
567 (is_enable) ? "enabling" : "disabling");
568}
569
570static int __devinit nct1008_configure_sensor(struct nct1008_data* data)
571{
572 struct i2c_client *client = data->client;
573 struct nct1008_platform_data *pdata = client->dev.platform_data;
574 u8 value;
575 s8 temp;
576 u8 temp2;
577 int err;
578
579 if (!pdata || !pdata->supported_hwrev)
580 return -ENODEV;
581
582 /* Place in Standby */
583 data->config = STANDBY_BIT;
584 err = i2c_smbus_write_byte_data(client, CONFIG_WR, data->config);
585 if (err)
586 goto error;
587
588 /* External temperature h/w shutdown limit */
589 value = temperature_to_value(pdata->ext_range, NCT1008_MAX_TEMP);
590 err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value);
591 if (err)
592 goto error;
593
594 /* Local temperature h/w shutdown limit */
595 value = temperature_to_value(pdata->ext_range, NCT1008_MAX_TEMP);
596 err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value);
597 if (err)
598 goto error;
599
600 /* set extended range mode if needed */
601 if (pdata->ext_range)
602 data->config |= EXTENDED_RANGE_BIT;
603 data->config &= ~(THERM2_BIT | ALERT_BIT);
604
605 err = i2c_smbus_write_byte_data(client, CONFIG_WR, data->config);
606 if (err)
607 goto error;
608
609 /* Temperature conversion rate */
610 err = i2c_smbus_write_byte_data(client, CONV_RATE_WR, pdata->conv_rate);
611 if (err)
612 goto error;
613
614 data->conv_period_ms = conv_period_ms_table[pdata->conv_rate];
615
616 /* Setup local hi and lo limits */
617 err = i2c_smbus_write_byte_data(client,
618 LOCAL_TEMP_HI_LIMIT_WR, NCT1008_MAX_TEMP);
619 if (err)
620 goto error;
621
622 err = i2c_smbus_write_byte_data(client,
623 LOCAL_TEMP_LO_LIMIT_WR, 0);
624 if (err)
625 goto error;
626
627 /* Setup external hi and lo limits */
628 err = i2c_smbus_write_byte_data(client,
629 EXT_TEMP_LO_LIMIT_HI_BYTE_WR, 0);
630 if (err)
631 goto error;
632 err = i2c_smbus_write_byte_data(client, EXT_TEMP_HI_LIMIT_HI_BYTE_WR,
633 NCT1008_MAX_TEMP);
634 if (err)
635 goto error;
636
637 /* read initial temperature */
638 value = i2c_smbus_read_byte_data(client, LOCAL_TEMP_RD);
639 if (value < 0) {
640 err = value;
641 goto error;
642 }
643 temp = value_to_temperature(pdata->ext_range, value);
644 dev_dbg(&client->dev, "\n initial local temp = %d ", temp);
645
646 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_LO);
647 if (value < 0) {
648 err = value;
649 goto error;
650 }
651 temp2 = (value >> 6);
652 value = i2c_smbus_read_byte_data(client, EXT_TEMP_RD_HI);
653 if (value < 0) {
654 err = value;
655 goto error;
656 }
657 temp = value_to_temperature(pdata->ext_range, value);
658
659 if (temp2 > 0)
660 dev_dbg(&client->dev, "\n initial ext temp = %d.%d deg",
661 temp, temp2 * 25);
662 else
663 dev_dbg(&client->dev, "\n initial ext temp = %d.0 deg", temp);
664
665 /* Remote channel offset */
666 err = i2c_smbus_write_byte_data(client, OFFSET_WR, pdata->offset / 4);
667 if (err < 0)
668 goto error;
669
670 /* Remote channel offset fraction (quarters) */
671 err = i2c_smbus_write_byte_data(client, OFFSET_QUARTER_WR,
672 (pdata->offset % 4) << 6);
673 if (err < 0)
674 goto error;
675
676 /* register sysfs hooks */
677 err = sysfs_create_group(&client->dev.kobj, &nct1008_attr_group);
678 if (err < 0) {
679 dev_err(&client->dev, "\n sysfs create err=%d ", err);
680 goto error;
681 }
682
683 return 0;
684error:
685 dev_err(&client->dev, "\n exit %s, err=%d ", __func__, err);
686 return err;
687}
688
689static int __devinit nct1008_configure_irq(struct nct1008_data *data)
690{
691 data->workqueue = create_singlethread_workqueue("nct1008");
692
693 INIT_WORK(&data->work, nct1008_work_func);
694
695 if (data->client->irq < 0)
696 return 0;
697 else
698 return request_irq(data->client->irq, nct1008_irq,
699 IRQF_TRIGGER_LOW,
700 DRIVER_NAME, data);
701}
702
703int nct1008_thermal_get_temp(struct nct1008_data *data, long *temp)
704{
705 return nct1008_get_temp(&data->client->dev, temp);
706}
707
708int nct1008_thermal_get_temp_low(struct nct1008_data *data, long *temp)
709{
710 *temp = 0;
711 return 0;
712}
713
714int nct1008_thermal_set_limits(struct nct1008_data *data,
715 long lo_limit_milli,
716 long hi_limit_milli)
717{
718 int err;
719 u8 value;
720 bool extended_range = data->plat_data.ext_range;
721 long lo_limit = MILLICELSIUS_TO_CELSIUS(lo_limit_milli);
722 long hi_limit = MILLICELSIUS_TO_CELSIUS(hi_limit_milli);
723
724 if (lo_limit >= hi_limit)
725 return -EINVAL;
726
727 if (data->current_lo_limit != lo_limit) {
728 value = temperature_to_value(extended_range, lo_limit);
729 pr_debug("%s: set lo_limit %ld\n", __func__, lo_limit);
730 err = i2c_smbus_write_byte_data(data->client,
731 EXT_TEMP_LO_LIMIT_HI_BYTE_WR, value);
732 if (err)
733 return err;
734
735 data->current_lo_limit = lo_limit;
736 }
737
738 if (data->current_hi_limit != hi_limit) {
739 value = temperature_to_value(extended_range, hi_limit);
740 pr_debug("%s: set hi_limit %ld\n", __func__, hi_limit);
741 err = i2c_smbus_write_byte_data(data->client,
742 EXT_TEMP_HI_LIMIT_HI_BYTE_WR, value);
743 if (err)
744 return err;
745
746 data->current_hi_limit = hi_limit;
747 }
748
749 return 0;
750}
751
752int nct1008_thermal_set_alert(struct nct1008_data *data,
753 void (*alert_func)(void *),
754 void *alert_data)
755{
756 data->alert_data = alert_data;
757 data->alert_func = alert_func;
758
759 return 0;
760}
761
762int nct1008_thermal_set_shutdown_temp(struct nct1008_data *data,
763 long shutdown_temp_milli)
764{
765 struct i2c_client *client = data->client;
766 struct nct1008_platform_data *pdata = client->dev.platform_data;
767 int err;
768 u8 value;
769 long shutdown_temp;
770
771 shutdown_temp = MILLICELSIUS_TO_CELSIUS(shutdown_temp_milli);
772
773 /* External temperature h/w shutdown limit */
774 value = temperature_to_value(pdata->ext_range, shutdown_temp);
775 err = i2c_smbus_write_byte_data(client, EXT_THERM_LIMIT_WR, value);
776 if (err)
777 return err;
778
779 /* Local temperature h/w shutdown limit */
780 value = temperature_to_value(pdata->ext_range, shutdown_temp);
781 err = i2c_smbus_write_byte_data(client, LOCAL_THERM_LIMIT_WR, value);
782 if (err)
783 return err;
784
785 return 0;
786}
787
788/*
789 * Manufacturer(OnSemi) recommended sequence for
790 * Extended Range mode is as follows
791 * 1. Place in Standby
792 * 2. Scale the THERM and ALERT limits
793 * appropriately(for Extended Range mode).
794 * 3. Enable Extended Range mode.
795 * ALERT mask/THERM2 mode may be done here
796 * as these are not critical
797 * 4. Set Conversion Rate as required
798 * 5. Take device out of Standby
799 */
800
801/*
802 * function nct1008_probe takes care of initial configuration
803 */
804static int __devinit nct1008_probe(struct i2c_client *client,
805 const struct i2c_device_id *id)
806{
807 struct nct1008_data *data;
808 int err;
809 unsigned int delay;
810
811 data = kzalloc(sizeof(struct nct1008_data), GFP_KERNEL);
812 if (!data)
813 return -ENOMEM;
814
815 data->client = client;
816 memcpy(&data->plat_data, client->dev.platform_data,
817 sizeof(struct nct1008_platform_data));
818 i2c_set_clientdata(client, data);
819
820 nct1008_power_control(data, true);
821 /* extended range recommended steps 1 through 4 taken care
822 * in nct1008_configure_sensor function */
823 err = nct1008_configure_sensor(data); /* sensor is in standby */
824 if (err < 0) {
825 dev_err(&client->dev, "\n error file: %s : %s(), line=%d ",
826 __FILE__, __func__, __LINE__);
827 goto error;
828 }
829
830 err = nct1008_configure_irq(data);
831 if (err < 0) {
832 dev_err(&client->dev, "\n error file: %s : %s(), line=%d ",
833 __FILE__, __func__, __LINE__);
834 goto error;
835 }
836 dev_info(&client->dev, "%s: initialized\n", __func__);
837
838 /* extended range recommended step 5 is in nct1008_enable function */
839 err = nct1008_enable(client); /* sensor is running */
840 if (err < 0) {
841 dev_err(&client->dev, "Error: %s, line=%d, error=%d\n",
842 __func__, __LINE__, err);
843 goto error;
844 }
845
846 err = nct1008_debuginit(data);
847 if (err < 0)
848 err = 0; /* without debugfs we may continue */
849
850 /* notify callback that probe is done */
851 if (data->plat_data.probe_callback)
852 data->plat_data.probe_callback(data);
853
854 return 0;
855
856error:
857 dev_err(&client->dev, "\n exit %s, err=%d ", __func__, err);
858 nct1008_power_control(data, false);
859 if (data->nct_reg)
860 regulator_put(data->nct_reg);
861 kfree(data);
862 return err;
863}
864
865static int __devexit nct1008_remove(struct i2c_client *client)
866{
867 struct nct1008_data *data = i2c_get_clientdata(client);
868
869 if (data->dent)
870 debugfs_remove(data->dent);
871
872 free_irq(data->client->irq, data);
873 cancel_work_sync(&data->work);
874 sysfs_remove_group(&client->dev.kobj, &nct1008_attr_group);
875 nct1008_power_control(data, false);
876 if (data->nct_reg)
877 regulator_put(data->nct_reg);
878
879 kfree(data);
880
881 return 0;
882}
883
884#ifdef CONFIG_PM
885static int nct1008_suspend(struct i2c_client *client, pm_message_t state)
886{
887 int err;
888
889 disable_irq(client->irq);
890 err = nct1008_disable(client);
891 return err;
892}
893
894static int nct1008_resume(struct i2c_client *client)
895{
896 struct nct1008_data *data = i2c_get_clientdata(client);
897 int err;
898
899 err = nct1008_enable(client);
900 if (err < 0) {
901 dev_err(&client->dev, "Error: %s, error=%d\n",
902 __func__, err);
903 return err;
904 }
905 enable_irq(client->irq);
906
907 return 0;
908}
909#endif
910
911static const struct i2c_device_id nct1008_id[] = {
912 { DRIVER_NAME, 0 },
913 { }
914};
915MODULE_DEVICE_TABLE(i2c, nct1008_id);
916
917static struct i2c_driver nct1008_driver = {
918 .driver = {
919 .name = DRIVER_NAME,
920 },
921 .probe = nct1008_probe,
922 .remove = __devexit_p(nct1008_remove),
923 .id_table = nct1008_id,
924#ifdef CONFIG_PM
925 .suspend = nct1008_suspend,
926 .resume = nct1008_resume,
927#endif
928};
929
930static int __init nct1008_init(void)
931{
932 return i2c_add_driver(&nct1008_driver);
933}
934
935static void __exit nct1008_exit(void)
936{
937 i2c_del_driver(&nct1008_driver);
938}
939
940MODULE_DESCRIPTION("Temperature sensor driver for OnSemi NCT1008");
941MODULE_LICENSE("GPL");
942
943module_init(nct1008_init);
944module_exit(nct1008_exit);
diff --git a/drivers/misc/tegra-baseband/Kconfig b/drivers/misc/tegra-baseband/Kconfig
new file mode 100644
index 00000000000..1f116918296
--- /dev/null
+++ b/drivers/misc/tegra-baseband/Kconfig
@@ -0,0 +1,32 @@
1menuconfig TEGRA_BB_SUPPORT
2 bool "Tegra baseband support"
3 depends on ARCH_TEGRA
4 ---help---
5 Say Y here to get to see options for tegra baseband support.
6 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 TEGRA_BB_SUPPORT
11
12config TEGRA_BB_POWER
13 bool "Enable tegra baseband power driver"
14 ---help---
15 Adds power management driver for managing different baseband
16 modems with tegra processor.
17
18 This driver should work with at least the following devices:
19
20 * STE M7400
21 * ...
22
23 Disabled by default. Choose Y here if you want to build the driver.
24
25config TEGRA_BB_M7400
26 bool "Enable driver for M7400 modem"
27 ---help---
28 Enables driver for M7400 modem.
29
30 Disabled by default. Choose Y here if you want to build the driver.
31
32endif # TEGRA_BB_SUPPORT
diff --git a/drivers/misc/tegra-baseband/Makefile b/drivers/misc/tegra-baseband/Makefile
new file mode 100644
index 00000000000..a95d84dbf11
--- /dev/null
+++ b/drivers/misc/tegra-baseband/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for tegra baseband support.
3#
4
5obj-$(CONFIG_TEGRA_BB_POWER) += bb-power.o
6obj-$(CONFIG_TEGRA_BB_M7400) += bb-m7400.o
diff --git a/drivers/misc/tegra-baseband/bb-m7400.c b/drivers/misc/tegra-baseband/bb-m7400.c
new file mode 100644
index 00000000000..5808a6e321c
--- /dev/null
+++ b/drivers/misc/tegra-baseband/bb-m7400.c
@@ -0,0 +1,340 @@
1/*
2 * drivers/misc/tegra-baseband/bb-m7400.c
3 *
4 * Copyright (c) 2011, NVIDIA Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <linux/resource.h>
22#include <linux/platform_device.h>
23#include <linux/delay.h>
24#include <linux/gpio.h>
25#include <linux/interrupt.h>
26#include <linux/irq.h>
27#include <linux/err.h>
28#include <linux/device.h>
29#include <linux/usb.h>
30#include <linux/wakelock.h>
31#include <asm/mach-types.h>
32#include <asm/mach/arch.h>
33#include <mach/tegra-bb-power.h>
34#include <mach/usb_phy.h>
35#include "bb-power.h"
36
37static struct tegra_bb_gpio_data m7400_gpios[] = {
38 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_PWR_ON" }, true },
39 { { GPIO_INVALID, GPIOF_IN, "MDM_PWRSTATUS" }, true },
40 { { GPIO_INVALID, GPIOF_OUT_INIT_HIGH, "MDM_SERVICE" }, true },
41 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_AWR" }, false },
42 { { GPIO_INVALID, GPIOF_IN, "MDM_USB_CWR" }, false },
43 { { GPIO_INVALID, GPIOF_IN, "MDM_RESOUT2" }, true },
44 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_ARR" }, false },
45 { { GPIO_INVALID, 0, NULL }, false }, /* End of table */
46};
47static bool ehci_registered;
48static int modem_status;
49static int gpio_awr;
50static int gpio_cwr;
51static int gpio_arr;
52static struct usb_device *m7400_usb_device;
53
54static int gpio_wait_timeout(int gpio, int value, int timeout_msec)
55{
56 int count;
57 for (count = 0; count < timeout_msec; ++count) {
58 if (gpio_get_value(gpio) == value)
59 return 0;
60 mdelay(1);
61 }
62 return -1;
63}
64
65static int m7400_enum_handshake(void)
66{
67 int retval = 0;
68
69 /* Wait for CP to indicate ready - by driving CWR high. */
70 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
71 pr_info("%s: Error: timeout waiting for modem resume.\n",
72 __func__);
73 retval = -1;
74 }
75
76 /* Signal AP ready - Drive AWR and ARR high. */
77 gpio_set_value(gpio_awr, 1);
78 gpio_set_value(gpio_arr, 1);
79
80 return retval;
81}
82
83static int m7400_apup_handshake(bool checkresponse)
84{
85 int retval = 0;
86
87 /* Signal AP ready - Drive AWR and ARR high. */
88 gpio_set_value(gpio_awr, 1);
89 gpio_set_value(gpio_arr, 1);
90
91 if (checkresponse) {
92 /* Wait for CP ack - by driving CWR high. */
93 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
94 pr_info("%s: Error: timeout waiting for modem ack.\n",
95 __func__);
96 retval = -1;
97 }
98 }
99 return retval;
100}
101
102static void m7400_apdown_handshake(void)
103{
104 /* Signal AP going down to modem - Drive AWR low. */
105 /* No need to wait for a CP response */
106 gpio_set_value(gpio_awr, 0);
107}
108
109static int m7400_l2_suspend(void)
110{
111 /* Gets called for two cases :
112 a) Port suspend.
113 b) Bus suspend. */
114 if (modem_status == BBSTATE_L2)
115 return 0;
116
117 /* Post bus suspend: Drive ARR low. */
118 gpio_set_value(gpio_arr, 0);
119 modem_status = BBSTATE_L2;
120 return 0;
121}
122
123static int m7400_l2_resume(void)
124{
125 /* Gets called for two cases :
126 a) L2 resume.
127 b) bus resume phase of L3 resume. */
128 if (modem_status == BBSTATE_L0)
129 return 0;
130
131 /* Pre bus resume: Drive ARR high. */
132 gpio_set_value(gpio_arr, 1);
133
134 /* If host initiated resume - Wait for CP ack (CWR goes high). */
135 /* If device initiated resume - CWR will be already high. */
136 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
137 pr_info("%s: Error: timeout waiting for modem ack.\n",
138 __func__);
139 return -1;
140 }
141 modem_status = BBSTATE_L0;
142 return 0;
143}
144
145static void m7400_l3_suspend(void)
146{
147 m7400_apdown_handshake();
148 modem_status = BBSTATE_L3;
149}
150
151static void m7400_l3_resume(void)
152{
153 m7400_apup_handshake(true);
154 modem_status = BBSTATE_L0;
155}
156
157static irqreturn_t m7400_wake_irq(int irq, void *dev_id)
158{
159 struct usb_interface *intf;
160
161 switch (modem_status) {
162 case BBSTATE_L2:
163 /* Resume usb host activity. */
164 if (m7400_usb_device) {
165 usb_lock_device(m7400_usb_device);
166 intf = usb_ifnum_to_if(m7400_usb_device, 0);
167 usb_autopm_get_interface(intf);
168 usb_autopm_put_interface(intf);
169 usb_unlock_device(m7400_usb_device);
170 }
171 break;
172 default:
173 break;
174 }
175
176 return IRQ_HANDLED;
177}
178
179static int m7400_power(int code)
180{
181 switch (code) {
182 case PWRSTATE_L2L3:
183 m7400_l3_suspend();
184 break;
185 case PWRSTATE_L3L0:
186 m7400_l3_resume();
187 break;
188 default:
189 break;
190 }
191 return 0;
192}
193
194static void m7400_ehci_customize(struct platform_device *pdev)
195{
196 struct tegra_ehci_platform_data *ehci_pdata;
197 struct tegra_uhsic_config *hsic_config;
198
199 ehci_pdata = (struct tegra_ehci_platform_data *)
200 pdev->dev.platform_data;
201 hsic_config = (struct tegra_uhsic_config *)
202 ehci_pdata->phy_config;
203
204 /* Register PHY callbacks */
205 hsic_config->postsuspend = m7400_l2_suspend;
206 hsic_config->preresume = m7400_l2_resume;
207
208 /* Override required settings */
209 ehci_pdata->power_down_on_bus_suspend = 0;
210}
211
212static int m7400_attrib_write(struct device *dev, int value)
213{
214 struct tegra_bb_pdata *pdata;
215 static struct platform_device *ehci_device;
216 static bool first_enum = true;
217
218 if (value > 1 || (!ehci_registered && !value)) {
219 /* Supported values are 0/1. */
220 return -1;
221 }
222
223 pdata = (struct tegra_bb_pdata *) dev->platform_data;
224 if (value) {
225
226 /* Check readiness for enumeration */
227 if (first_enum)
228 first_enum = false;
229 else
230 m7400_enum_handshake();
231
232 /* Register ehci controller */
233 ehci_device = pdata->ehci_register();
234 if (ehci_device == NULL) {
235 pr_info("%s - Error: ehci register failed.\n",
236 __func__);
237 return -1;
238 }
239
240 /* Customize PHY setup/callbacks */
241 m7400_ehci_customize(ehci_device);
242
243 ehci_registered = true;
244 } else {
245 /* Unregister ehci controller */
246 if (ehci_device != NULL)
247 pdata->ehci_unregister(ehci_device);
248
249 /* Signal AP going down */
250 m7400_apdown_handshake();
251 ehci_registered = false;
252 }
253
254 return 0;
255}
256
257static int m7400_registered(struct usb_device *udev)
258{
259 m7400_usb_device = udev;
260 modem_status = BBSTATE_L0;
261 return 0;
262}
263
264static struct tegra_bb_gpio_irqdata m7400_gpioirqs[] = {
265 { GPIO_INVALID, "tegra_bb_wake", m7400_wake_irq,
266 IRQF_TRIGGER_RISING, true, NULL },
267 { GPIO_INVALID, NULL, NULL, 0, NULL }, /* End of table */
268};
269
270static struct tegra_bb_power_gdata m7400_gdata = {
271 .gpio = m7400_gpios,
272 .gpioirq = m7400_gpioirqs,
273};
274
275static struct tegra_bb_power_mdata m7400_mdata = {
276 .vid = 0x04cc,
277 .pid = 0x230f,
278 .wake_capable = true,
279 .autosuspend_ready = true,
280 .reg_cb = m7400_registered,
281};
282
283static struct tegra_bb_power_data m7400_data = {
284 .gpio_data = &m7400_gdata,
285 .modem_data = &m7400_mdata,
286};
287
288static void *m7400_init(void *pdata)
289{
290 struct tegra_bb_pdata *platdata = (struct tegra_bb_pdata *) pdata;
291 union tegra_bb_gpio_id *id = platdata->id;
292
293 /* Fill the gpio ids allocated by hardware */
294 m7400_gpios[0].data.gpio = id->m7400.pwr_on;
295 m7400_gpios[1].data.gpio = id->m7400.pwr_status;
296 m7400_gpios[2].data.gpio = id->m7400.service;
297 m7400_gpios[3].data.gpio = id->m7400.usb_awr;
298 m7400_gpios[4].data.gpio = id->m7400.usb_cwr;
299 m7400_gpios[5].data.gpio = id->m7400.resout2;
300 m7400_gpios[6].data.gpio = id->m7400.uart_awr;
301 m7400_gpioirqs[0].id = id->m7400.usb_cwr;
302
303 if (!platdata->ehci_register || !platdata->ehci_unregister) {
304 pr_info("%s - Error: ehci reg/unreg functions missing.\n"
305 , __func__);
306 return 0;
307 }
308
309 gpio_awr = m7400_gpios[3].data.gpio;
310 gpio_cwr = m7400_gpios[4].data.gpio;
311 gpio_arr = m7400_gpios[6].data.gpio;
312 if (gpio_awr == GPIO_INVALID || gpio_cwr == GPIO_INVALID
313 || gpio_arr == GPIO_INVALID) {
314 pr_info("%s - Error: Invalid gpio data.\n", __func__);
315 return 0;
316 }
317
318 ehci_registered = false;
319 modem_status = BBSTATE_UNKNOWN;
320 return (void *) &m7400_data;
321}
322
323static void *m7400_deinit(void)
324{
325 return (void *) &m7400_data;
326}
327
328static struct tegra_bb_callback m7400_callbacks = {
329 .init = m7400_init,
330 .deinit = m7400_deinit,
331 .attrib = m7400_attrib_write,
332#ifdef CONFIG_PM
333 .power = m7400_power,
334#endif
335};
336
337void *m7400_get_cblist(void)
338{
339 return (void *) &m7400_callbacks;
340}
diff --git a/drivers/misc/tegra-baseband/bb-power.c b/drivers/misc/tegra-baseband/bb-power.c
new file mode 100644
index 00000000000..9210a8f3e84
--- /dev/null
+++ b/drivers/misc/tegra-baseband/bb-power.c
@@ -0,0 +1,337 @@
1/*
2 * drivers/misc/tegra-baseband/bb-power.c
3 *
4 * Copyright (C) 2011 NVIDIA Corporation
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
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 */
16
17#include <linux/kernel.h>
18#include <linux/init.h>
19#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/platform_device.h>
22#include <linux/gpio.h>
23#include <linux/interrupt.h>
24#include <linux/workqueue.h>
25#include <linux/delay.h>
26#include <linux/fs.h>
27#include <linux/usb.h>
28#include <linux/uaccess.h>
29#include <linux/platform_data/tegra_usb.h>
30#include <mach/usb_phy.h>
31#include <mach/tegra-bb-power.h>
32#include "bb-power.h"
33
34static struct tegra_bb_callback *callback;
35static int attr_load_val;
36static struct tegra_bb_power_mdata *mdata;
37static bb_get_cblist get_cblist[] = {
38 NULL,
39 NULL,
40 NULL,
41 M7400_CB,
42};
43
44static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata)
45{
46 int ret;
47 int irq;
48 unsigned gpio_id;
49 const char *gpio_label;
50 unsigned long gpio_flags;
51 struct tegra_bb_gpio_data *gpiolist;
52 struct tegra_bb_gpio_irqdata *gpioirq;
53
54 gpiolist = gdata->gpio;
55 for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) {
56 gpio_id = (gpiolist->data.gpio);
57 gpio_label = (gpiolist->data.label);
58 gpio_flags = (gpiolist->data.flags);
59
60 /* Request the gpio */
61 ret = gpio_request(gpio_id, gpio_label);
62 if (ret) {
63 pr_err("%s: Error: gpio_request for gpio %d failed.\n",
64 __func__, gpio_id);
65 return ret;
66 }
67
68 /* Set gpio direction, as requested */
69 if (gpio_flags == GPIOF_IN)
70 gpio_direction_input(gpio_id);
71 else
72 gpio_direction_output(gpio_id, (!gpio_flags ? 0 : 1));
73
74 /* Enable the gpio */
75 tegra_gpio_enable(gpio_id);
76
77 /* Create a sysfs node, if requested */
78 if (gpiolist->doexport)
79 gpio_export(gpio_id, false);
80 }
81
82 gpioirq = gdata->gpioirq;
83 for (; gpioirq->id != GPIO_INVALID; ++gpioirq) {
84
85 /* Create interrupt handler, if requested */
86 if (gpioirq->handler != NULL) {
87 irq = gpio_to_irq(gpioirq->id);
88 ret = request_threaded_irq(irq, NULL, gpioirq->handler,
89 gpioirq->flags, gpioirq->name, gpioirq->cookie);
90 if (ret < 0) {
91 pr_err("%s: Error: threaded_irq req fail.\n"
92 , __func__);
93 return ret;
94 }
95
96 if (gpioirq->wake_capable) {
97 ret = enable_irq_wake(irq);
98 if (ret) {
99 pr_err("%s: Error: irqwake req fail.\n",
100 __func__);
101 return ret;
102 }
103 }
104 }
105 }
106 return 0;
107}
108
109static int tegra_bb_power_gpio_deinit(struct tegra_bb_power_gdata *gdata)
110{
111 struct tegra_bb_gpio_data *gpiolist;
112 struct tegra_bb_gpio_irqdata *gpioirq;
113
114 gpiolist = gdata->gpio;
115 for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) {
116
117 /* Free the gpio */
118 gpio_free(gpiolist->data.gpio);
119 }
120
121 gpioirq = gdata->gpioirq;
122 for (; gpioirq->id != GPIO_INVALID; ++gpioirq) {
123
124 /* Free the irq */
125 free_irq(gpio_to_irq(gpioirq->id), gpioirq->cookie);
126 }
127 return 0;
128}
129
130static ssize_t tegra_bb_attr_write(struct device *dev,
131 struct device_attribute *attr,
132 const char *buf, size_t count)
133{
134 int val;
135
136 if (sscanf(buf, "%d", &val) != 1)
137 return -EINVAL;
138
139 if (callback && callback->attrib) {
140 if (!callback->attrib(dev, val))
141 attr_load_val = val;
142 }
143 return count;
144}
145
146static ssize_t tegra_bb_attr_read(struct device *dev,
147 struct device_attribute *attr, char *buf)
148{
149 return sprintf(buf, "%d", attr_load_val);
150}
151
152static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP,
153 tegra_bb_attr_read, tegra_bb_attr_write);
154
155static void tegra_usbdevice_added(struct usb_device *udev)
156{
157 const struct usb_device_descriptor *desc = &udev->descriptor;
158
159 if (desc->idVendor == mdata->vid &&
160 desc->idProduct == mdata->pid) {
161 pr_debug("%s: Device %s added.\n", udev->product, __func__);
162
163 if (mdata->wake_capable)
164 device_set_wakeup_enable(&udev->dev, true);
165 if (mdata->autosuspend_ready)
166 usb_enable_autosuspend(udev);
167 if (mdata->reg_cb)
168 mdata->reg_cb(udev);
169 }
170}
171
172static void tegra_usbdevice_removed(struct usb_device *udev)
173{
174 const struct usb_device_descriptor *desc = &udev->descriptor;
175
176 if (desc->idVendor == mdata->vid &&
177 desc->idProduct == mdata->pid) {
178 pr_debug("%s: Device %s removed.\n", udev->product, __func__);
179 }
180}
181
182static int tegra_usb_notify(struct notifier_block *self, unsigned long action,
183 void *dev)
184{
185 switch (action) {
186 case USB_DEVICE_ADD:
187 tegra_usbdevice_added((struct usb_device *)dev);
188 break;
189 case USB_DEVICE_REMOVE:
190 tegra_usbdevice_removed((struct usb_device *)dev);
191 break;
192 }
193 return NOTIFY_OK;
194}
195
196static struct notifier_block tegra_usb_nb = {
197 .notifier_call = tegra_usb_notify,
198};
199
200static int tegra_bb_power_probe(struct platform_device *device)
201{
202 struct device *dev = &device->dev;
203 struct tegra_bb_pdata *pdata;
204 struct tegra_bb_power_data *data;
205 struct tegra_bb_power_gdata *gdata;
206 int err;
207 unsigned int bb_id;
208
209 pdata = (struct tegra_bb_pdata *) dev->platform_data;
210 if (!pdata) {
211 pr_err("%s - Error: platform data is empty.\n", __func__);
212 return -ENODEV;
213 }
214
215 /* Obtain BB specific callback list */
216 bb_id = pdata->bb_id;
217 if (get_cblist[bb_id] != NULL) {
218 callback = (struct tegra_bb_callback *) get_cblist[bb_id]();
219 if (callback && callback->init) {
220 data = (struct tegra_bb_power_data *)
221 callback->init((void *)pdata);
222
223 gdata = data->gpio_data;
224 if (!gdata) {
225 pr_err("%s - Error: Gpio data is empty.\n",
226 __func__);
227 return -ENODEV;
228 }
229
230 /* Initialize gpio as required */
231 tegra_bb_power_gpio_init(gdata);
232
233 mdata = data->modem_data;
234 if (mdata && mdata->vid && mdata->pid)
235 /* Register to notifications from usb core */
236 usb_register_notify(&tegra_usb_nb);
237 } else {
238 pr_err("%s - Error: init callback is empty.\n",
239 __func__);
240 return -ENODEV;
241 }
242 } else {
243 pr_err("%s - Error: callback data is empty.\n", __func__);
244 return -ENODEV;
245 }
246
247 /* Create the control sysfs node */
248 err = device_create_file(dev, &dev_attr_load);
249 if (err < 0) {
250 pr_err("%s - Error: device_create_file failed.\n", __func__);
251 return -ENODEV;
252 }
253 attr_load_val = 0;
254
255 return 0;
256}
257
258static int tegra_bb_power_remove(struct platform_device *device)
259{
260 struct device *dev = &device->dev;
261 struct tegra_bb_power_data *data;
262 struct tegra_bb_power_gdata *gdata;
263
264 /* BB specific callback */
265 if (callback && callback->deinit) {
266 data = (struct tegra_bb_power_data *)
267 callback->deinit();
268
269 /* Deinitialize gpios */
270 gdata = data->gpio_data;
271 if (gdata)
272 tegra_bb_power_gpio_deinit(gdata);
273 else {
274 pr_err("%s - Error: Gpio data is empty.\n", __func__);
275 return -ENODEV;
276 }
277
278 mdata = data->modem_data;
279 if (mdata && mdata->vid && mdata->pid)
280 /* Register to notifications from usb core */
281 usb_unregister_notify(&tegra_usb_nb);
282 }
283
284 /* Remove the control sysfs node */
285 device_remove_file(dev, &dev_attr_load);
286
287 return 0;
288}
289
290#ifdef CONFIG_PM
291static int tegra_bb_power_suspend(struct platform_device *device,
292 pm_message_t state)
293{
294 /* BB specific callback */
295 if (callback && callback->power)
296 callback->power(PWRSTATE_L2L3);
297 return 0;
298}
299
300static int tegra_bb_power_resume(struct platform_device *device)
301{
302 /* BB specific callback */
303 if (callback && callback->power)
304 callback->power(PWRSTATE_L3L0);
305 return 0;
306}
307#endif
308
309static struct platform_driver tegra_bb_power_driver = {
310 .probe = tegra_bb_power_probe,
311 .remove = tegra_bb_power_remove,
312#ifdef CONFIG_PM
313 .suspend = tegra_bb_power_suspend,
314 .resume = tegra_bb_power_resume,
315#endif
316 .driver = {
317 .name = "tegra_baseband_power",
318 },
319};
320
321static int __init tegra_baseband_power_init(void)
322{
323 pr_debug("%s\n", __func__);
324 return platform_driver_register(&tegra_bb_power_driver);
325}
326
327static void __exit tegra_baseband_power_exit(void)
328{
329 pr_debug("%s\n", __func__);
330 platform_driver_unregister(&tegra_bb_power_driver);
331}
332
333module_init(tegra_baseband_power_init)
334module_exit(tegra_baseband_power_exit)
335MODULE_AUTHOR("NVIDIA Corporation");
336MODULE_DESCRIPTION("Tegra modem power management driver");
337MODULE_LICENSE("GPL");
diff --git a/drivers/misc/tegra-baseband/bb-power.h b/drivers/misc/tegra-baseband/bb-power.h
new file mode 100644
index 00000000000..cdd69380203
--- /dev/null
+++ b/drivers/misc/tegra-baseband/bb-power.h
@@ -0,0 +1,99 @@
1/*
2 * drivers/misc/tegra-baseband/bb-power.h
3 *
4 * Copyright (C) 2011 NVIDIA Corporation
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
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 */
16
17enum tegra_bb_state {
18 BBSTATE_UNKNOWN,
19 /* Baseband state L0 - Running */
20 BBSTATE_L0,
21 /* Baseband state L2 - Suspended */
22 BBSTATE_L2,
23 /* Baseband state L3 - Suspended and detached */
24 BBSTATE_L3,
25};
26
27enum tegra_bb_pwrstate {
28 /* System power state - Entering suspend */
29 PWRSTATE_L2L3,
30 /* System power state - Resuming from suspend */
31 PWRSTATE_L3L0,
32 PWRSTATE_INVALID,
33};
34
35struct tegra_bb_gpio_data {
36 /* Baseband gpio data */
37 struct gpio data;
38 /* Baseband gpio - Should it be exported to sysfs ? */
39 bool doexport;
40};
41
42struct tegra_bb_gpio_irqdata {
43 /* Baseband gpio IRQ - Id */
44 int id;
45 /* Baseband gpio IRQ - Friendly name */
46 const char *name;
47 /* Baseband gpio IRQ - IRQ handler */
48 irq_handler_t handler;
49 /* Baseband gpio IRQ - IRQ trigger flags */
50 int flags;
51 /* Baseband gpio IRQ - Can the gpio wake system from sleep ? */
52 bool wake_capable;
53 void *cookie;
54};
55
56typedef void* (*bb_get_cblist)(void);
57typedef void* (*bb_init_cb)(void *pdata);
58typedef void* (*bb_deinit_cb)(void);
59typedef int (*bb_power_cb)(int code);
60typedef int (*bb_attrib_cb)(struct device *dev, int value);
61typedef int (*modem_register_cb)(struct usb_device *udev);
62
63struct tegra_bb_power_gdata {
64 struct tegra_bb_gpio_data *gpio;
65 struct tegra_bb_gpio_irqdata *gpioirq;
66};
67
68struct tegra_bb_power_mdata {
69 /* Baseband USB vendor ID */
70 int vid;
71 /* Baseband USB product ID */
72 int pid;
73 /* Baseband capability - Can it generate a wakeup ? */
74 bool wake_capable;
75 /* Baseband capability - Can it be auto/runtime suspended ? */
76 bool autosuspend_ready;
77 /* Baseband callback after a successful registration */
78 modem_register_cb reg_cb;
79};
80
81struct tegra_bb_power_data {
82 struct tegra_bb_power_gdata *gpio_data;
83 struct tegra_bb_power_mdata *modem_data;
84};
85
86struct tegra_bb_callback {
87 bb_init_cb init;
88 bb_deinit_cb deinit;
89 bb_power_cb power;
90 bb_attrib_cb attrib;
91 bool valid;
92};
93
94#ifdef CONFIG_TEGRA_BB_M7400
95extern void *m7400_get_cblist(void);
96#define M7400_CB m7400_get_cblist
97#else
98#define M7400_CB NULL
99#endif
diff --git a/drivers/misc/tegra-cryptodev.c b/drivers/misc/tegra-cryptodev.c
new file mode 100644
index 00000000000..d5ed6a22dda
--- /dev/null
+++ b/drivers/misc/tegra-cryptodev.c
@@ -0,0 +1,349 @@
1/*
2 * drivers/misc/tegra-cryptodev.c
3 *
4 * crypto dev node for NVIDIA tegra aes hardware
5 *
6 * Copyright (c) 2010, NVIDIA Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/errno.h>
26#include <linux/kernel.h>
27#include <linux/slab.h>
28#include <linux/fs.h>
29#include <linux/miscdevice.h>
30#include <linux/crypto.h>
31#include <linux/scatterlist.h>
32#include <linux/uaccess.h>
33#include <crypto/rng.h>
34
35#include "tegra-cryptodev.h"
36
37#define NBUFS 2
38
39struct tegra_crypto_ctx {
40 struct crypto_ablkcipher *ecb_tfm;
41 struct crypto_ablkcipher *cbc_tfm;
42 struct crypto_rng *rng;
43 u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
44 int use_ssk;
45};
46
47struct tegra_crypto_completion {
48 struct completion restart;
49 int req_err;
50};
51
52static int alloc_bufs(unsigned long *buf[NBUFS])
53{
54 int i;
55
56 for (i = 0; i < NBUFS; i++) {
57 buf[i] = (void *)__get_free_page(GFP_KERNEL);
58 if (!buf[i])
59 goto err_free_buf;
60 }
61
62 return 0;
63
64err_free_buf:
65 while (i-- > 0)
66 free_page((unsigned long)buf[i]);
67
68 return -ENOMEM;
69}
70
71static void free_bufs(unsigned long *buf[NBUFS])
72{
73 int i;
74
75 for (i = 0; i < NBUFS; i++)
76 free_page((unsigned long)buf[i]);
77}
78
79static int tegra_crypto_dev_open(struct inode *inode, struct file *filp)
80{
81 struct tegra_crypto_ctx *ctx;
82 int ret = 0;
83
84 ctx = kzalloc(sizeof(struct tegra_crypto_ctx), GFP_KERNEL);
85 if (!ctx) {
86 pr_err("no memory for context\n");
87 return -ENOMEM;
88 }
89
90 ctx->ecb_tfm = crypto_alloc_ablkcipher("ecb-aes-tegra",
91 CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
92 if (IS_ERR(ctx->ecb_tfm)) {
93 pr_err("Failed to load transform for ecb-aes-tegra: %ld\n",
94 PTR_ERR(ctx->ecb_tfm));
95 ret = PTR_ERR(ctx->ecb_tfm);
96 goto fail_ecb;
97 }
98
99 ctx->cbc_tfm = crypto_alloc_ablkcipher("cbc-aes-tegra",
100 CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
101 if (IS_ERR(ctx->cbc_tfm)) {
102 pr_err("Failed to load transform for cbc-aes-tegra: %ld\n",
103 PTR_ERR(ctx->cbc_tfm));
104 ret = PTR_ERR(ctx->cbc_tfm);
105 goto fail_cbc;
106 }
107
108 ctx->rng = crypto_alloc_rng("rng-aes-tegra", CRYPTO_ALG_TYPE_RNG, 0);
109 if (IS_ERR(ctx->rng)) {
110 pr_err("Failed to load transform for tegra rng: %ld\n",
111 PTR_ERR(ctx->rng));
112 ret = PTR_ERR(ctx->rng);
113 goto fail_rng;
114 }
115
116 filp->private_data = ctx;
117 return ret;
118
119fail_rng:
120 crypto_free_ablkcipher(ctx->cbc_tfm);
121
122fail_cbc:
123 crypto_free_ablkcipher(ctx->ecb_tfm);
124
125fail_ecb:
126 kfree(ctx);
127 return ret;
128}
129
130static int tegra_crypto_dev_release(struct inode *inode, struct file *filp)
131{
132 struct tegra_crypto_ctx *ctx = filp->private_data;
133
134 crypto_free_ablkcipher(ctx->ecb_tfm);
135 crypto_free_ablkcipher(ctx->cbc_tfm);
136 crypto_free_rng(ctx->rng);
137 kfree(ctx);
138 filp->private_data = NULL;
139 return 0;
140}
141
142static void tegra_crypt_complete(struct crypto_async_request *req, int err)
143{
144 struct tegra_crypto_completion *done = req->data;
145
146 if (err != -EINPROGRESS) {
147 done->req_err = err;
148 complete(&done->restart);
149 }
150}
151
152static int process_crypt_req(struct tegra_crypto_ctx *ctx, struct tegra_crypt_req *crypt_req)
153{
154 struct crypto_ablkcipher *tfm;
155 struct ablkcipher_request *req = NULL;
156 struct scatterlist in_sg;
157 struct scatterlist out_sg;
158 unsigned long *xbuf[NBUFS];
159 int ret = 0, size = 0;
160 unsigned long total = 0;
161 struct tegra_crypto_completion tcrypt_complete;
162 const u8 *key = NULL;
163
164 if (crypt_req->op & TEGRA_CRYPTO_ECB) {
165 req = ablkcipher_request_alloc(ctx->ecb_tfm, GFP_KERNEL);
166 tfm = ctx->ecb_tfm;
167 } else {
168 req = ablkcipher_request_alloc(ctx->cbc_tfm, GFP_KERNEL);
169 tfm = ctx->cbc_tfm;
170 }
171 if (!req) {
172 pr_err("%s: Failed to allocate request\n", __func__);
173 return -ENOMEM;
174 }
175
176 if ((crypt_req->keylen < 0) || (crypt_req->keylen > AES_MAX_KEY_SIZE))
177 return -EINVAL;
178
179 crypto_ablkcipher_clear_flags(tfm, ~0);
180
181 if (!ctx->use_ssk)
182 key = crypt_req->key;
183
184 ret = crypto_ablkcipher_setkey(tfm, key, crypt_req->keylen);
185 if (ret < 0) {
186 pr_err("setkey failed");
187 goto process_req_out;
188 }
189
190 ret = alloc_bufs(xbuf);
191 if (ret < 0) {
192 pr_err("alloc_bufs failed");
193 goto process_req_out;
194 }
195
196 init_completion(&tcrypt_complete.restart);
197
198 ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
199 tegra_crypt_complete, &tcrypt_complete);
200
201 total = crypt_req->plaintext_sz;
202 while (total > 0) {
203 size = min(total, PAGE_SIZE);
204 ret = copy_from_user((void *)xbuf[0],
205 (void __user *)crypt_req->plaintext, size);
206 if (ret < 0) {
207 pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
208 goto process_req_buf_out;
209 }
210 sg_init_one(&in_sg, xbuf[0], size);
211 sg_init_one(&out_sg, xbuf[1], size);
212
213 ablkcipher_request_set_crypt(req, &in_sg,
214 &out_sg, size, crypt_req->iv);
215
216 INIT_COMPLETION(tcrypt_complete.restart);
217 tcrypt_complete.req_err = 0;
218 ret = crypt_req->encrypt ?
219 crypto_ablkcipher_encrypt(req) :
220 crypto_ablkcipher_decrypt(req);
221
222 if ((ret == -EINPROGRESS) || (ret == -EBUSY)) {
223 /* crypto driver is asynchronous */
224 ret = wait_for_completion_interruptible(&tcrypt_complete.restart);
225
226 if (ret < 0)
227 goto process_req_buf_out;
228
229 if (tcrypt_complete.req_err < 0) {
230 ret = tcrypt_complete.req_err;
231 goto process_req_buf_out;
232 }
233 } else if (ret < 0) {
234 pr_debug("%scrypt failed (%d)\n",
235 crypt_req->encrypt ? "en" : "de", ret);
236 goto process_req_buf_out;
237 }
238
239 ret = copy_to_user((void __user *)crypt_req->result,
240 (const void *)xbuf[1], size);
241 if (ret < 0)
242 goto process_req_buf_out;
243
244 total -= size;
245 crypt_req->result += size;
246 crypt_req->plaintext += size;
247 }
248
249process_req_buf_out:
250 free_bufs(xbuf);
251process_req_out:
252 ablkcipher_request_free(req);
253
254 return ret;
255}
256
257static long tegra_crypto_dev_ioctl(struct file *filp,
258 unsigned int ioctl_num, unsigned long arg)
259{
260 struct tegra_crypto_ctx *ctx = filp->private_data;
261 struct tegra_crypt_req crypt_req;
262 struct tegra_rng_req rng_req;
263 char *rng;
264 int ret = 0;
265
266 switch (ioctl_num) {
267 case TEGRA_CRYPTO_IOCTL_NEED_SSK:
268 ctx->use_ssk = (int)arg;
269 break;
270
271 case TEGRA_CRYPTO_IOCTL_PROCESS_REQ:
272 ret = copy_from_user(&crypt_req, (void __user *)arg, sizeof(crypt_req));
273 if (ret < 0) {
274 pr_err("%s: copy_from_user fail(%d)\n", __func__, ret);
275 break;
276 }
277
278 ret = process_crypt_req(ctx, &crypt_req);
279 break;
280
281 case TEGRA_CRYPTO_IOCTL_SET_SEED:
282 if (copy_from_user(&rng_req, (void __user *)arg, sizeof(rng_req)))
283 return -EFAULT;
284
285 memcpy(ctx->seed, rng_req.seed, TEGRA_CRYPTO_RNG_SEED_SIZE);
286
287 ret = crypto_rng_reset(ctx->rng, ctx->seed,
288 crypto_rng_seedsize(ctx->rng));
289 break;
290
291 case TEGRA_CRYPTO_IOCTL_GET_RANDOM:
292 if (copy_from_user(&rng_req, (void __user *)arg, sizeof(rng_req)))
293 return -EFAULT;
294
295 rng = kzalloc(rng_req.nbytes, GFP_KERNEL);
296 if (!rng) {
297 pr_err("mem alloc for rng fail");
298 ret = -ENODATA;
299 goto rng_out;
300 }
301
302 ret = crypto_rng_get_bytes(ctx->rng, rng,
303 rng_req.nbytes);
304
305 if (ret != rng_req.nbytes) {
306 pr_err("rng failed");
307 ret = -ENODATA;
308 goto rng_out;
309 }
310
311 ret = copy_to_user((void __user *)rng_req.rdata,
312 (const void *)rng, rng_req.nbytes);
313 ret = (ret < 0) ? -ENODATA : 0;
314rng_out:
315 if (rng)
316 kfree(rng);
317 break;
318
319 default:
320 pr_debug("invalid ioctl code(%d)", ioctl_num);
321 ret = -EINVAL;
322 }
323
324 return ret;
325}
326
327struct file_operations tegra_crypto_fops = {
328 .owner = THIS_MODULE,
329 .open = tegra_crypto_dev_open,
330 .release = tegra_crypto_dev_release,
331 .unlocked_ioctl = tegra_crypto_dev_ioctl,
332};
333
334struct miscdevice tegra_crypto_device = {
335 .minor = MISC_DYNAMIC_MINOR,
336 .name = "tegra-crypto",
337 .fops = &tegra_crypto_fops,
338};
339
340static int __init tegra_crypto_dev_init(void)
341{
342 return misc_register(&tegra_crypto_device);
343}
344
345late_initcall(tegra_crypto_dev_init);
346
347MODULE_DESCRIPTION("Tegra AES hw device node.");
348MODULE_AUTHOR("NVIDIA Corporation");
349MODULE_LICENSE("GPLv2");
diff --git a/drivers/misc/tegra-cryptodev.h b/drivers/misc/tegra-cryptodev.h
new file mode 100644
index 00000000000..ed62a52eca0
--- /dev/null
+++ b/drivers/misc/tegra-cryptodev.h
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2010, NVIDIA Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#ifndef __TEGRA_CRYPTODEV_H
20#define __TEGRA_CRYPTODEV_H
21
22#include <crypto/aes.h>
23
24#include <asm-generic/ioctl.h>
25
26/* ioctl arg = 1 if you want to use ssk. arg = 0 to use normal key */
27#define TEGRA_CRYPTO_IOCTL_NEED_SSK _IOWR(0x98, 100, int)
28#define TEGRA_CRYPTO_IOCTL_PROCESS_REQ _IOWR(0x98, 101, int*)
29#define TEGRA_CRYPTO_IOCTL_SET_SEED _IOWR(0x98, 102, int*)
30#define TEGRA_CRYPTO_IOCTL_GET_RANDOM _IOWR(0x98, 103, int*)
31
32#define TEGRA_CRYPTO_MAX_KEY_SIZE AES_MAX_KEY_SIZE
33#define TEGRA_CRYPTO_IV_SIZE AES_BLOCK_SIZE
34#define DEFAULT_RNG_BLK_SZ 16
35
36/* the seed consists of 16 bytes of key + 16 bytes of init vector */
37#define TEGRA_CRYPTO_RNG_SEED_SIZE AES_KEYSIZE_128 + DEFAULT_RNG_BLK_SZ
38#define TEGRA_CRYPTO_RNG_SIZE SZ_16
39
40/* encrypt/decrypt operations */
41#define TEGRA_CRYPTO_ECB BIT(0)
42#define TEGRA_CRYPTO_CBC BIT(1)
43#define TEGRA_CRYPTO_RNG BIT(2)
44
45/* a pointer to this struct needs to be passed to:
46 * TEGRA_CRYPTO_IOCTL_PROCESS_REQ
47 */
48struct tegra_crypt_req {
49 int op; /* e.g. TEGRA_CRYPTO_ECB */
50 bool encrypt;
51 char key[TEGRA_CRYPTO_MAX_KEY_SIZE];
52 int keylen;
53 char iv[TEGRA_CRYPTO_IV_SIZE];
54 int ivlen;
55 u8 *plaintext;
56 int plaintext_sz;
57 u8 *result;
58};
59
60/* pointer to this struct should be passed to:
61 * TEGRA_CRYPTO_IOCTL_SET_SEED
62 * TEGRA_CRYPTO_IOCTL_GET_RANDOM
63 */
64struct tegra_rng_req {
65 u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
66 u8 *rdata; /* random generated data */
67 int nbytes; /* random data length */
68};
69
70#endif
diff --git a/drivers/misc/ti-st/gps_drv.c b/drivers/misc/ti-st/gps_drv.c
new file mode 100644
index 00000000000..17ca92dd177
--- /dev/null
+++ b/drivers/misc/ti-st/gps_drv.c
@@ -0,0 +1,804 @@
1/*
2 * GPS Char Driver for Texas Instrument's Connectivity Chip.
3 * Copyright (C) 2009 Texas Instruments
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 version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19#include <linux/cdev.h>
20#include <linux/fs.h>
21#include <linux/device.h>
22
23#include <linux/uaccess.h>
24#include <linux/tty.h>
25#include <linux/sched.h>
26
27#include <linux/delay.h>
28#include <linux/firmware.h>
29#include <linux/platform_device.h>
30#include <linux/poll.h>
31#include <linux/skbuff.h>
32#include <linux/interrupt.h>
33
34#include <linux/ti_wilink_st.h>
35
36#undef VERBOSE
37#undef DEBUG
38
39/* Debug macros*/
40#if defined(DEBUG) /* limited debug messages */
41#define GPSDRV_DBG(fmt, arg...) \
42 printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
43#define GPSDRV_VER(fmt, arg...)
44#elif defined(VERBOSE) /* very verbose */
45#define GPSDRV_DBG(fmt, arg...) \
46 printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
47#define GPSDRV_VER(fmt, arg...) \
48 printk(KERN_INFO "[GPS] (gpsdrv):"fmt"\n" , ## arg)
49#define GPSDRV_ERR(fmt, arg...) \
50 printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
51#else /* Error msgs only */
52#define GPSDRV_ERR(fmt, arg...) \
53 printk(KERN_ERR "[GPS] (gpsdrv):"fmt"\n" , ## arg)
54#define GPSDRV_VER(fmt, arg...)
55#define GPSDRV_DBG(fmt, arg...)
56#endif
57
58static void gpsdrv_tsklet_write(unsigned long data);
59
60/* List of error codes returned by the gps driver*/
61enum {
62 GPS_ERR_FAILURE = -1, /* check struct */
63 GPS_SUCCESS,
64 GPS_ERR_CLASS = -15,
65 GPS_ERR_CPY_TO_USR,
66 GPS_ERR_CPY_FRM_USR,
67 GPS_ERR_UNKNOWN,
68};
69
70/* Channel-9 details for GPS */
71#define GPS_CH9_PKT_HDR_SIZE 4
72#define GPS_CH9_PKT_NUMBER 0x9
73#define GPS_CH9_OP_WRITE 0x1
74#define GPS_CH9_OP_READ 0x2
75#define GPS_CH9_OP_COMPLETED_EVT 0x3
76
77/* Macros for Syncronising GPS registration and other R/W/ICTL operations */
78#define GPS_ST_REGISTERED 0
79#define GPS_ST_RUNNING 1
80
81/* Read time out defined to 10 seconds */
82#define GPSDRV_READ_TIMEOUT 10000
83/* Reg time out defined to 6 seconds */
84#define GPSDRV_REG_TIMEOUT 6000
85
86
87struct gpsdrv_event_hdr {
88 uint8_t opcode;
89 uint16_t plen;
90} __packed;
91
92/*
93 * struct gpsdrv_data - gps internal driver data
94 * @gpsdrv_reg_completed - completion to wait for registration
95 * @streg_cbdata - registration feedback
96 * @state - driver state
97 * @tx_count - TX throttling/unthrottling
98 * @st_write - write ptr from ST
99 * @rx_list - Rx data SKB queue
100 * @tx_list - Tx data SKB queue
101 * @gpsdrv_data_q - dataq checked up on poll/receive
102 * @lock - spin lock
103 * @gpsdrv_tx_tsklet - gps write task
104 */
105
106struct gpsdrv_data {
107 struct completion gpsdrv_reg_completed;
108 char streg_cbdata;
109 unsigned long state;
110 unsigned char tx_count;
111 long (*st_write) (struct sk_buff *skb);
112 struct sk_buff_head rx_list;
113 struct sk_buff_head tx_list;
114 wait_queue_head_t gpsdrv_data_q;
115 spinlock_t lock;
116 struct tasklet_struct gpsdrv_tx_tsklet;
117};
118
119#define DEVICE_NAME "tigps"
120
121/***********Functions called from ST driver**********************************/
122
123/* gpsdrv_st_recv Function
124 * This is Called in from -- ST Core when a data is received
125 * This is a registered callback with ST core when the gps driver registers
126 * with ST.
127 *
128 * Parameters:
129 * @skb : SKB buffer pointer which contains the incoming Ch-9 GPS data.
130 * Returns:
131 * GPS_SUCCESS - On Success
132 * else suitable error code
133 */
134long gpsdrv_st_recv(void *arg, struct sk_buff *skb)
135{
136 struct gpsdrv_event_hdr gpsdrv_hdr = { 0x00, 0x0000 };
137 struct gpsdrv_data *hgps = (struct gpsdrv_data *)arg;
138
139 /* SKB is NULL */
140 if (NULL == skb) {
141 GPSDRV_ERR("Input SKB is NULL");
142 return GPS_ERR_FAILURE;
143 }
144
145 /* Sanity Check - To Check if the Rx Pkt is Channel -9 or not */
146 if (0x09 != skb->cb[0]) {
147 GPSDRV_ERR("Input SKB is not a Channel-9 packet");
148 return GPS_ERR_FAILURE;
149 }
150 /* Copy Ch-9 info to local structure */
151 memcpy(&gpsdrv_hdr, skb->data, GPS_CH9_PKT_HDR_SIZE - 1);
152 skb_pull(skb, GPS_CH9_PKT_HDR_SIZE - 1);
153
154 /* check if skb->len and gpsdrv_hdr.plen are equal */
155 if (skb->len != gpsdrv_hdr.plen) {
156 GPSDRV_ERR("Received corrupted packet - Length Mismatch");
157 return -EINVAL;
158 }
159#ifdef VERBOSE
160 printk(KERN_INFO"data start >>\n");
161 print_hex_dump(KERN_INFO, ">in>", DUMP_PREFIX_NONE,
162 16, 1, skb->data, skb->len, 0);
163 printk(KERN_INFO"\n<< end\n");
164#endif
165 /* Check the Opcode */
166 if ((gpsdrv_hdr.opcode != GPS_CH9_OP_READ) && (gpsdrv_hdr.opcode != \
167 GPS_CH9_OP_COMPLETED_EVT)) {
168 GPSDRV_ERR("Rec corrupt pkt opcode %x", gpsdrv_hdr.opcode);
169 return -EINVAL;
170 }
171
172 /* Strip Channel 9 packet information from SKB only
173 * if the opcode is GPS_CH9_OP_READ and get AI2 packet
174 */
175 if (GPS_CH9_OP_READ == gpsdrv_hdr.opcode) {
176 skb_queue_tail(&hgps->rx_list, skb);
177 wake_up_interruptible(&hgps->gpsdrv_data_q);
178 } else {
179 spin_lock(&hgps->lock);
180 /* The no. of Completed Packets is always 1
181 * in case of Channel 9 as per spec.
182 * Forcing it to 1 for precaution.
183 */
184 hgps->tx_count = 1;
185 /* Check if Tx queue and Tx count not empty */
186 if (!skb_queue_empty(&hgps->tx_list)) {
187 /* Schedule the Tx-task let */
188 spin_unlock(&hgps->lock);
189 GPSDRV_VER(" Scheduling tasklet to write");
190 tasklet_schedule(&hgps->gpsdrv_tx_tsklet);
191 } else {
192 spin_unlock(&hgps->lock);
193 }
194
195 GPSDRV_VER(" Tx count = %x", hgps->tx_count);
196 /* Free the received command complete SKB */
197 kfree_skb(skb);
198 }
199
200 return GPS_SUCCESS;
201
202}
203
204/* gpsdrv_st_cb Function
205 * This is Called in from -- ST Core when the state is pending during
206 * st_register. This is a registered callback with ST core when the gps
207 * driver registers with ST.
208 *
209 * Parameters:
210 * @data Status update of GPS registration
211 * Returns: NULL
212 */
213void gpsdrv_st_cb(void *arg, char data)
214{
215 struct gpsdrv_data *hgps = (struct gpsdrv_data *)arg;
216
217 GPSDRV_DBG(" Inside %s", __func__);
218 hgps->streg_cbdata = data; /* ST registration callback status */
219 complete_all(&hgps->gpsdrv_reg_completed);
220 return;
221}
222
223static struct st_proto_s gpsdrv_proto = {
224 .chnl_id = 0x09,
225 .max_frame_size = 1024,
226 .hdr_len = 3,
227 .offset_len_in_hdr = 1,
228 .len_size = 2,
229 .reserve = 1,
230 .recv = gpsdrv_st_recv,
231 .reg_complete_cb = gpsdrv_st_cb,
232};
233
234/** gpsdrv_tsklet_write Function
235 * This tasklet function will be scheduled when there is a data in Tx queue
236 * and GPS chip sent an command completion packet with non zero value.
237 *
238 * Parameters :
239 * @data : data passed to tasklet function
240 * Returns : NULL
241 */
242void gpsdrv_tsklet_write(unsigned long data)
243{
244 struct sk_buff *skb = NULL;
245 struct gpsdrv_data *hgps = (struct gpsdrv_data *)data;
246
247 GPSDRV_DBG(" Inside %s", __func__);
248
249 spin_lock(&hgps->lock);
250
251 /* Perform sanity check of verifying the status
252 to perform an st_write */
253 if (((!hgps->st_write) || (0 == hgps->tx_count))
254 || ((skb_queue_empty(&hgps->tx_list)))) {
255 spin_unlock(&hgps->lock);
256 GPSDRV_ERR("Sanity check Failed exiting %s", __func__);
257 return;
258 }
259 /* hgps->tx_list not empty skb already present
260 * dequeue the tx-data and perform a st_write
261 */
262 hgps->tx_count--;
263 spin_unlock(&hgps->lock);
264 GPSDRV_VER(" Tx count in gpsdrv_tsklet_write = %x", hgps->tx_count);
265 skb = skb_dequeue(&hgps->tx_list);
266 hgps->st_write(skb);
267
268 return;
269}
270
271/*********Functions Called from GPS host***************************************/
272
273/** gpsdrv_open Function
274 * This function will perform an register on ST driver.
275 *
276 * Parameters :
277 * @file : File pointer for GPS char driver
278 * @inod :
279 * Returns GPS_SUCCESS - on success
280 * else suitable error code
281 */
282int gpsdrv_open(struct inode *inod, struct file *file)
283{
284 int ret = 0;
285 unsigned long timeout = GPSDRV_REG_TIMEOUT;
286 struct gpsdrv_data *hgps;
287
288 GPSDRV_DBG(" Inside %s", __func__);
289
290 /* Allocate local resource memory */
291 hgps = kzalloc(sizeof(struct gpsdrv_data), GFP_KERNEL);
292 if (!(hgps)) {
293 GPSDRV_ERR("Can't allocate GPS data structure");
294 return -ENOMEM;
295 }
296
297 /* Initialize wait queue, skb queue head and
298 * registration complete strucuture
299 */
300 skb_queue_head_init(&hgps->rx_list);
301 skb_queue_head_init(&hgps->tx_list);
302 init_completion(&hgps->gpsdrv_reg_completed);
303 init_waitqueue_head(&hgps->gpsdrv_data_q);
304 spin_lock_init(&hgps->lock);
305
306 /* Check if GPS is already registered with ST */
307 if (test_and_set_bit(GPS_ST_REGISTERED, &hgps->state)) {
308 GPSDRV_ERR("GPS Registered/Registration in progress with ST"
309 " ,open called again?");
310 kfree(hgps);
311 return -EAGAIN;
312 }
313
314 /* Initialize gpsdrv_reg_completed so as to wait for completion
315 * on the same
316 * if st_register returns with a PENDING status
317 */
318 INIT_COMPLETION(hgps->gpsdrv_reg_completed);
319
320 gpsdrv_proto.priv_data = hgps;
321 /* Resgister GPS with ST */
322 ret = st_register(&gpsdrv_proto);
323 GPSDRV_VER(" st_register returned %d", ret);
324
325 /* If GPS Registration returned with error, then clear GPS_ST_REGISTERED
326 * for future open calls and return the appropriate error code
327 */
328 if (ret < 0 && ret != -EINPROGRESS) {
329 GPSDRV_ERR(" st_register failed");
330 clear_bit(GPS_ST_REGISTERED, &hgps->state);
331 if (ret == -EINPROGRESS)
332 return -EAGAIN;
333 return GPS_ERR_FAILURE;
334 }
335
336 /* if returned status is pending, wait for the completion */
337 if (ret == -EINPROGRESS) {
338 GPSDRV_VER(" GPS Register waiting for completion ");
339 timeout = wait_for_completion_timeout \
340 (&hgps->gpsdrv_reg_completed, msecs_to_jiffies(timeout));
341 /* Check for timed out condition */
342 if (0 == timeout) {
343 GPSDRV_ERR("GPS Device registration timed out");
344 clear_bit(GPS_ST_REGISTERED, &hgps->state);
345 return -ETIMEDOUT;
346 } else if (0 > hgps->streg_cbdata) {
347 GPSDRV_ERR("GPS Device Registration Failed-ST\n");
348 GPSDRV_ERR("RegCB called with ");
349 GPSDRV_ERR("Invalid value %d\n", hgps->streg_cbdata);
350 clear_bit(GPS_ST_REGISTERED, &hgps->state);
351 return -EAGAIN;
352 }
353 }
354 GPSDRV_DBG(" gps registration complete ");
355
356 /* Assign the write callback pointer */
357 hgps->st_write = gpsdrv_proto.write;
358 hgps->tx_count = 1;
359 file->private_data = hgps; /* set drv data */
360 tasklet_init(&hgps->gpsdrv_tx_tsklet, (void *)gpsdrv_tsklet_write,
361 (unsigned long)hgps);
362 set_bit(GPS_ST_RUNNING, &hgps->state);
363
364 return GPS_SUCCESS;
365}
366
367/** gpsdrv_release Function
368 * This function will un-registers from the ST driver.
369 *
370 * Parameters :
371 * @file : File pointer for GPS char driver
372 * @inod :
373 * Returns GPS_SUCCESS - on success
374 * else suitable error code
375 */
376int gpsdrv_release(struct inode *inod, struct file *file)
377{
378 struct gpsdrv_data *hgps = file->private_data;
379
380 GPSDRV_DBG(" Inside %s", __func__);
381
382 /* Disabling task-let 1st & then un-reg to avoid
383 * tasklet getting scheduled
384 */
385 tasklet_disable(&hgps->gpsdrv_tx_tsklet);
386 tasklet_kill(&hgps->gpsdrv_tx_tsklet);
387 /* Cleat registered bit if already registered */
388 if (test_and_clear_bit(GPS_ST_REGISTERED, &hgps->state)) {
389 if (st_unregister(&gpsdrv_proto) < 0) {
390 GPSDRV_ERR(" st_unregister failed");
391 /* Re-Enable the task-let if un-register fails */
392 tasklet_enable(&hgps->gpsdrv_tx_tsklet);
393 return GPS_ERR_FAILURE;
394 }
395 }
396
397 /* Reset Tx count value and st_write function pointer */
398 hgps->tx_count = 0;
399 hgps->st_write = NULL;
400 clear_bit(GPS_ST_RUNNING, &hgps->state);
401 GPSDRV_VER(" st_unregister success");
402
403 skb_queue_purge(&hgps->rx_list);
404 skb_queue_purge(&hgps->tx_list);
405 kfree(hgps);
406 file->private_data = NULL;
407
408 return GPS_SUCCESS;
409}
410
411/** gpsdrv_read Function
412 * This function will wait till the data received from the ST driver
413 * and then strips the GPS-Channel-9 header from the
414 * incoming AI2 packet and then send it to GPS host application.
415 *
416 * Parameters :
417 * @file : File pointer for GPS char driver
418 * @data : Data which needs to be passed to APP
419 * @size : Length of the data passesd
420 * offset :
421 * Returns Size of AI2 packet received - on success
422 * else suitable error code
423 */
424ssize_t gpsdrv_read(struct file *file, char __user *data, size_t size,
425 loff_t *offset)
426{
427 int len = 0, error = 0;
428 struct sk_buff *skb = NULL;
429 unsigned long timeout = GPSDRV_READ_TIMEOUT;
430 struct gpsdrv_data *hgps;
431
432 GPSDRV_DBG(" Inside %s", __func__);
433
434 /* Validate input parameters */
435 if ((NULL == file) || (((NULL == data) || (0 == size)))) {
436 GPSDRV_ERR("Invalid input params passed to %s", __func__);
437 return -EINVAL;
438 }
439
440 hgps = file->private_data;
441 /* Check if GPS is registered to perform read operation */
442 if (!test_bit(GPS_ST_RUNNING, &hgps->state)) {
443 GPSDRV_ERR("GPS Device is not running");
444 return -EINVAL;
445 }
446
447 /* cannot come here if poll-ed before reading
448 * if not poll-ed wait on the same wait_q
449 */
450 timeout = wait_event_interruptible_timeout(hgps->gpsdrv_data_q,
451 !skb_queue_empty(&hgps->rx_list), msecs_to_jiffies(timeout));
452 /* Check for timed out condition */
453 if (0 == timeout) {
454 GPSDRV_ERR("GPS Device Read timed out");
455 return -ETIMEDOUT;
456 }
457
458
459 /* hgps->rx_list not empty skb already present */
460 skb = skb_dequeue(&hgps->rx_list);
461
462 if (!skb) {
463 GPSDRV_ERR("Dequed SKB is NULL?");
464 return GPS_ERR_UNKNOWN;
465 } else if (skb->len > size) {
466 GPSDRV_DBG("SKB length is Greater than requested size ");
467 GPSDRV_DBG("Returning the available length of SKB\n");
468
469 error = copy_to_user(data, skb->data, size);
470 skb_pull(skb, size);
471
472 if (skb->len != 0)
473 skb_queue_head(&hgps->rx_list, skb);
474
475 /* printk(KERN_DEBUG "gpsdrv: total size read= %d", size);*/
476 return size;
477 }
478
479#ifdef VERBOSE
480 print_hex_dump(KERN_INFO, ">in>", DUMP_PREFIX_NONE,
481 16, 1, skb->data, skb->len, 0);
482#endif
483
484 /* Forward the data to the user */
485 if (skb->len <= size) {
486 if (copy_to_user(data, skb->data, skb->len)) {
487 GPSDRV_ERR(" Unable to copy to user space");
488 /* Queue the skb back to head */
489 skb_queue_head(&hgps->rx_list, skb);
490 return GPS_ERR_CPY_TO_USR;
491 }
492 }
493
494 len = skb->len;
495 kfree_skb(skb);
496 /* printk(KERN_DEBUG "gpsdrv: total size read= %d", len); */
497 return len;
498}
499/* gpsdrv_write Function
500 * This function will pre-pend the GPS-Channel-9 header to the
501 * incoming AI2 packet sent from the GPS host application.
502 *
503 * Parameters :
504 * @file : File pointer for GPS char driver
505 * @data : AI2 packet data from GPS application
506 * @size : Size of the AI2 packet data
507 * @offset :
508 * Returns Size of AI2 packet on success
509 * else suitable error code
510 */
511ssize_t gpsdrv_write(struct file *file, const char __user *data,
512 size_t size, loff_t *offset)
513{
514 unsigned char channel = GPS_CH9_PKT_NUMBER; /* GPS Channel number */
515 /* Initialize gpsdrv_event_hdr with write opcode */
516 struct gpsdrv_event_hdr gpsdrv_hdr = { GPS_CH9_OP_WRITE, 0x0000 };
517 struct sk_buff *skb = NULL;
518 struct gpsdrv_data *hgps;
519
520 GPSDRV_DBG(" Inside %s", __func__);
521 /* Validate input parameters */
522 if ((NULL == file) || (((NULL == data) || (0 == size)))) {
523 GPSDRV_ERR("Invalid input params passed to %s", __func__);
524 return -EINVAL;
525 }
526
527 hgps = file->private_data;
528
529 /* Check if GPS is registered to perform write operation */
530 if (!test_bit(GPS_ST_RUNNING, &hgps->state)) {
531 GPSDRV_ERR("GPS Device is not running");
532 return -EINVAL;
533 }
534
535 if (!hgps->st_write) {
536 GPSDRV_ERR(" Can't write to ST, hgps->st_write null ?");
537 return -EINVAL;
538 }
539
540
541 skb = alloc_skb(size + GPS_CH9_PKT_HDR_SIZE, GFP_ATOMIC);
542 /* Validate Created SKB */
543 if (NULL == skb) {
544 GPSDRV_ERR("Error aaloacting SKB");
545 return -ENOMEM;
546 }
547
548 /* Update chnl-9 information with plen=AI2 pckt size which is "size"*/
549 gpsdrv_hdr.plen = size;
550
551 /* PrePend Channel-9 header to AI2 packet and write to SKB */
552 memcpy(skb_put(skb, 1), &channel, 1);
553 memcpy(skb_put(skb, GPS_CH9_PKT_HDR_SIZE - 1), &gpsdrv_hdr,
554 GPS_CH9_PKT_HDR_SIZE - 1);
555
556 /* Forward the data from the user space to ST core */
557 if (copy_from_user(skb_put(skb, size), data, size)) {
558 GPSDRV_ERR(" Unable to copy from user space");
559 kfree_skb(skb);
560 return GPS_ERR_CPY_FRM_USR;
561 }
562
563#ifdef VERBOSE
564 GPSDRV_VER("start data..");
565 print_hex_dump(KERN_INFO, "<out<", DUMP_PREFIX_NONE,
566 16, 1, skb->data, size, 0);
567 GPSDRV_VER("\n..end data");
568#endif
569
570 /* Check if data can be sent to GPS chip
571 * If not, add it to queue and that can be sent later
572 */
573 spin_lock(&hgps->lock);
574 if (0 != hgps->tx_count) {
575 /* If TX Q is empty send current SKB;
576 * else, queue current SKB at end of tx_list queue and
577 * send first SKB in tx_list queue.
578 */
579 hgps->tx_count--;
580 spin_unlock(&hgps->lock);
581
582 GPSDRV_VER(" Tx count in gpsdrv_write = %x", hgps->tx_count);
583
584 if (skb_queue_empty(&hgps->tx_list)) {
585 hgps->st_write(skb);
586 } else {
587 skb_queue_tail(&hgps->tx_list, skb);
588 hgps->st_write(skb_dequeue(&hgps->tx_list));
589 }
590 } else {
591 /* Add it to TX queue */
592 spin_unlock(&hgps->lock);
593 GPSDRV_VER(" SKB added to Tx queue ");
594 GPSDRV_VER("Tx count = %x ", hgps->tx_count);
595 skb_queue_tail(&hgps->tx_list, skb);
596 /* This is added for precaution in the case that there is a
597 * context switch during the execution of the above lines.
598 * Redundant otherwise.
599 */
600 if ((0 != hgps->tx_count) && \
601 (!skb_queue_empty(&hgps->tx_list))) {
602 /* Schedule the Tx-task let */
603 GPSDRV_VER("Scheduling tasklet to write\n");
604 tasklet_schedule(&hgps->gpsdrv_tx_tsklet);
605 }
606 }
607
608 return size;
609}
610
611/** gpsdrv_ioctl Function
612 * This will peform the functions as directed by the command and command
613 * argument.
614 *
615 * Parameters :
616 * @file : File pointer for GPS char driver
617 * @cmd : IOCTL Command
618 * @arg : Command argument for IOCTL command
619 * Returns GPS_SUCCESS on success
620 * else suitable error code
621 */
622static long gpsdrv_ioctl(struct file *file,
623 unsigned int cmd, unsigned long arg)
624{
625 struct sk_buff *skb = NULL;
626 int retCode = GPS_SUCCESS;
627 struct gpsdrv_data *hgps;
628
629 GPSDRV_DBG(" Inside %s", __func__);
630
631 /* Validate input parameters */
632 if ((NULL == file) || (0 == cmd)) {
633 GPSDRV_ERR("Invalid input parameters passed to %s", __func__);
634 return -EINVAL;
635 }
636
637 hgps = file->private_data;
638
639 /* Check if GPS is registered to perform IOCTL operation */
640 if (!test_bit(GPS_ST_RUNNING, &hgps->state)) {
641 GPSDRV_ERR("GPS Device is not running");
642 return -EINVAL;
643 }
644
645 switch (cmd) {
646 case TCFLSH:
647 GPSDRV_VER(" IOCTL TCFLSH invoked with %ld argument", arg);
648 switch (arg) {
649 /* purge Rx/Tx SKB list queues depending on arg value */
650 case TCIFLUSH:
651 skb_queue_purge(&hgps->rx_list);
652 break;
653 case TCOFLUSH:
654 skb_queue_purge(&hgps->tx_list);
655 break;
656 case TCIOFLUSH:
657 skb_queue_purge(&hgps->rx_list);
658 skb_queue_purge(&hgps->tx_list);
659 break;
660 default:
661 GPSDRV_ERR("Invalid Command passed for tcflush");
662 retCode = 0;
663 break;
664 }
665 break;
666 case FIONREAD:
667 /* Deque the SKB from the head if rx_list is not empty
668 * And update the argument with skb->len to provide
669 * the amount of data available in the available SKB
670 */
671 skb = skb_dequeue(&hgps->rx_list);
672 if (skb != NULL) {
673 *(unsigned int *)arg = skb->len;
674 /* Re-Store the SKB for furtur Read operations */
675 skb_queue_head(&hgps->rx_list, skb);
676 } else {
677 *(unsigned int *)arg = 0;
678 }
679 GPSDRV_DBG("returning %d\n", *(unsigned int *)arg);
680
681 break;
682 default:
683 GPSDRV_DBG("Un-Identified IOCTL %d", cmd);
684 retCode = 0;
685 break;
686 }
687
688 return retCode;
689}
690
691/** gpsdrv_poll Function
692 * This function will wait till some data is received to the gps driver from ST
693 *
694 * Parameters :
695 * @file : File pointer for GPS char driver
696 * @wait : POLL wait information
697 * Returns status of POLL on success
698 * else suitable error code
699 */
700static unsigned int gpsdrv_poll(struct file *file, poll_table *wait)
701{
702 unsigned long mask = 0;
703 struct gpsdrv_data *hgps = file->private_data;
704
705 /* Check if GPS is registered to perform read operation */
706 if (!test_bit(GPS_ST_RUNNING, &hgps->state)) {
707 GPSDRV_ERR("GPS Device is not running");
708 return -EINVAL;
709 }
710
711 /* Wait till data is signalled from gpsdrv_st_recv function
712 * with AI2 packet
713 */
714 poll_wait(file, &hgps->gpsdrv_data_q, wait);
715
716 if (!skb_queue_empty(&hgps->rx_list))
717 mask |= POLLIN; /* TODO: check app for mask */
718
719 return mask;
720}
721
722/* GPS Char driver function pointers
723 * These functions are called from USER space by pefroming File Operations
724 * on /dev/gps node exposed by this driver during init
725 */
726const struct file_operations gpsdrv_chrdev_ops = {
727 .owner = THIS_MODULE,
728 .open = gpsdrv_open,
729 .read = gpsdrv_read,
730 .write = gpsdrv_write,
731 .unlocked_ioctl = gpsdrv_ioctl,
732 .poll = gpsdrv_poll,
733 .release = gpsdrv_release,
734};
735
736/*********Functions called during insmod and delmod****************************/
737
738static int gpsdrv_major; /* GPS major number */
739static struct class *gpsdrv_class; /* GPS class during class_create */
740static struct device *gpsdrv_dev; /* GPS dev during device_create */
741/** gpsdrv_init Function
742 * This function Initializes the gps driver parametes and exposes
743 * /dev/gps node to user space
744 *
745 * Parameters : NULL
746 * Returns GPS_SUCCESS on success
747 * else suitable error code
748 */
749static int __init gpsdrv_init(void)
750{
751
752 GPSDRV_DBG(" Inside %s", __func__);
753
754 /* Expose the device DEVICE_NAME to user space
755 * And obtain the major number for the device
756 */
757 gpsdrv_major = register_chrdev(0, DEVICE_NAME, \
758 &gpsdrv_chrdev_ops);
759 if (0 > gpsdrv_major) {
760 GPSDRV_ERR("Error when registering to char dev");
761 return GPS_ERR_FAILURE;
762 }
763 GPSDRV_VER("allocated %d, %d", gpsdrv_major, 0);
764
765 /* udev */
766 gpsdrv_class = class_create(THIS_MODULE, DEVICE_NAME);
767 if (IS_ERR(gpsdrv_class)) {
768 GPSDRV_ERR(" Something went wrong in class_create");
769 unregister_chrdev(gpsdrv_major, DEVICE_NAME);
770 return GPS_ERR_CLASS;
771 }
772
773 gpsdrv_dev =
774 device_create(gpsdrv_class, NULL, MKDEV(gpsdrv_major, 0),
775 NULL, DEVICE_NAME);
776 if (IS_ERR(gpsdrv_dev)) {
777 GPSDRV_ERR(" Error in class_create");
778 unregister_chrdev(gpsdrv_major, DEVICE_NAME);
779 class_destroy(gpsdrv_class);
780 return GPS_ERR_CLASS;
781 }
782
783 return GPS_SUCCESS;
784}
785
786/** gpsdrv_exit Function
787 * This function Destroys the gps driver parametes and /dev/gps node
788 *
789 * Parameters : NULL
790 * Returns NULL
791 */
792static void __exit gpsdrv_exit(void)
793{
794 GPSDRV_DBG(" Inside %s", __func__);
795 GPSDRV_VER(" Bye.. freeing up %d", gpsdrv_major);
796
797 device_destroy(gpsdrv_class, MKDEV(gpsdrv_major, 0));
798 class_destroy(gpsdrv_class);
799 unregister_chrdev(gpsdrv_major, DEVICE_NAME);
800}
801
802module_init(gpsdrv_init);
803module_exit(gpsdrv_exit);
804MODULE_LICENSE("GPL");
diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c
new file mode 100644
index 00000000000..2141124a6c1
--- /dev/null
+++ b/drivers/misc/uid_stat.c
@@ -0,0 +1,156 @@
1/* drivers/misc/uid_stat.c
2 *
3 * Copyright (C) 2008 - 2009 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 */
15
16#include <asm/atomic.h>
17
18#include <linux/err.h>
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/list.h>
22#include <linux/proc_fs.h>
23#include <linux/slab.h>
24#include <linux/spinlock.h>
25#include <linux/stat.h>
26#include <linux/uid_stat.h>
27#include <net/activity_stats.h>
28
29static DEFINE_SPINLOCK(uid_lock);
30static LIST_HEAD(uid_list);
31static struct proc_dir_entry *parent;
32
33struct uid_stat {
34 struct list_head link;
35 uid_t uid;
36 atomic_t tcp_rcv;
37 atomic_t tcp_snd;
38};
39
40static struct uid_stat *find_uid_stat(uid_t uid) {
41 unsigned long flags;
42 struct uid_stat *entry;
43
44 spin_lock_irqsave(&uid_lock, flags);
45 list_for_each_entry(entry, &uid_list, link) {
46 if (entry->uid == uid) {
47 spin_unlock_irqrestore(&uid_lock, flags);
48 return entry;
49 }
50 }
51 spin_unlock_irqrestore(&uid_lock, flags);
52 return NULL;
53}
54
55static int tcp_snd_read_proc(char *page, char **start, off_t off,
56 int count, int *eof, void *data)
57{
58 int len;
59 unsigned int bytes;
60 char *p = page;
61 struct uid_stat *uid_entry = (struct uid_stat *) data;
62 if (!data)
63 return 0;
64
65 bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN);
66 p += sprintf(p, "%u\n", bytes);
67 len = (p - page) - off;
68 *eof = (len <= count) ? 1 : 0;
69 *start = page + off;
70 return len;
71}
72
73static int tcp_rcv_read_proc(char *page, char **start, off_t off,
74 int count, int *eof, void *data)
75{
76 int len;
77 unsigned int bytes;
78 char *p = page;
79 struct uid_stat *uid_entry = (struct uid_stat *) data;
80 if (!data)
81 return 0;
82
83 bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN);
84 p += sprintf(p, "%u\n", bytes);
85 len = (p - page) - off;
86 *eof = (len <= count) ? 1 : 0;
87 *start = page + off;
88 return len;
89}
90
91/* Create a new entry for tracking the specified uid. */
92static struct uid_stat *create_stat(uid_t uid) {
93 unsigned long flags;
94 char uid_s[32];
95 struct uid_stat *new_uid;
96 struct proc_dir_entry *entry;
97
98 /* Create the uid stat struct and append it to the list. */
99 if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL)
100 return NULL;
101
102 new_uid->uid = uid;
103 /* Counters start at INT_MIN, so we can track 4GB of network traffic. */
104 atomic_set(&new_uid->tcp_rcv, INT_MIN);
105 atomic_set(&new_uid->tcp_snd, INT_MIN);
106
107 spin_lock_irqsave(&uid_lock, flags);
108 list_add_tail(&new_uid->link, &uid_list);
109 spin_unlock_irqrestore(&uid_lock, flags);
110
111 sprintf(uid_s, "%d", uid);
112 entry = proc_mkdir(uid_s, parent);
113
114 /* Keep reference to uid_stat so we know what uid to read stats from. */
115 create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc,
116 (void *) new_uid);
117
118 create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc,
119 (void *) new_uid);
120
121 return new_uid;
122}
123
124int uid_stat_tcp_snd(uid_t uid, int size) {
125 struct uid_stat *entry;
126 activity_stats_update();
127 if ((entry = find_uid_stat(uid)) == NULL &&
128 ((entry = create_stat(uid)) == NULL)) {
129 return -1;
130 }
131 atomic_add(size, &entry->tcp_snd);
132 return 0;
133}
134
135int uid_stat_tcp_rcv(uid_t uid, int size) {
136 struct uid_stat *entry;
137 activity_stats_update();
138 if ((entry = find_uid_stat(uid)) == NULL &&
139 ((entry = create_stat(uid)) == NULL)) {
140 return -1;
141 }
142 atomic_add(size, &entry->tcp_rcv);
143 return 0;
144}
145
146static int __init uid_stat_init(void)
147{
148 parent = proc_mkdir("uid_stat", NULL);
149 if (!parent) {
150 pr_err("uid_stat: failed to create proc entry\n");
151 return -1;
152 }
153 return 0;
154}
155
156__initcall(uid_stat_init);
diff --git a/drivers/misc/wl127x-rfkill.c b/drivers/misc/wl127x-rfkill.c
new file mode 100644
index 00000000000..f5b95152948
--- /dev/null
+++ b/drivers/misc/wl127x-rfkill.c
@@ -0,0 +1,121 @@
1/*
2 * Bluetooth TI wl127x rfkill power control via GPIO
3 *
4 * Copyright (C) 2009 Motorola, Inc.
5 * Copyright (C) 2008 Texas Instruments
6 * Initial code: Pavan Savoy <pavan.savoy@gmail.com> (wl127x_power.c)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/gpio.h>
27#include <linux/rfkill.h>
28#include <linux/platform_device.h>
29#include <linux/wl127x-rfkill.h>
30
31static int wl127x_rfkill_set_power(void *data, enum rfkill_state state)
32{
33 int nshutdown_gpio = (int) data;
34
35 switch (state) {
36 case RFKILL_STATE_UNBLOCKED:
37 gpio_set_value(nshutdown_gpio, 1);
38 break;
39 case RFKILL_STATE_SOFT_BLOCKED:
40 gpio_set_value(nshutdown_gpio, 0);
41 break;
42 default:
43 printk(KERN_ERR "invalid bluetooth rfkill state %d\n", state);
44 }
45 return 0;
46}
47
48static int wl127x_rfkill_probe(struct platform_device *pdev)
49{
50 int rc = 0;
51 struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
52 enum rfkill_state default_state = RFKILL_STATE_SOFT_BLOCKED; /* off */
53
54 rc = gpio_request(pdata->nshutdown_gpio, "wl127x_nshutdown_gpio");
55 if (unlikely(rc))
56 return rc;
57
58 rc = gpio_direction_output(pdata->nshutdown_gpio, 0);
59 if (unlikely(rc))
60 return rc;
61
62 rfkill_set_default(RFKILL_TYPE_BLUETOOTH, default_state);
63 wl127x_rfkill_set_power(NULL, default_state);
64
65 pdata->rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH);
66 if (unlikely(!pdata->rfkill))
67 return -ENOMEM;
68
69 pdata->rfkill->name = "wl127x";
70 pdata->rfkill->state = default_state;
71 /* userspace cannot take exclusive control */
72 pdata->rfkill->user_claim_unsupported = 1;
73 pdata->rfkill->user_claim = 0;
74 pdata->rfkill->data = (void *) pdata->nshutdown_gpio;
75 pdata->rfkill->toggle_radio = wl127x_rfkill_set_power;
76
77 rc = rfkill_register(pdata->rfkill);
78
79 if (unlikely(rc))
80 rfkill_free(pdata->rfkill);
81
82 return 0;
83}
84
85static int wl127x_rfkill_remove(struct platform_device *pdev)
86{
87 struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
88
89 rfkill_unregister(pdata->rfkill);
90 rfkill_free(pdata->rfkill);
91 gpio_free(pdata->nshutdown_gpio);
92
93 return 0;
94}
95
96static struct platform_driver wl127x_rfkill_platform_driver = {
97 .probe = wl127x_rfkill_probe,
98 .remove = wl127x_rfkill_remove,
99 .driver = {
100 .name = "wl127x-rfkill",
101 .owner = THIS_MODULE,
102 },
103};
104
105static int __init wl127x_rfkill_init(void)
106{
107 return platform_driver_register(&wl127x_rfkill_platform_driver);
108}
109
110static void __exit wl127x_rfkill_exit(void)
111{
112 platform_driver_unregister(&wl127x_rfkill_platform_driver);
113}
114
115module_init(wl127x_rfkill_init);
116module_exit(wl127x_rfkill_exit);
117
118MODULE_ALIAS("platform:wl127x");
119MODULE_DESCRIPTION("wl127x-rfkill");
120MODULE_AUTHOR("Motorola");
121MODULE_LICENSE("GPL");