aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/compass/hmc5883.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/hmc5883.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/inv_mpu/compass/hmc5883.c')
-rw-r--r--drivers/misc/inv_mpu/compass/hmc5883.c391
1 files changed, 391 insertions, 0 deletions
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 */