aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/compass
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/inv_mpu/compass
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu/compass')
-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
16 files changed, 6153 insertions, 0 deletions
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 */