blob: d5053ebe84e1001bcb534e25a4f4d310929b0cd0 [file] [log] [blame]
Yan Zhang7d417c62013-02-01 11:15:51 +08001/*
2 * mma8x5x.c - Linux kernel modules for 3-Axis Orientation/Motion
3 * Detection Sensor MMA8451/MMA8452/MMA8453
4 *
Bingzhe Caid24b8e72013-07-05 17:50:38 +08005 * Copyright (c) 2013, The Linux Foundation. All Rights Reserved.
6 * Linux Foundation chooses to take subject only to the GPLv2 license
7 * terms, and distributes only under these terms.
Yan Zhang7d417c62013-02-01 11:15:51 +08008 * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
Bingzhe Caid24b8e72013-07-05 17:50:38 +080024#include <linux/kernel.h>
Yan Zhang7d417c62013-02-01 11:15:51 +080025#include <linux/module.h>
Yan Zhang7d417c62013-02-01 11:15:51 +080026#include <linux/slab.h>
Yan Zhang7d417c62013-02-01 11:15:51 +080027#include <linux/delay.h>
Bingzhe Caid24b8e72013-07-05 17:50:38 +080028#include <linux/i2c.h>
Yan Zhang7d417c62013-02-01 11:15:51 +080029#include <linux/input-polldev.h>
Jie Cheng8386f402013-12-04 15:14:09 +080030#include <linux/sensors.h>
Richard Liu1d49d4a2013-04-14 17:01:26 -070031#include <linux/regulator/consumer.h>
Bingzhe Caid24b8e72013-07-05 17:50:38 +080032#include <linux/of_gpio.h>
Yan Zhang7d417c62013-02-01 11:15:51 +080033
Bingzhe Caid24b8e72013-07-05 17:50:38 +080034#define ACCEL_INPUT_DEV_NAME "accelerometer"
Yan Zhang7d417c62013-02-01 11:15:51 +080035#define MMA8451_ID 0x1A
36#define MMA8452_ID 0x2A
37#define MMA8453_ID 0x3A
38#define MMA8652_ID 0x4A
39#define MMA8653_ID 0x5A
40
41
42#define POLL_INTERVAL_MIN 1
43#define POLL_INTERVAL_MAX 500
44#define POLL_INTERVAL 100 /* msecs */
45
46/* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */
Bingzhe Caibe5348a2013-09-05 18:27:19 +080047#define POLL_STOP_TIME 10000
Yan Zhang7d417c62013-02-01 11:15:51 +080048#define INPUT_FUZZ 32
49#define INPUT_FLAT 32
Xiaocheng Li7c0abb42013-07-12 15:41:56 -070050#define INPUT_DATA_DIVIDER 16
Yan Zhang7d417c62013-02-01 11:15:51 +080051#define MODE_CHANGE_DELAY_MS 100
52
53#define MMA8X5X_STATUS_ZYXDR 0x08
54#define MMA8X5X_BUF_SIZE 7
Richard Liu1d49d4a2013-04-14 17:01:26 -070055
mengmengc36b2f62013-07-23 07:46:28 +080056#define MMA_SHUTTEDDOWN (1 << 31)
Bingzhe Cai3fd4f572013-11-06 18:00:56 +080057#define MMA_STATE_MASK (~MMA_SHUTTEDDOWN)
mengmengc36b2f62013-07-23 07:46:28 +080058
Jie Cheng8386f402013-12-04 15:14:09 +080059static struct sensors_classdev sensors_cdev = {
60 .name = "mma8x5x-accel",
61 .vendor = "Freescale",
62 .version = 1,
63 .handle = SENSORS_ACCELERATION_HANDLE,
64 .type = SENSOR_TYPE_ACCELEROMETER,
65 .max_range = "19.6",
66 .resolution = "0.01",
67 .sensor_power = "0.2",
68 .min_delay = 2000,
69 .fifo_reserved_event_count = 0,
70 .fifo_max_event_count = 0,
71};
72
Richard Liu1d49d4a2013-04-14 17:01:26 -070073struct sensor_regulator {
74 struct regulator *vreg;
75 const char *name;
76 u32 min_uV;
77 u32 max_uV;
78};
79
80static struct sensor_regulator mma_vreg[] = {
81 {NULL, "vdd", 2850000, 2850000},
82 {NULL, "vio", 1800000, 1800000},
83};
84
Yan Zhang7d417c62013-02-01 11:15:51 +080085/* register enum for mma8x5x registers */
86enum {
87 MMA8X5X_STATUS = 0x00,
88 MMA8X5X_OUT_X_MSB,
89 MMA8X5X_OUT_X_LSB,
90 MMA8X5X_OUT_Y_MSB,
91 MMA8X5X_OUT_Y_LSB,
92 MMA8X5X_OUT_Z_MSB,
93 MMA8X5X_OUT_Z_LSB,
94
95 MMA8X5X_F_SETUP = 0x09,
96 MMA8X5X_TRIG_CFG,
97 MMA8X5X_SYSMOD,
98 MMA8X5X_INT_SOURCE,
99 MMA8X5X_WHO_AM_I,
100 MMA8X5X_XYZ_DATA_CFG,
101 MMA8X5X_HP_FILTER_CUTOFF,
102
103 MMA8X5X_PL_STATUS,
104 MMA8X5X_PL_CFG,
105 MMA8X5X_PL_COUNT,
106 MMA8X5X_PL_BF_ZCOMP,
107 MMA8X5X_P_L_THS_REG,
108
109 MMA8X5X_FF_MT_CFG,
110 MMA8X5X_FF_MT_SRC,
111 MMA8X5X_FF_MT_THS,
112 MMA8X5X_FF_MT_COUNT,
113
114 MMA8X5X_TRANSIENT_CFG = 0x1D,
115 MMA8X5X_TRANSIENT_SRC,
116 MMA8X5X_TRANSIENT_THS,
117 MMA8X5X_TRANSIENT_COUNT,
118
119 MMA8X5X_PULSE_CFG,
120 MMA8X5X_PULSE_SRC,
121 MMA8X5X_PULSE_THSX,
122 MMA8X5X_PULSE_THSY,
123 MMA8X5X_PULSE_THSZ,
124 MMA8X5X_PULSE_TMLT,
125 MMA8X5X_PULSE_LTCY,
126 MMA8X5X_PULSE_WIND,
127
128 MMA8X5X_ASLP_COUNT,
129 MMA8X5X_CTRL_REG1,
130 MMA8X5X_CTRL_REG2,
131 MMA8X5X_CTRL_REG3,
132 MMA8X5X_CTRL_REG4,
133 MMA8X5X_CTRL_REG5,
134
135 MMA8X5X_OFF_X,
136 MMA8X5X_OFF_Y,
137 MMA8X5X_OFF_Z,
138
139 MMA8X5X_REG_END,
140};
141
142/* The sensitivity is represented in counts/g. In 2g mode the
143sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512
144counts/g and in 8g mode the sensitivity is 256 counts/g.
145 */
146enum {
147 MODE_2G = 0,
148 MODE_4G,
149 MODE_8G,
150};
151
152enum {
153 MMA_STANDBY = 0,
154 MMA_ACTIVED,
155};
156struct mma8x5x_data_axis {
157 short x;
158 short y;
159 short z;
160};
161struct mma8x5x_data {
162 struct i2c_client *client;
163 struct input_polled_dev *poll_dev;
164 struct mutex data_lock;
165 int active;
166 int position;
167 u8 chip_id;
168 int mode;
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800169 int int_pin;
170 u32 int_flags;
Yan Zhang7d417c62013-02-01 11:15:51 +0800171};
172/* Addresses scanned */
173static const unsigned short normal_i2c[] = {0x1c, 0x1d, I2C_CLIENT_END};
174
175static int mma8x5x_chip_id[] = {
176 MMA8451_ID,
177 MMA8452_ID,
178 MMA8453_ID,
179 MMA8652_ID,
180 MMA8653_ID,
181};
182static char *mma8x5x_names[] = {
183 "mma8451",
184 "mma8452",
185 "mma8453",
186 "mma8652",
187 "mma8653",
188};
189static int mma8x5x_position_setting[8][3][3] = {
190 {{ 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} },
191 {{-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} },
192 {{ 0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
193 {{ 1, 0, 0}, { 0, 1, 0}, {0, 0, 1} },
194 {{ 0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
195 {{-1, 0, 0}, { 0, 1, 0}, {0, 0, -1} },
196 {{ 0, 1, 0}, { 1, 0, 0}, {0, 0, -1} },
197 {{ 1, 0, 0}, { 0, -1, 0}, {0, 0, -1} },
198};
199
Richard Liu1d49d4a2013-04-14 17:01:26 -0700200static int mma8x5x_config_regulator(struct i2c_client *client, bool on)
201{
202 int rc = 0, i;
203 int num_vreg = sizeof(mma_vreg)/sizeof(struct sensor_regulator);
204
205 if (on) {
206 for (i = 0; i < num_vreg; i++) {
207 mma_vreg[i].vreg = regulator_get(&client->dev,
208 mma_vreg[i].name);
209 if (IS_ERR(mma_vreg[i].vreg)) {
210 rc = PTR_ERR(mma_vreg[i].vreg);
211 dev_err(&client->dev, "%s:regulator get failed rc=%d\n",
212 __func__, rc);
213 mma_vreg[i].vreg = NULL;
214 goto error_vdd;
215 }
216 if (regulator_count_voltages(mma_vreg[i].vreg) > 0) {
217 rc = regulator_set_voltage(mma_vreg[i].vreg,
218 mma_vreg[i].min_uV, mma_vreg[i].max_uV);
219 if (rc) {
220 dev_err(&client->dev, "%s:set_voltage failed rc=%d\n",
221 __func__, rc);
222 regulator_put(mma_vreg[i].vreg);
223 mma_vreg[i].vreg = NULL;
224 goto error_vdd;
225 }
226 }
227 rc = regulator_enable(mma_vreg[i].vreg);
228 if (rc) {
229 dev_err(&client->dev, "%s: regulator_enable failed rc =%d\n",
230 __func__, rc);
231 if (regulator_count_voltages(mma_vreg[i].vreg)
232 > 0) {
233 regulator_set_voltage(mma_vreg[i].vreg,
234 0, mma_vreg[i].max_uV);
235 }
236 regulator_put(mma_vreg[i].vreg);
237 mma_vreg[i].vreg = NULL;
238 goto error_vdd;
239 }
240 }
241 return rc;
242 } else {
243 i = num_vreg;
244 }
245error_vdd:
246 while (--i >= 0) {
247 if (!IS_ERR_OR_NULL(mma_vreg[i].vreg)) {
248 if (regulator_count_voltages(
249 mma_vreg[i].vreg) > 0) {
250 regulator_set_voltage(mma_vreg[i].vreg, 0,
251 mma_vreg[i].max_uV);
252 }
253 regulator_disable(mma_vreg[i].vreg);
254 regulator_put(mma_vreg[i].vreg);
255 mma_vreg[i].vreg = NULL;
256 }
257 }
258 return rc;
259}
260
Yan Zhang7d417c62013-02-01 11:15:51 +0800261static int mma8x5x_data_convert(struct mma8x5x_data *pdata,
262 struct mma8x5x_data_axis *axis_data)
263{
264 short rawdata[3], data[3];
265 int i, j;
266 int position = pdata->position ;
267 if (position < 0 || position > 7)
268 position = 0;
269 rawdata[0] = axis_data->x;
270 rawdata[1] = axis_data->y;
271 rawdata[2] = axis_data->z;
272 for (i = 0; i < 3 ; i++) {
273 data[i] = 0;
274 for (j = 0; j < 3; j++)
275 data[i] += rawdata[j] *
276 mma8x5x_position_setting[position][i][j];
277 }
Xiaocheng Li7c0abb42013-07-12 15:41:56 -0700278 axis_data->x = data[0]/INPUT_DATA_DIVIDER;
279 axis_data->y = data[1]/INPUT_DATA_DIVIDER;
280 axis_data->z = data[2]/INPUT_DATA_DIVIDER;
Yan Zhang7d417c62013-02-01 11:15:51 +0800281 return 0;
282}
283static int mma8x5x_check_id(int id)
284{
285 int i = 0;
286 for (i = 0; i < sizeof(mma8x5x_chip_id)/sizeof(mma8x5x_chip_id[0]);
287 i++)
288 if (id == mma8x5x_chip_id[i])
289 return 1;
290 return 0;
291}
292static char *mma8x5x_id2name(u8 id)
293{
294 return mma8x5x_names[(id >> 4)-1];
295}
296static int mma8x5x_device_init(struct i2c_client *client)
297{
298 int result;
299 struct mma8x5x_data *pdata = i2c_get_clientdata(client);
300 result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, 0);
301 if (result < 0)
302 goto out;
303
304 result = i2c_smbus_write_byte_data(client, MMA8X5X_XYZ_DATA_CFG,
305 pdata->mode);
306 if (result < 0)
307 goto out;
308 pdata->active = MMA_STANDBY;
309 msleep(MODE_CHANGE_DELAY_MS);
310 return 0;
311out:
312 dev_err(&client->dev, "error when init mma8x5x:(%d)", result);
313 return result;
314}
315static int mma8x5x_device_stop(struct i2c_client *client)
316{
317 u8 val;
318 val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
319 i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xfe);
320 return 0;
321}
322
Bingzhe Cai70626742013-10-15 13:35:15 +0800323static int mma8x5x_device_start(struct i2c_client *client)
324{
325 struct mma8x5x_data *pdata = i2c_get_clientdata(client);
326
327 if (i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, 0))
328 goto err_out;
329 if (i2c_smbus_write_byte_data(client, MMA8X5X_XYZ_DATA_CFG,
330 pdata->mode))
331 goto err_out;
332
333 /* The BT(boot time) for mma8x5x is 1.55ms according to
334 *Freescale mma8450Q document. Document Number:MMA8450Q
335 *Rev: 9.1, 04/2012
336 */
337 usleep_range(1600, 2000);
338 return 0;
339
340err_out:
341 dev_err(&client->dev, "%s:start device failed", __func__);
342 return -EIO;
343}
344
Yan Zhang7d417c62013-02-01 11:15:51 +0800345static int mma8x5x_read_data(struct i2c_client *client,
346 struct mma8x5x_data_axis *data)
347{
348 u8 tmp_data[MMA8X5X_BUF_SIZE];
349 int ret;
350
351 ret = i2c_smbus_read_i2c_block_data(client,
352 MMA8X5X_OUT_X_MSB, 7, tmp_data);
353 if (ret < MMA8X5X_BUF_SIZE) {
354 dev_err(&client->dev, "i2c block read failed\n");
355 return -EIO;
356 }
357 data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
358 data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
359 data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
360 return 0;
361}
362
363static void mma8x5x_report_data(struct mma8x5x_data *pdata)
364{
365 struct input_polled_dev *poll_dev = pdata->poll_dev;
366 struct mma8x5x_data_axis data;
367 mutex_lock(&pdata->data_lock);
Bingzhe Cai3fd4f572013-11-06 18:00:56 +0800368 if ((pdata->active & MMA_STATE_MASK) == MMA_STANDBY) {
Yan Zhang7d417c62013-02-01 11:15:51 +0800369 poll_dev->poll_interval = POLL_STOP_TIME;
370 /* if standby ,set as 10s to slow the poll. */
371 goto out;
372 } else {
373 if (poll_dev->poll_interval == POLL_STOP_TIME)
374 poll_dev->poll_interval = POLL_INTERVAL;
375 }
376 if (mma8x5x_read_data(pdata->client, &data) != 0)
377 goto out;
378 mma8x5x_data_convert(pdata, &data);
379 input_report_abs(poll_dev->input, ABS_X, data.x);
380 input_report_abs(poll_dev->input, ABS_Y, data.y);
381 input_report_abs(poll_dev->input, ABS_Z, data.z);
382 input_sync(poll_dev->input);
383out:
384 mutex_unlock(&pdata->data_lock);
385}
386
387static void mma8x5x_dev_poll(struct input_polled_dev *dev)
388{
389 struct mma8x5x_data *pdata = (struct mma8x5x_data *)dev->private;
390 mma8x5x_report_data(pdata);
391}
392
393static ssize_t mma8x5x_enable_show(struct device *dev,
394 struct device_attribute *attr, char *buf)
395{
396 struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
397 struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
398 struct i2c_client *client = pdata->client;
399 u8 val;
400 int enable;
401
402 mutex_lock(&pdata->data_lock);
403 val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
404 if ((val & 0x01) && pdata->active == MMA_ACTIVED)
405 enable = 1;
406 else
407 enable = 0;
408 mutex_unlock(&pdata->data_lock);
409 return snprintf(buf, PAGE_SIZE, "%d\n", enable);
410}
411
412static ssize_t mma8x5x_enable_store(struct device *dev,
413 struct device_attribute *attr,
414 const char *buf, size_t count)
415{
416 struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
417 struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
418 struct i2c_client *client = pdata->client;
419 int ret;
420 unsigned long enable;
421 u8 val = 0;
Bingzhe Cai70626742013-10-15 13:35:15 +0800422
Yan Zhang7d417c62013-02-01 11:15:51 +0800423 ret = kstrtoul(buf, 10, &enable);
424 if (ret)
425 return ret;
426 mutex_lock(&pdata->data_lock);
427 enable = (enable > 0) ? 1 : 0;
Bingzhe Cai70626742013-10-15 13:35:15 +0800428 if (enable) {
429 if (pdata->active & MMA_SHUTTEDDOWN) {
430 ret = mma8x5x_config_regulator(client, 1);
431 if (ret)
432 goto err_failed;
433
434 ret = mma8x5x_device_start(client);
435 if (ret)
436 goto err_failed;
437
438 pdata->active &= ~MMA_SHUTTEDDOWN;
439 }
440 if (pdata->active == MMA_STANDBY) {
441 val = i2c_smbus_read_byte_data(client,
442 MMA8X5X_CTRL_REG1);
443 if (val < 0) {
444 dev_err(dev, "read device state failed!");
445 ret = val;
446 goto err_failed;
447 }
448
449 ret = i2c_smbus_write_byte_data(client,
450 MMA8X5X_CTRL_REG1, val | 0x01);
451 if (ret) {
452 dev_err(dev, "change device state failed!");
453 goto err_failed;
454 }
Yan Zhang7d417c62013-02-01 11:15:51 +0800455 pdata->active = MMA_ACTIVED;
Bingzhe Cai70626742013-10-15 13:35:15 +0800456 dev_dbg(dev, "%s:mma enable setting active.\n",
457 __func__);
Yan Zhang7d417c62013-02-01 11:15:51 +0800458 }
Bingzhe Cai70626742013-10-15 13:35:15 +0800459 } else if (enable == 0) {
460 if (pdata->active == MMA_ACTIVED) {
461 val = i2c_smbus_read_byte_data(client,
462 MMA8X5X_CTRL_REG1);
463 if (val < 0) {
464 dev_err(dev, "read device state failed!");
465 ret = val;
466 goto err_failed;
467 }
468
469 ret = i2c_smbus_write_byte_data(client,
470 MMA8X5X_CTRL_REG1, val & 0xFE);
471 if (ret) {
472 dev_err(dev, "change device state failed!");
473 goto err_failed;
474 }
475
Yan Zhang7d417c62013-02-01 11:15:51 +0800476 pdata->active = MMA_STANDBY;
Bingzhe Cai70626742013-10-15 13:35:15 +0800477 dev_dbg(dev, "%s:mma enable setting inactive.\n",
478 __func__);
Yan Zhang7d417c62013-02-01 11:15:51 +0800479 }
Bingzhe Cai70626742013-10-15 13:35:15 +0800480 if (!mma8x5x_config_regulator(client, 0))
481 pdata->active |= MMA_SHUTTEDDOWN;
Yan Zhang7d417c62013-02-01 11:15:51 +0800482 }
483 mutex_unlock(&pdata->data_lock);
484 return count;
Bingzhe Cai70626742013-10-15 13:35:15 +0800485
486err_failed:
487 mutex_unlock(&pdata->data_lock);
488 return ret;
Yan Zhang7d417c62013-02-01 11:15:51 +0800489}
490static ssize_t mma8x5x_position_show(struct device *dev,
491 struct device_attribute *attr, char *buf)
492{
493 struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
494 struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
495 int position = 0;
496 mutex_lock(&pdata->data_lock);
497 position = pdata->position ;
498 mutex_unlock(&pdata->data_lock);
499 return snprintf(buf, PAGE_SIZE, "%d\n", position);
500}
501
502static ssize_t mma8x5x_position_store(struct device *dev,
503 struct device_attribute *attr,
504 const char *buf, size_t count)
505{
506 struct input_polled_dev *poll_dev = dev_get_drvdata(dev);
507 struct mma8x5x_data *pdata = (struct mma8x5x_data *)(poll_dev->private);
508 int position;
509 int ret;
510 ret = kstrtoint(buf, 10, &position);
511 if (ret)
512 return ret;
513 mutex_lock(&pdata->data_lock);
514 pdata->position = position;
515 mutex_unlock(&pdata->data_lock);
516 return count;
517}
518
519static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
520 mma8x5x_enable_show, mma8x5x_enable_store);
521static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
522 mma8x5x_position_show, mma8x5x_position_store);
523
524static struct attribute *mma8x5x_attributes[] = {
525 &dev_attr_enable.attr,
526 &dev_attr_position.attr,
527 NULL
528};
529
530static const struct attribute_group mma8x5x_attr_group = {
531 .attrs = mma8x5x_attributes,
532};
533static int mma8x5x_detect(struct i2c_client *client,
534 struct i2c_board_info *info)
535{
536 struct i2c_adapter *adapter = client->adapter;
537 int chip_id;
538 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
539 return -ENODEV;
540 chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
541 if (!mma8x5x_check_id(chip_id))
542 return -ENODEV;
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800543 dev_dbg(&client->dev, "%s,check %s i2c address 0x%x.\n",
544 __func__, mma8x5x_id2name(chip_id), client->addr);
Yan Zhang7d417c62013-02-01 11:15:51 +0800545 strlcpy(info->type, "mma8x5x", I2C_NAME_SIZE);
546 return 0;
547}
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800548
549static int mma8x5x_parse_dt(struct device *dev, struct mma8x5x_data *data)
550{
551 int rc;
552 struct device_node *np = dev->of_node;
553 u32 temp_val;
554
555 data->int_pin = of_get_named_gpio_flags(np, "fsl,irq-gpio",
556 0, &data->int_flags);
557 if (data->int_pin < 0) {
558 dev_err(dev, "Unable to read irq-gpio\n");
559 return data->int_pin;
560 }
561
562 rc = of_property_read_u32(np, "fsl,sensors-position", &temp_val);
563 if (!rc)
564 data->position = temp_val;
565 else {
566 dev_err(dev, "Unable to read sensors-position\n");
567 return rc;
568 }
569
570 return 0;
571}
572
Yan Zhang7d417c62013-02-01 11:15:51 +0800573static int __devinit mma8x5x_probe(struct i2c_client *client,
574 const struct i2c_device_id *id)
575{
576 int result, chip_id;
577 struct input_dev *idev;
578 struct mma8x5x_data *pdata;
579 struct i2c_adapter *adapter;
580 struct input_polled_dev *poll_dev;
581 adapter = to_i2c_adapter(client->dev.parent);
Richard Liu1d49d4a2013-04-14 17:01:26 -0700582 /* power on the device */
583 result = mma8x5x_config_regulator(client, 1);
584 if (result)
585 goto err_power_on;
586
Yan Zhang7d417c62013-02-01 11:15:51 +0800587 result = i2c_check_functionality(adapter,
588 I2C_FUNC_SMBUS_BYTE |
589 I2C_FUNC_SMBUS_BYTE_DATA);
590 if (!result)
591 goto err_out;
592
593 chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
594
595 if (!mma8x5x_check_id(chip_id)) {
596 dev_err(&client->dev,
597 "read chip ID 0x%x is not equal to 0x%x,0x%x,0x%x,0x%x,0x%x!\n",
598 chip_id, MMA8451_ID, MMA8452_ID, MMA8453_ID,
599 MMA8652_ID, MMA8653_ID);
600 result = -EINVAL;
601 goto err_out;
602 }
Richard Liu1d49d4a2013-04-14 17:01:26 -0700603 /* set the private data */
Yan Zhang7d417c62013-02-01 11:15:51 +0800604 pdata = kzalloc(sizeof(struct mma8x5x_data), GFP_KERNEL);
605 if (!pdata) {
606 result = -ENOMEM;
607 dev_err(&client->dev, "alloc data memory error!\n");
608 goto err_out;
609 }
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800610
611 if (client->dev.of_node) {
612 result = mma8x5x_parse_dt(&client->dev, pdata);
613 if (result)
614 return result;
615 } else {
616 pdata->position = CONFIG_SENSORS_MMA_POSITION;
617 pdata->int_pin = -1;
618 pdata->int_flags = 0;
619 }
620
Yan Zhang7d417c62013-02-01 11:15:51 +0800621 /* Initialize the MMA8X5X chip */
622 pdata->client = client;
623 pdata->chip_id = chip_id;
624 pdata->mode = MODE_2G;
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800625
Yan Zhang7d417c62013-02-01 11:15:51 +0800626 mutex_init(&pdata->data_lock);
627 i2c_set_clientdata(client, pdata);
Richard Liu1d49d4a2013-04-14 17:01:26 -0700628 /* Initialize the MMA8X5X chip */
Yan Zhang7d417c62013-02-01 11:15:51 +0800629 mma8x5x_device_init(client);
Richard Liu1d49d4a2013-04-14 17:01:26 -0700630 /* create the input poll device */
Yan Zhang7d417c62013-02-01 11:15:51 +0800631 poll_dev = input_allocate_polled_device();
632 if (!poll_dev) {
633 result = -ENOMEM;
634 dev_err(&client->dev, "alloc poll device failed!\n");
635 goto err_alloc_poll_device;
636 }
637 poll_dev->poll = mma8x5x_dev_poll;
638 poll_dev->poll_interval = POLL_STOP_TIME;
639 poll_dev->poll_interval_min = POLL_INTERVAL_MIN;
640 poll_dev->poll_interval_max = POLL_INTERVAL_MAX;
641 poll_dev->private = pdata;
642 idev = poll_dev->input;
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800643 idev->name = ACCEL_INPUT_DEV_NAME;
Yan Zhang7d417c62013-02-01 11:15:51 +0800644 idev->uniq = mma8x5x_id2name(pdata->chip_id);
645 idev->id.bustype = BUS_I2C;
646 idev->evbit[0] = BIT_MASK(EV_ABS);
647 input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
648 input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
649 input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
650 pdata->poll_dev = poll_dev;
651 result = input_register_polled_device(pdata->poll_dev);
652 if (result) {
653 dev_err(&client->dev, "register poll device failed!\n");
654 goto err_register_polled_device;
655 }
656 result = sysfs_create_group(&idev->dev.kobj, &mma8x5x_attr_group);
657 if (result) {
658 dev_err(&client->dev, "create device file failed!\n");
659 result = -EINVAL;
660 goto err_create_sysfs;
661 }
Jie Cheng8386f402013-12-04 15:14:09 +0800662 result = sensors_classdev_register(&client->dev, &sensors_cdev);
663 if (result) {
664 dev_err(&client->dev, "create class device file failed!\n");
665 result = -EINVAL;
666 goto err_create_class_sysfs;
667 }
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800668 dev_info(&client->dev,
669 "%s:mma8x5x device driver probe successfully, position =%d\n",
670 __func__, pdata->position);
671
Yan Zhang7d417c62013-02-01 11:15:51 +0800672 return 0;
Jie Cheng8386f402013-12-04 15:14:09 +0800673err_create_class_sysfs:
674 sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group);
Yan Zhang7d417c62013-02-01 11:15:51 +0800675err_create_sysfs:
676 input_unregister_polled_device(pdata->poll_dev);
677err_register_polled_device:
678 input_free_polled_device(poll_dev);
679err_alloc_poll_device:
680 kfree(pdata);
681err_out:
Richard Liu1d49d4a2013-04-14 17:01:26 -0700682 mma8x5x_config_regulator(client, 0);
683err_power_on:
Yan Zhang7d417c62013-02-01 11:15:51 +0800684 return result;
685}
686static int __devexit mma8x5x_remove(struct i2c_client *client)
687{
688 struct mma8x5x_data *pdata = i2c_get_clientdata(client);
Richard Liu1d49d4a2013-04-14 17:01:26 -0700689 struct input_polled_dev *poll_dev;
Yan Zhang7d417c62013-02-01 11:15:51 +0800690 mma8x5x_device_stop(client);
691 if (pdata) {
Richard Liu1d49d4a2013-04-14 17:01:26 -0700692 poll_dev = pdata->poll_dev;
Yan Zhang7d417c62013-02-01 11:15:51 +0800693 input_unregister_polled_device(poll_dev);
694 input_free_polled_device(poll_dev);
695 kfree(pdata);
696 }
697 return 0;
698}
699
700#ifdef CONFIG_PM_SLEEP
701static int mma8x5x_suspend(struct device *dev)
702{
703 struct i2c_client *client = to_i2c_client(dev);
704 struct mma8x5x_data *pdata = i2c_get_clientdata(client);
705 if (pdata->active == MMA_ACTIVED)
706 mma8x5x_device_stop(client);
Bingzhe Cai70626742013-10-15 13:35:15 +0800707 if (pdata->active & MMA_SHUTTEDDOWN)
708 return 0;
mengmengc36b2f62013-07-23 07:46:28 +0800709 if (!mma8x5x_config_regulator(client, 0))
710 /* The highest bit sotres the power state */
711 pdata->active |= MMA_SHUTTEDDOWN;
Yan Zhang7d417c62013-02-01 11:15:51 +0800712 return 0;
713}
714
715static int mma8x5x_resume(struct device *dev)
716{
717 int val = 0;
718 struct i2c_client *client = to_i2c_client(dev);
719 struct mma8x5x_data *pdata = i2c_get_clientdata(client);
Bingzhe Cai70626742013-10-15 13:35:15 +0800720
721 /* No need to power on while device is shutdowned from standby state */
722 if (pdata->active == (MMA_SHUTTEDDOWN | MMA_STANDBY))
723 return 0;
mengmengc36b2f62013-07-23 07:46:28 +0800724 if (pdata->active & MMA_SHUTTEDDOWN) {
725 if (mma8x5x_config_regulator(client, 1))
726 goto out;
727
Bingzhe Cai70626742013-10-15 13:35:15 +0800728 if (mma8x5x_device_start(client))
mengmengc36b2f62013-07-23 07:46:28 +0800729 goto out;
mengmengc36b2f62013-07-23 07:46:28 +0800730 pdata->active &= ~MMA_SHUTTEDDOWN;
731 }
Yan Zhang7d417c62013-02-01 11:15:51 +0800732 if (pdata->active == MMA_ACTIVED) {
733 val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
734 i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val|0x01);
735 }
mengmengc36b2f62013-07-23 07:46:28 +0800736
Yan Zhang7d417c62013-02-01 11:15:51 +0800737 return 0;
mengmengc36b2f62013-07-23 07:46:28 +0800738out:
739 dev_err(&client->dev, "%s:failed during resume operation", __func__);
740 return -EIO;
Yan Zhang7d417c62013-02-01 11:15:51 +0800741
742}
743#endif
744
745static const struct i2c_device_id mma8x5x_id[] = {
746 {"mma8x5x", 0},
747 { }
748};
749MODULE_DEVICE_TABLE(i2c, mma8x5x_id);
750
Richard Liu1d49d4a2013-04-14 17:01:26 -0700751static const struct of_device_id mma8x5x_of_match[] = {
752 { .compatible = "fsl,mma8x5x", },
753 { },
754};
755
Yan Zhang7d417c62013-02-01 11:15:51 +0800756static SIMPLE_DEV_PM_OPS(mma8x5x_pm_ops, mma8x5x_suspend, mma8x5x_resume);
757static struct i2c_driver mma8x5x_driver = {
758 .class = I2C_CLASS_HWMON,
759 .driver = {
Richard Liu1d49d4a2013-04-14 17:01:26 -0700760 .name = "mma8x5x",
761 .owner = THIS_MODULE,
762 .pm = &mma8x5x_pm_ops,
763 .of_match_table = mma8x5x_of_match,
764 },
Yan Zhang7d417c62013-02-01 11:15:51 +0800765 .probe = mma8x5x_probe,
766 .remove = __devexit_p(mma8x5x_remove),
767 .id_table = mma8x5x_id,
768 .detect = mma8x5x_detect,
769 .address_list = normal_i2c,
770};
771
772static int __init mma8x5x_init(void)
773{
774 /* register driver */
775 int res;
776
777 res = i2c_add_driver(&mma8x5x_driver);
778 if (res < 0) {
Bingzhe Caid24b8e72013-07-05 17:50:38 +0800779 pr_info("%s:add mma8x5x i2c driver failed\n", __func__);
Yan Zhang7d417c62013-02-01 11:15:51 +0800780 return -ENODEV;
781 }
782 return res;
783}
784
785static void __exit mma8x5x_exit(void)
786{
787 i2c_del_driver(&mma8x5x_driver);
788}
789
790MODULE_AUTHOR("Freescale Semiconductor, Inc.");
791MODULE_DESCRIPTION("MMA8X5X 3-Axis Orientation/Motion Detection Sensor driver");
792MODULE_LICENSE("GPL");
793
794module_init(mma8x5x_init);
795module_exit(mma8x5x_exit);