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