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