diff options
author | Luwei Zhou <b45643@freescale.com> | 2014-03-31 02:05:23 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 09:58:12 -0400 |
commit | a6280e9bb35440f51eb090898dcede82c4e2994e (patch) | |
tree | 3f709ca30eb45b8a18543b330471b92a0b711231 /drivers/hwmon | |
parent | 1ddd70da4ce5558d0b0a76cadae263a648628d7a (diff) |
ENGR00306134-1: hwmon: mag3110 Enable mag3110 driver for i.MX6SX-SDB platform
The mag3110 driver can support irq mode and polling mode. On i.MX6SX-SDB platorm,
mag3110 sensor and other sensor share the same GPIO interrutp line. The main modification:
* Modify the driver to support irq working mode.
* Add shared interrupt support in the driver.
* Fix the clear interrupt flag issue.
Signed-off-by: Luwei Zhou <b45643@freescale.com>
Diffstat (limited to 'drivers/hwmon')
-rwxr-xr-x | drivers/hwmon/mag3110.c | 59 |
1 files changed, 44 insertions, 15 deletions
diff --git a/drivers/hwmon/mag3110.c b/drivers/hwmon/mag3110.c index 9d7f617bcaef..a8d80fa46dfe 100755 --- a/drivers/hwmon/mag3110.c +++ b/drivers/hwmon/mag3110.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * | 2 | * |
3 | * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. | 3 | * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 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 | 6 | * it under the terms of the GNU General Public License as published by |
@@ -37,12 +37,11 @@ | |||
37 | #define MAG3110_ID 0xC4 | 37 | #define MAG3110_ID 0xC4 |
38 | #define MAG3110_XYZ_DATA_LEN 6 | 38 | #define MAG3110_XYZ_DATA_LEN 6 |
39 | #define MAG3110_STATUS_ZYXDR 0x08 | 39 | #define MAG3110_STATUS_ZYXDR 0x08 |
40 | 40 | #define MAG3110_IRQ_USED 1 | |
41 | #define MAG3110_AC_MASK (0x01) | 41 | #define MAG3110_AC_MASK (0x01) |
42 | #define MAG3110_AC_OFFSET 0 | 42 | #define MAG3110_AC_OFFSET 0 |
43 | #define MAG3110_DR_MODE_MASK (0x7 << 5) | 43 | #define MAG3110_DR_MODE_MASK (0x7 << 5) |
44 | #define MAG3110_DR_MODE_OFFSET 5 | 44 | #define MAG3110_DR_MODE_OFFSET 5 |
45 | #define MAG3110_IRQ_USED 0 | ||
46 | 45 | ||
47 | #define POLL_INTERVAL_MAX 500 | 46 | #define POLL_INTERVAL_MAX 500 |
48 | #define POLL_INTERVAL 100 | 47 | #define POLL_INTERVAL 100 |
@@ -182,9 +181,11 @@ static int mag3110_init_client(struct i2c_client *client) | |||
182 | static int mag3110_read_data(short *x, short *y, short *z) | 181 | static int mag3110_read_data(short *x, short *y, short *z) |
183 | { | 182 | { |
184 | struct mag3110_data *data; | 183 | struct mag3110_data *data; |
185 | int retry = 3; | ||
186 | u8 tmp_data[MAG3110_XYZ_DATA_LEN]; | 184 | u8 tmp_data[MAG3110_XYZ_DATA_LEN]; |
185 | #if !MAG3110_IRQ_USED | ||
186 | int retry = 3; | ||
187 | int result; | 187 | int result; |
188 | #endif | ||
188 | if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY) | 189 | if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY) |
189 | return -EINVAL; | 190 | return -EINVAL; |
190 | 191 | ||
@@ -210,10 +211,12 @@ static int mag3110_read_data(short *x, short *y, short *z) | |||
210 | 211 | ||
211 | data->data_ready = 0; | 212 | data->data_ready = 0; |
212 | 213 | ||
213 | if (mag3110_read_block_data(data->client, | 214 | while (i2c_smbus_read_byte_data(data->client, MAG3110_DR_STATUS)) { |
215 | if (mag3110_read_block_data(data->client, | ||
214 | MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN, | 216 | MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN, |
215 | tmp_data) < 0) | 217 | tmp_data) < 0) |
216 | return -1; | 218 | return -1; |
219 | } | ||
217 | 220 | ||
218 | *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; | 221 | *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; |
219 | *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; | 222 | *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; |
@@ -248,8 +251,26 @@ static void mag3110_dev_poll(struct input_polled_dev *dev) | |||
248 | #if MAG3110_IRQ_USED | 251 | #if MAG3110_IRQ_USED |
249 | static irqreturn_t mag3110_irq_handler(int irq, void *dev_id) | 252 | static irqreturn_t mag3110_irq_handler(int irq, void *dev_id) |
250 | { | 253 | { |
254 | int result; | ||
255 | u8 tmp_data[MAG3110_XYZ_DATA_LEN]; | ||
256 | result = i2c_smbus_read_byte_data(mag3110_pdata->client, | ||
257 | MAG3110_DR_STATUS); | ||
258 | if (!(result & MAG3110_STATUS_ZYXDR)) | ||
259 | return IRQ_NONE; | ||
260 | |||
251 | mag3110_pdata->data_ready = 1; | 261 | mag3110_pdata->data_ready = 1; |
252 | wake_up_interruptible(&mag3110_pdata->waitq); | 262 | |
263 | if (mag3110_pdata->active == MAG_STANDBY) | ||
264 | /* | ||
265 | * Since the mode will be changed, sometimes irq will | ||
266 | * be handled in StandBy mode because of interrupt latency. | ||
267 | * So just clear the interrutp flag via reading block data. | ||
268 | */ | ||
269 | mag3110_read_block_data(mag3110_pdata->client, | ||
270 | MAG3110_OUT_X_MSB, | ||
271 | MAG3110_XYZ_DATA_LEN, tmp_data); | ||
272 | else | ||
273 | wake_up_interruptible(&mag3110_pdata->waitq); | ||
253 | 274 | ||
254 | return IRQ_HANDLED; | 275 | return IRQ_HANDLED; |
255 | } | 276 | } |
@@ -284,6 +305,7 @@ static ssize_t mag3110_enable_store(struct device *dev, | |||
284 | 305 | ||
285 | mutex_lock(&mag3110_lock); | 306 | mutex_lock(&mag3110_lock); |
286 | client = mag3110_pdata->client; | 307 | client = mag3110_pdata->client; |
308 | |||
287 | reg = mag3110_read_reg(client, MAG3110_CTRL_REG1); | 309 | reg = mag3110_read_reg(client, MAG3110_CTRL_REG1); |
288 | if (enable && mag3110_pdata->active == MAG_STANDBY) { | 310 | if (enable && mag3110_pdata->active == MAG_STANDBY) { |
289 | reg |= MAG3110_AC_MASK; | 311 | reg |= MAG3110_AC_MASK; |
@@ -297,12 +319,10 @@ static ssize_t mag3110_enable_store(struct device *dev, | |||
297 | mag3110_pdata->active = MAG_STANDBY; | 319 | mag3110_pdata->active = MAG_STANDBY; |
298 | } | 320 | } |
299 | 321 | ||
300 | if (mag3110_pdata->active == MAG_ACTIVED) { | 322 | /* Read out MSB data to clear interrupt flag */ |
301 | msleep(100); | 323 | msleep(100); |
302 | /* Read out MSB data to clear interrupt flag automatically */ | 324 | mag3110_read_block_data(mag3110_pdata->client, MAG3110_OUT_X_MSB, |
303 | mag3110_read_block_data(client, MAG3110_OUT_X_MSB, | ||
304 | MAG3110_XYZ_DATA_LEN, tmp_data); | 325 | MAG3110_XYZ_DATA_LEN, tmp_data); |
305 | } | ||
306 | mutex_unlock(&mag3110_lock); | 326 | mutex_unlock(&mag3110_lock); |
307 | return count; | 327 | return count; |
308 | } | 328 | } |
@@ -402,6 +422,11 @@ static int mag3110_probe(struct i2c_client *client, | |||
402 | struct regulator *vdd, *vdd_io; | 422 | struct regulator *vdd, *vdd_io; |
403 | u32 pos = 0; | 423 | u32 pos = 0; |
404 | struct device_node *of_node = client->dev.of_node; | 424 | struct device_node *of_node = client->dev.of_node; |
425 | #if MAG3110_IRQ_USED | ||
426 | struct irq_data *irq_data = irq_get_irq_data(client->irq); | ||
427 | u32 irq_flag; | ||
428 | bool shared_irq = of_property_read_bool(of_node, "shared-interrupt"); | ||
429 | #endif | ||
405 | vdd = NULL; | 430 | vdd = NULL; |
406 | vdd_io = NULL; | 431 | vdd_io = NULL; |
407 | 432 | ||
@@ -484,10 +509,14 @@ static int mag3110_probe(struct i2c_client *client, | |||
484 | ret = -EINVAL; | 509 | ret = -EINVAL; |
485 | goto error_rm_poll_dev; | 510 | goto error_rm_poll_dev; |
486 | } | 511 | } |
487 | /* set irq type to edge rising */ | 512 | |
488 | #if MAG3110_IRQ_USED | 513 | #if MAG3110_IRQ_USED |
489 | ret = request_irq(client->irq, mag3110_irq_handler, | 514 | irq_flag = irqd_get_trigger_type(irq_data); |
490 | IRQF_TRIGGER_RISING, client->dev.driver->name, idev); | 515 | irq_flag |= IRQF_ONESHOT; |
516 | if (shared_irq) | ||
517 | irq_flag |= IRQF_SHARED; | ||
518 | ret = request_threaded_irq(client->irq, NULL, mag3110_irq_handler, | ||
519 | irq_flag, client->dev.driver->name, idev); | ||
491 | if (ret < 0) { | 520 | if (ret < 0) { |
492 | dev_err(&client->dev, "failed to register irq %d!\n", | 521 | dev_err(&client->dev, "failed to register irq %d!\n", |
493 | client->irq); | 522 | client->irq); |