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