diff options
author | luweizhou <b45643@freescale.com> | 2014-06-26 20:23:35 -0400 |
---|---|---|
committer | luweizhou <b45643@freescale.com> | 2014-06-26 21:15:47 -0400 |
commit | 97083f8b86a037c032520ce28562b2b14baf1f66 (patch) | |
tree | ec4ae05ae4d916f152eca4268afac04ed529e5e4 | |
parent | 7db52dbac37e7f0d6c963d1b768581d6b1e3dc54 (diff) |
ENGR00320098 hwmon: mma8451: Add a new mma8451 driver from android team update
The android app has its own interface for mma8451 data. The inerface isn't a regular input dev interface.
Since it is difficult to maintain the code within one file, one new file and configure will be added. It would
not be compiled in the default config. If want to use this driver, need to follow:
*Disable the CONFIG_MXC_MMA8451.
*Enable the CONFIG_MXC_MMA8x5x and make.
The code is from branch imx_3.10.y_android. The main modification is :
*Using device to pass into parameters related with platform not hard code.
*Codeing style issues
Signed-off-by: Luwei Zhou <b45643@freescale.com>
(cherry picked from commit e3faf9d404fcd3663c47cd1183e01a71c53b9eb8)
-rw-r--r-- | drivers/hwmon/Kconfig | 6 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/mma8x5x.c | 1036 |
3 files changed, 1043 insertions, 0 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 981a22f0812e..54192aacf45b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -1579,4 +1579,10 @@ config MXC_MMA8451 | |||
1579 | depends on I2C | 1579 | depends on I2C |
1580 | default y | 1580 | default y |
1581 | 1581 | ||
1582 | config MXC_MMA8x5x | ||
1583 | tristate "MMA8x5x device driver" | ||
1584 | depends on I2C && SYSFS | ||
1585 | default n | ||
1586 | help | ||
1587 | This configure is used by android sensor driver. | ||
1582 | endif # HWMON | 1588 | endif # HWMON |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 43fe743901ee..4fcafff867c3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -143,6 +143,7 @@ obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o | |||
143 | obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o | 143 | obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o |
144 | obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o | 144 | obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o |
145 | obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o | 145 | obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o |
146 | obj-$(CONFIG_MXC_MMA8x5x) += mma8x5x.o | ||
146 | 147 | ||
147 | obj-$(CONFIG_PMBUS) += pmbus/ | 148 | obj-$(CONFIG_PMBUS) += pmbus/ |
148 | 149 | ||
diff --git a/drivers/hwmon/mma8x5x.c b/drivers/hwmon/mma8x5x.c new file mode 100644 index 000000000000..d77dc515043f --- /dev/null +++ b/drivers/hwmon/mma8x5x.c | |||
@@ -0,0 +1,1036 @@ | |||
1 | /* | ||
2 | * mma8x5x.c - Linux kernel modules for 3-Axis Orientation/Motion | ||
3 | * Detection Sensor MMA8451/MMA8452/MMA8453/MMA8652/MMA8653 | ||
4 | * | ||
5 | * Copyright (C) 2012-2014 Freescale Semiconductor, Inc. All Rights Reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/pm.h> | ||
27 | #include <linux/mutex.h> | ||
28 | #include <linux/delay.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/irq.h> | ||
31 | #include <linux/hwmon-sysfs.h> | ||
32 | #include <linux/err.h> | ||
33 | #include <linux/poll.h> | ||
34 | #include <linux/hwmon.h> | ||
35 | #include <linux/input.h> | ||
36 | #include <linux/miscdevice.h> | ||
37 | #include <linux/regulator/consumer.h> | ||
38 | |||
39 | #define MMA8X5X_I2C_ADDR 0x1D | ||
40 | #define MMA8451_ID 0x1A | ||
41 | #define MMA8452_ID 0x2A | ||
42 | #define MMA8453_ID 0x3A | ||
43 | #define MMA8652_ID 0x4A | ||
44 | #define MMA8653_ID 0x5A | ||
45 | |||
46 | |||
47 | #define POLL_INTERVAL_MIN 1 | ||
48 | #define POLL_INTERVAL_MAX 500 | ||
49 | #define POLL_INTERVAL 100 /* msecs */ | ||
50 | |||
51 | /* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */ | ||
52 | #define POLL_STOP_TIME 200 | ||
53 | #define INPUT_FUZZ 32 | ||
54 | #define INPUT_FLAT 32 | ||
55 | #define MODE_CHANGE_DELAY_MS 100 | ||
56 | |||
57 | #define MMA8X5X_STATUS_ZYXDR 0x08 | ||
58 | #define MMA8X5X_BUF_SIZE 6 | ||
59 | |||
60 | #define MMA8X5X_FIFO_SIZE 32 | ||
61 | /* register enum for mma8x5x registers */ | ||
62 | enum { | ||
63 | MMA8X5X_STATUS = 0x00, | ||
64 | MMA8X5X_OUT_X_MSB, | ||
65 | MMA8X5X_OUT_X_LSB, | ||
66 | MMA8X5X_OUT_Y_MSB, | ||
67 | MMA8X5X_OUT_Y_LSB, | ||
68 | MMA8X5X_OUT_Z_MSB, | ||
69 | MMA8X5X_OUT_Z_LSB, | ||
70 | |||
71 | MMA8X5X_F_SETUP = 0x09, | ||
72 | MMA8X5X_TRIG_CFG, | ||
73 | MMA8X5X_SYSMOD, | ||
74 | MMA8X5X_INT_SOURCE, | ||
75 | MMA8X5X_WHO_AM_I, | ||
76 | MMA8X5X_XYZ_DATA_CFG, | ||
77 | MMA8X5X_HP_FILTER_CUTOFF, | ||
78 | |||
79 | MMA8X5X_PL_STATUS, | ||
80 | MMA8X5X_PL_CFG, | ||
81 | MMA8X5X_PL_COUNT, | ||
82 | MMA8X5X_PL_BF_ZCOMP, | ||
83 | MMA8X5X_P_L_THS_REG, | ||
84 | |||
85 | MMA8X5X_FF_MT_CFG, | ||
86 | MMA8X5X_FF_MT_SRC, | ||
87 | MMA8X5X_FF_MT_THS, | ||
88 | MMA8X5X_FF_MT_COUNT, | ||
89 | |||
90 | MMA8X5X_TRANSIENT_CFG = 0x1D, | ||
91 | MMA8X5X_TRANSIENT_SRC, | ||
92 | MMA8X5X_TRANSIENT_THS, | ||
93 | MMA8X5X_TRANSIENT_COUNT, | ||
94 | |||
95 | MMA8X5X_PULSE_CFG, | ||
96 | MMA8X5X_PULSE_SRC, | ||
97 | MMA8X5X_PULSE_THSX, | ||
98 | MMA8X5X_PULSE_THSY, | ||
99 | MMA8X5X_PULSE_THSZ, | ||
100 | MMA8X5X_PULSE_TMLT, | ||
101 | MMA8X5X_PULSE_LTCY, | ||
102 | MMA8X5X_PULSE_WIND, | ||
103 | |||
104 | MMA8X5X_ASLP_COUNT, | ||
105 | MMA8X5X_CTRL_REG1, | ||
106 | MMA8X5X_CTRL_REG2, | ||
107 | MMA8X5X_CTRL_REG3, | ||
108 | MMA8X5X_CTRL_REG4, | ||
109 | MMA8X5X_CTRL_REG5, | ||
110 | |||
111 | MMA8X5X_OFF_X, | ||
112 | MMA8X5X_OFF_Y, | ||
113 | MMA8X5X_OFF_Z, | ||
114 | |||
115 | MMA8X5X_REG_END, | ||
116 | }; | ||
117 | |||
118 | /* The sensitivity is represented in counts/g. In 2g mode the | ||
119 | sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512 | ||
120 | counts/g and in 8g mode the sensitivity is 256 counts/g. | ||
121 | */ | ||
122 | enum { | ||
123 | MODE_2G = 0, | ||
124 | MODE_4G, | ||
125 | MODE_8G, | ||
126 | }; | ||
127 | |||
128 | enum { | ||
129 | MMA_STANDBY = 0, | ||
130 | MMA_ACTIVED, | ||
131 | }; | ||
132 | #pragma pack(1) | ||
133 | struct mma8x5x_data_axis { | ||
134 | short x; | ||
135 | short y; | ||
136 | short z; | ||
137 | }; | ||
138 | struct mma8x5x_fifo{ | ||
139 | int count; | ||
140 | s64 period; | ||
141 | s64 timestamp; | ||
142 | struct mma8x5x_data_axis fifo_data[MMA8X5X_FIFO_SIZE]; | ||
143 | }; | ||
144 | #pragma pack() | ||
145 | |||
146 | struct mma8x5x_data { | ||
147 | struct i2c_client *client; | ||
148 | struct input_dev *idev; | ||
149 | struct delayed_work work; | ||
150 | struct mutex data_lock; | ||
151 | struct mma8x5x_fifo fifo; | ||
152 | wait_queue_head_t fifo_wq; | ||
153 | atomic_t fifo_ready; | ||
154 | int active; | ||
155 | int delay; | ||
156 | int position; | ||
157 | u8 chip_id; | ||
158 | int mode; | ||
159 | int awaken; | ||
160 | s64 period_rel; | ||
161 | int fifo_wakeup; | ||
162 | int fifo_timeout; | ||
163 | u32 int_pin; | ||
164 | }; | ||
165 | |||
166 | static struct mma8x5x_data *p_mma8x5x_data; | ||
167 | |||
168 | /* Addresses scanned */ | ||
169 | static const unsigned short normal_i2c[] = { 0x1c, 0x1d, I2C_CLIENT_END }; | ||
170 | |||
171 | static int mma8x5x_chip_id[] = { | ||
172 | MMA8451_ID, | ||
173 | MMA8452_ID, | ||
174 | MMA8453_ID, | ||
175 | MMA8652_ID, | ||
176 | MMA8653_ID, | ||
177 | }; | ||
178 | static char *mma8x5x_names[] = { | ||
179 | "mma8451", | ||
180 | "mma8452", | ||
181 | "mma8453", | ||
182 | "mma8652", | ||
183 | "mma8653", | ||
184 | }; | ||
185 | static int mma8x5x_position_setting[8][3][3] = { | ||
186 | { { 0, -1, 0 }, { 1, 0, 0 }, { 0, 0, 1 } }, | ||
187 | { { -1, 0, 0 }, { 0, -1, 0 }, { 0, 0, 1 } }, | ||
188 | { { 0, 1, 0 }, { -1, 0, 0 }, { 0, 0, 1 } }, | ||
189 | { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, | ||
190 | |||
191 | { { 0, -1, 0 }, { -1, 0, 0 }, { 0, 0, -1 } }, | ||
192 | { { -1, 0, 0 }, { 0, 1, 0 }, { 0, 0, -1 } }, | ||
193 | { { 0, 1, 0 }, { 1, 0, 0 }, { 0, 0, -1 } }, | ||
194 | { { 1, 0, 0 }, { 0, -1, 0 }, { 0, 0, -1 } }, | ||
195 | }; | ||
196 | static int mma8x5x_data_convert(struct mma8x5x_data *pdata, | ||
197 | struct mma8x5x_data_axis *axis_data) | ||
198 | { | ||
199 | short rawdata[3], data[3]; | ||
200 | int i, j; | ||
201 | int position = pdata->position; | ||
202 | |||
203 | if (position < 0 || position > 7) | ||
204 | position = 0; | ||
205 | rawdata[0] = axis_data->x; | ||
206 | rawdata[1] = axis_data->y; | ||
207 | rawdata[2] = axis_data->z; | ||
208 | for (i = 0; i < 3; i++) { | ||
209 | data[i] = 0; | ||
210 | for (j = 0; j < 3; j++) | ||
211 | data[i] += rawdata[j] * | ||
212 | mma8x5x_position_setting[position][i][j]; | ||
213 | } | ||
214 | axis_data->x = data[0]; | ||
215 | axis_data->y = data[1]; | ||
216 | axis_data->z = data[2]; | ||
217 | return 0; | ||
218 | } | ||
219 | static int mma8x5x_check_id(int id) | ||
220 | { | ||
221 | int i = 0; | ||
222 | |||
223 | for (i = 0; i < sizeof(mma8x5x_chip_id) / | ||
224 | sizeof(mma8x5x_chip_id[0]); i++) | ||
225 | if (id == mma8x5x_chip_id[i]) | ||
226 | return 1; | ||
227 | return 0; | ||
228 | } | ||
229 | static char *mma8x5x_id2name(u8 id) | ||
230 | { | ||
231 | return mma8x5x_names[(id >> 4) - 1]; | ||
232 | } | ||
233 | |||
234 | static int mma8x5x_i2c_read_fifo(struct i2c_client *client, | ||
235 | u8 reg, char *buf, int len) | ||
236 | { | ||
237 | char send_buf[] = {reg}; | ||
238 | struct i2c_msg msgs[] = { | ||
239 | { | ||
240 | .addr = client->addr, | ||
241 | .flags = 0, | ||
242 | .len = 1, | ||
243 | .buf = send_buf, | ||
244 | }, | ||
245 | { | ||
246 | .addr = client->addr, | ||
247 | .flags = I2C_M_RD, | ||
248 | .len = len, | ||
249 | .buf = buf, | ||
250 | }, | ||
251 | }; | ||
252 | if (i2c_transfer(client->adapter, msgs, 2) < 0) { | ||
253 | printk(KERN_ERR "mma8x5x: transfer error\n"); | ||
254 | return -EIO; | ||
255 | } else | ||
256 | return len; | ||
257 | } | ||
258 | |||
259 | /*period is ms, return the real period per event*/ | ||
260 | static s64 mma8x5x_odr_set(struct i2c_client *client, int period) | ||
261 | { | ||
262 | u8 odr; | ||
263 | u8 val; | ||
264 | s64 period_rel; | ||
265 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
266 | /*Standby*/ | ||
267 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & (~0x01)); | ||
268 | val &= ~(0x07 << 3); | ||
269 | if (period >= 640) { | ||
270 | /*1.56HZ*/ | ||
271 | odr = 0x7; | ||
272 | period_rel = 640 * NSEC_PER_MSEC; | ||
273 | } else if (period >= 160) { | ||
274 | /*6.25HZ*/ | ||
275 | odr = 0x06; | ||
276 | period_rel = 160 * NSEC_PER_MSEC; | ||
277 | } else if (period >= 80) { | ||
278 | /*12.5HZ*/ | ||
279 | odr = 0x05; | ||
280 | period_rel = 80 * NSEC_PER_MSEC; | ||
281 | } else if (period >= 20) { | ||
282 | /*50HZ*/ | ||
283 | odr = 0x04; | ||
284 | period_rel = 20 * NSEC_PER_MSEC; | ||
285 | } else if (period >= 10) { | ||
286 | /*100HZ*/ | ||
287 | odr = 0x03; | ||
288 | period_rel = 10 * NSEC_PER_MSEC; | ||
289 | } else if (period >= 5) { | ||
290 | /*200HZ*/ | ||
291 | odr = 0x02; | ||
292 | period_rel = 5 * NSEC_PER_MSEC; | ||
293 | } else if ((period * 2) >= 5) { | ||
294 | /*400HZ*/ | ||
295 | odr = 0x01; | ||
296 | period_rel = 2500 * NSEC_PER_USEC; | ||
297 | } else { | ||
298 | /*800HZ*/ | ||
299 | odr = 0x00; | ||
300 | period_rel = 1250 * NSEC_PER_USEC; | ||
301 | } | ||
302 | val |= (odr << 3); | ||
303 | /*Standby*/ | ||
304 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val); | ||
305 | return period_rel; | ||
306 | } | ||
307 | static int mma8x5x_device_init(struct i2c_client *client) | ||
308 | { | ||
309 | int result; | ||
310 | struct mma8x5x_data *pdata = i2c_get_clientdata(client); | ||
311 | |||
312 | result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, 0); | ||
313 | if (result < 0) | ||
314 | goto out; | ||
315 | |||
316 | result = i2c_smbus_write_byte_data(client, MMA8X5X_XYZ_DATA_CFG, | ||
317 | pdata->mode); | ||
318 | if (result < 0) | ||
319 | goto out; | ||
320 | pdata->active = MMA_STANDBY; | ||
321 | msleep(MODE_CHANGE_DELAY_MS); | ||
322 | return 0; | ||
323 | out: | ||
324 | dev_err(&client->dev, "error when init mma8x5x:(%d)", result); | ||
325 | return result; | ||
326 | } | ||
327 | static int mma8x5x_device_stop(struct i2c_client *client) | ||
328 | { | ||
329 | u8 val; | ||
330 | |||
331 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
332 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xfe); | ||
333 | return 0; | ||
334 | } | ||
335 | static int mma8x5x_read_data(struct i2c_client *client, | ||
336 | struct mma8x5x_data_axis *data) | ||
337 | { | ||
338 | u8 tmp_data[MMA8X5X_BUF_SIZE]; | ||
339 | int ret; | ||
340 | |||
341 | ret = i2c_smbus_read_i2c_block_data(client, | ||
342 | MMA8X5X_OUT_X_MSB, | ||
343 | MMA8X5X_BUF_SIZE, tmp_data); | ||
344 | if (ret < MMA8X5X_BUF_SIZE) { | ||
345 | dev_err(&client->dev, "i2c block read failed\n"); | ||
346 | return -EIO; | ||
347 | } | ||
348 | data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1]; | ||
349 | data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3]; | ||
350 | data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5]; | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int mma8x5x_fifo_interrupt(struct i2c_client *client, int enable) | ||
355 | { | ||
356 | u8 val, sys_mode; | ||
357 | sys_mode = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
358 | /*standby*/ | ||
359 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, | ||
360 | (sys_mode & (~0x01))); | ||
361 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG4); | ||
362 | val &= ~(0x01 << 6); | ||
363 | if (enable) | ||
364 | val |= (0x01 << 6); | ||
365 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG4, val); | ||
366 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, sys_mode); | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int mma8x5x_fifo_setting(struct mma8x5x_data *pdata, | ||
371 | int time_out, int is_overwrite) | ||
372 | { | ||
373 | u8 val, sys_mode, pin_cfg; | ||
374 | struct i2c_client *client = pdata->client; | ||
375 | sys_mode = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
376 | /*standby*/ | ||
377 | i2c_smbus_write_byte_data(client, | ||
378 | MMA8X5X_CTRL_REG1, | ||
379 | (sys_mode & (~0x01))); | ||
380 | pin_cfg = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG5); | ||
381 | val = i2c_smbus_read_byte_data(client, MMA8X5X_F_SETUP); | ||
382 | val &= ~(0x03 << 6); | ||
383 | if (time_out > 0) { | ||
384 | if (is_overwrite) | ||
385 | val |= (0x01 << 6); | ||
386 | else | ||
387 | val |= (0x02 << 6); | ||
388 | } | ||
389 | i2c_smbus_write_byte_data(client, MMA8X5X_F_SETUP, val); | ||
390 | /*route to pin 1*/ | ||
391 | if (pdata->int_pin == 1) | ||
392 | i2c_smbus_write_byte_data(client, | ||
393 | MMA8X5X_CTRL_REG5, | ||
394 | pin_cfg | (0x01 << 6)); | ||
395 | /*route to pin 1*/ | ||
396 | else | ||
397 | i2c_smbus_write_byte_data(client, | ||
398 | MMA8X5X_CTRL_REG5, | ||
399 | pin_cfg & ~(0x01 << 6)); | ||
400 | |||
401 | i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, sys_mode); | ||
402 | |||
403 | if (time_out > 0) { | ||
404 | /*fifo len is 32*/ | ||
405 | pdata->period_rel = mma8x5x_odr_set(client, time_out/32); | ||
406 | } | ||
407 | return 0; | ||
408 | } | ||
409 | static int mma8x5x_read_fifo_data(struct mma8x5x_data *pdata) | ||
410 | { | ||
411 | int count, cnt; | ||
412 | u8 buf[256], val; | ||
413 | int i, index; | ||
414 | struct i2c_client *client = pdata->client; | ||
415 | struct mma8x5x_fifo *pfifo = &pdata->fifo; | ||
416 | struct timespec ts; | ||
417 | val = i2c_smbus_read_byte_data(client, MMA8X5X_STATUS); | ||
418 | /*FIFO overflow*/ | ||
419 | if (val & (0x01 << 7)) { | ||
420 | cnt = (val & 0x3f); | ||
421 | count = mma8x5x_i2c_read_fifo(client, MMA8X5X_OUT_X_MSB, | ||
422 | buf, MMA8X5X_BUF_SIZE * cnt); | ||
423 | if (count > 0) { | ||
424 | ktime_get_ts(&ts); | ||
425 | for (i = 0; i < count/MMA8X5X_BUF_SIZE ; i++) { | ||
426 | index = MMA8X5X_BUF_SIZE * i; | ||
427 | pfifo->fifo_data[i].x = | ||
428 | ((buf[index] << 8) & 0xff00) | buf[index + 1]; | ||
429 | pfifo->fifo_data[i].y = | ||
430 | ((buf[index + 2] << 8) & 0xff00) | buf[index + 3]; | ||
431 | pfifo->fifo_data[i].z = | ||
432 | ((buf[index + 4] << 8) & 0xff00) | buf[index + 5]; | ||
433 | mma8x5x_data_convert(pdata, | ||
434 | &pfifo->fifo_data[i]); | ||
435 | } | ||
436 | pfifo->period = pdata->period_rel; | ||
437 | pfifo->count = count / MMA8X5X_BUF_SIZE; | ||
438 | pfifo->timestamp = ((s64)ts.tv_sec) * NSEC_PER_SEC | ||
439 | + ts.tv_nsec; | ||
440 | return 0; | ||
441 | } | ||
442 | } | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | static void mma8x5x_report_data(struct mma8x5x_data *pdata) | ||
447 | { | ||
448 | struct mma8x5x_data_axis data; | ||
449 | int ret; | ||
450 | ret = mma8x5x_read_data(pdata->client, &data); | ||
451 | if (!ret) { | ||
452 | mma8x5x_data_convert(pdata, &data); | ||
453 | input_report_abs(pdata->idev, ABS_X, data.x); | ||
454 | input_report_abs(pdata->idev, ABS_Y, data.y); | ||
455 | input_report_abs(pdata->idev, ABS_Z, data.z); | ||
456 | input_sync(pdata->idev); | ||
457 | } | ||
458 | } | ||
459 | static void mma8x5x_work(struct mma8x5x_data *pdata) | ||
460 | { | ||
461 | int delay; | ||
462 | if (pdata->active == MMA_ACTIVED) { | ||
463 | delay = msecs_to_jiffies(pdata->delay); | ||
464 | if (delay >= HZ) | ||
465 | delay = round_jiffies_relative(delay); | ||
466 | schedule_delayed_work(&pdata->work, delay); | ||
467 | } | ||
468 | } | ||
469 | static void mma8x5x_dev_poll(struct work_struct *work) | ||
470 | { | ||
471 | struct mma8x5x_data *pdata = container_of(work, | ||
472 | struct mma8x5x_data, | ||
473 | work.work); | ||
474 | mma8x5x_report_data(pdata); | ||
475 | mma8x5x_work(pdata); | ||
476 | } | ||
477 | static irqreturn_t mma8x5x_irq_handler(int irq, void *dev) | ||
478 | { | ||
479 | int ret; | ||
480 | u8 int_src; | ||
481 | struct mma8x5x_data *pdata = (struct mma8x5x_data *)dev; | ||
482 | int_src = i2c_smbus_read_byte_data(pdata->client, MMA8X5X_INT_SOURCE); | ||
483 | if (int_src & (0x01 << 6)) { | ||
484 | ret = mma8x5x_read_fifo_data(pdata); | ||
485 | if (!ret) { | ||
486 | atomic_set(&pdata->fifo_ready, 1); | ||
487 | wake_up(&pdata->fifo_wq); | ||
488 | } | ||
489 | /*is just awken from suspend*/ | ||
490 | if (pdata->awaken) { | ||
491 | /*10s timeout*/ | ||
492 | mma8x5x_fifo_setting(pdata, pdata->fifo_timeout, 0); | ||
493 | mma8x5x_fifo_interrupt(pdata->client, 1); | ||
494 | pdata->awaken = 0; | ||
495 | } | ||
496 | } | ||
497 | return IRQ_HANDLED; | ||
498 | } | ||
499 | |||
500 | static ssize_t mma8x5x_enable_show(struct device *dev, | ||
501 | struct device_attribute *attr, char *buf) | ||
502 | { | ||
503 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
504 | struct i2c_client *client = pdata->client; | ||
505 | u8 val; | ||
506 | int enable; | ||
507 | |||
508 | mutex_lock(&pdata->data_lock); | ||
509 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
510 | if ((val & 0x01) && pdata->active == MMA_ACTIVED) | ||
511 | enable = 1; | ||
512 | else | ||
513 | enable = 0; | ||
514 | mutex_unlock(&pdata->data_lock); | ||
515 | return sprintf(buf, "%d\n", enable); | ||
516 | } | ||
517 | |||
518 | static ssize_t mma8x5x_enable_store(struct device *dev, | ||
519 | struct device_attribute *attr, | ||
520 | const char *buf, size_t count) | ||
521 | { | ||
522 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
523 | struct i2c_client *client = pdata->client; | ||
524 | int ret; | ||
525 | unsigned long enable; | ||
526 | u8 val = 0; | ||
527 | |||
528 | ret = strict_strtol(buf, 10, &enable); | ||
529 | if (ret) { | ||
530 | dev_err(dev, "string to long error\n"); | ||
531 | return ret; | ||
532 | } | ||
533 | mutex_lock(&pdata->data_lock); | ||
534 | enable = (enable > 0) ? 1 : 0; | ||
535 | if (enable && pdata->active == MMA_STANDBY) { | ||
536 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
537 | ret = i2c_smbus_write_byte_data(client, | ||
538 | MMA8X5X_CTRL_REG1, | ||
539 | val | 0x01); | ||
540 | if (!ret) { | ||
541 | pdata->active = MMA_ACTIVED; | ||
542 | /*continuous mode*/ | ||
543 | if (pdata->fifo_timeout <= 0) | ||
544 | mma8x5x_work(pdata); | ||
545 | else { | ||
546 | /*fifo mode*/ | ||
547 | mma8x5x_fifo_setting(pdata, | ||
548 | pdata->fifo_timeout, 0); | ||
549 | mma8x5x_fifo_interrupt(client, 1); | ||
550 | } | ||
551 | printk(KERN_INFO"mma enable setting active\n"); | ||
552 | } | ||
553 | } else if (enable == 0 && pdata->active == MMA_ACTIVED) { | ||
554 | val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1); | ||
555 | ret = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, | ||
556 | val & 0xFE); | ||
557 | if (!ret) { | ||
558 | pdata->active = MMA_STANDBY; | ||
559 | if (pdata->fifo_timeout <= 0) | ||
560 | /*continuous mode*/ | ||
561 | cancel_delayed_work_sync(&pdata->work); | ||
562 | else { | ||
563 | /*fifo mode*/ | ||
564 | mma8x5x_fifo_setting(pdata, 0, 0); | ||
565 | mma8x5x_fifo_interrupt(client, 0); | ||
566 | } | ||
567 | printk(KERN_INFO"mma enable setting inactive\n"); | ||
568 | } | ||
569 | } | ||
570 | mutex_unlock(&pdata->data_lock); | ||
571 | return count; | ||
572 | } | ||
573 | |||
574 | static ssize_t mma8x5x_delay_show(struct device *dev, | ||
575 | struct device_attribute *attr, char *buf) | ||
576 | { | ||
577 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
578 | int delay; | ||
579 | mutex_lock(&pdata->data_lock); | ||
580 | delay = pdata->delay; | ||
581 | mutex_unlock(&pdata->data_lock); | ||
582 | return sprintf(buf, "%d\n", delay); | ||
583 | } | ||
584 | |||
585 | static ssize_t mma8x5x_delay_store(struct device *dev, | ||
586 | struct device_attribute *attr, | ||
587 | const char *buf, size_t count) | ||
588 | { | ||
589 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
590 | struct i2c_client *client = pdata->client; | ||
591 | int ret; | ||
592 | long delay; | ||
593 | ret = strict_strtol(buf, 10, &delay); | ||
594 | if (ret) { | ||
595 | dev_err(dev, "string to long error\n"); | ||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | mutex_lock(&pdata->data_lock); | ||
600 | cancel_delayed_work_sync(&pdata->work); | ||
601 | pdata->delay = (int)delay; | ||
602 | if (pdata->active == MMA_ACTIVED && pdata->fifo_timeout <= 0) { | ||
603 | mma8x5x_odr_set(client, (int)delay); | ||
604 | mma8x5x_work(pdata); | ||
605 | } | ||
606 | mutex_unlock(&pdata->data_lock); | ||
607 | return count; | ||
608 | } | ||
609 | |||
610 | static ssize_t mma8x5x_fifo_show(struct device *dev, | ||
611 | struct device_attribute *attr, char *buf) | ||
612 | { | ||
613 | int count = 0; | ||
614 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
615 | mutex_lock(&pdata->data_lock); | ||
616 | count = sprintf(&buf[count], "period poll :%d ms\n", pdata->delay); | ||
617 | count += sprintf(&buf[count], "period fifo :%lld ns\n", | ||
618 | pdata->period_rel); | ||
619 | count += sprintf(&buf[count], "timeout :%d ms\n", pdata->fifo_timeout); | ||
620 | /*is the interrupt enable*/ | ||
621 | count += sprintf(&buf[count], "interrupt wake up: %s\n", | ||
622 | (pdata->fifo_wakeup ? "yes" : "no")); | ||
623 | mutex_unlock(&pdata->data_lock); | ||
624 | return count; | ||
625 | } | ||
626 | |||
627 | static ssize_t mma8x5x_fifo_store(struct device *dev, | ||
628 | struct device_attribute *attr, | ||
629 | const char *buf, size_t count) | ||
630 | { | ||
631 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
632 | struct i2c_client *client = pdata->client; | ||
633 | int period, timeout, wakeup; | ||
634 | sscanf(buf, "%d,%d,%d", &period, &timeout, &wakeup); | ||
635 | printk(KERN_INFO"period %d ,timeout is %d, wake up is :%d\n", | ||
636 | period, timeout, wakeup); | ||
637 | if (timeout > 0) { | ||
638 | mutex_lock(&pdata->data_lock); | ||
639 | cancel_delayed_work_sync(&pdata->work); | ||
640 | pdata->delay = period; | ||
641 | mutex_unlock(&pdata->data_lock); | ||
642 | /*no overwirte fifo*/ | ||
643 | mma8x5x_fifo_setting(pdata, timeout, 0); | ||
644 | mma8x5x_fifo_interrupt(client, 1); | ||
645 | pdata->fifo_timeout = timeout; | ||
646 | pdata->fifo_wakeup = wakeup; | ||
647 | } else { | ||
648 | /*no overwirte fifo*/ | ||
649 | mma8x5x_fifo_setting(pdata, timeout, 0); | ||
650 | mma8x5x_fifo_interrupt(client, 0); | ||
651 | pdata->fifo_timeout = timeout; | ||
652 | pdata->fifo_wakeup = wakeup; | ||
653 | mutex_lock(&pdata->data_lock); | ||
654 | pdata->delay = period; | ||
655 | if (pdata->active == MMA_ACTIVED) | ||
656 | mma8x5x_work(pdata); | ||
657 | mutex_unlock(&pdata->data_lock); | ||
658 | } | ||
659 | return count; | ||
660 | } | ||
661 | |||
662 | static ssize_t mma8x5x_position_show(struct device *dev, | ||
663 | struct device_attribute *attr, char *buf) | ||
664 | { | ||
665 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
666 | int position = 0; | ||
667 | mutex_lock(&pdata->data_lock); | ||
668 | position = pdata->position; | ||
669 | mutex_unlock(&pdata->data_lock); | ||
670 | return sprintf(buf, "%d\n", position); | ||
671 | } | ||
672 | |||
673 | static ssize_t mma8x5x_position_store(struct device *dev, | ||
674 | struct device_attribute *attr, | ||
675 | const char *buf, size_t count) | ||
676 | { | ||
677 | struct mma8x5x_data *pdata = dev_get_drvdata(dev); | ||
678 | int ret; | ||
679 | long position; | ||
680 | ret = strict_strtol(buf, 10, &position); | ||
681 | if (ret) { | ||
682 | dev_err(dev, "string to long error\n"); | ||
683 | return ret; | ||
684 | } | ||
685 | mutex_lock(&pdata->data_lock); | ||
686 | pdata->position = (int)position; | ||
687 | mutex_unlock(&pdata->data_lock); | ||
688 | return count; | ||
689 | } | ||
690 | |||
691 | static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, | ||
692 | mma8x5x_enable_show, mma8x5x_enable_store); | ||
693 | static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO, | ||
694 | mma8x5x_delay_show, mma8x5x_delay_store); | ||
695 | |||
696 | static DEVICE_ATTR(fifo, S_IWUSR | S_IRUGO, | ||
697 | mma8x5x_fifo_show, mma8x5x_fifo_store); | ||
698 | static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, | ||
699 | mma8x5x_position_show, mma8x5x_position_store); | ||
700 | |||
701 | static struct attribute *mma8x5x_attributes[] = { | ||
702 | &dev_attr_enable.attr, | ||
703 | &dev_attr_poll_delay.attr, | ||
704 | &dev_attr_fifo.attr, | ||
705 | &dev_attr_position.attr, | ||
706 | NULL | ||
707 | }; | ||
708 | |||
709 | static const struct attribute_group mma8x5x_attr_group = { | ||
710 | .attrs = mma8x5x_attributes, | ||
711 | }; | ||
712 | static int mma8x5x_detect(struct i2c_client *client, | ||
713 | struct i2c_board_info *info) | ||
714 | { | ||
715 | struct i2c_adapter *adapter = client->adapter; | ||
716 | int chip_id; | ||
717 | |||
718 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) | ||
719 | return -ENODEV; | ||
720 | chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I); | ||
721 | if (!mma8x5x_check_id(chip_id)) | ||
722 | return -ENODEV; | ||
723 | printk(KERN_INFO"check %s i2c address 0x%x\n", | ||
724 | mma8x5x_id2name(chip_id), client->addr); | ||
725 | strlcpy(info->type, "mma8x5x", I2C_NAME_SIZE); | ||
726 | return 0; | ||
727 | } | ||
728 | static int mma8x5x_open(struct inode *inode, struct file *file) | ||
729 | { | ||
730 | int err; | ||
731 | err = nonseekable_open(inode, file); | ||
732 | if (err) | ||
733 | return err; | ||
734 | file->private_data = p_mma8x5x_data; | ||
735 | return 0; | ||
736 | } | ||
737 | static ssize_t mma8x5x_read(struct file *file, | ||
738 | char __user *buf, | ||
739 | size_t size, loff_t *ppos) | ||
740 | { | ||
741 | struct mma8x5x_data *pdata = file->private_data; | ||
742 | int ret = 0; | ||
743 | if (!(file->f_flags & O_NONBLOCK)) { | ||
744 | ret = wait_event_interruptible(pdata->fifo_wq, | ||
745 | (atomic_read(&pdata->fifo_ready) != 0)); | ||
746 | if (ret) | ||
747 | return ret; | ||
748 | } | ||
749 | if (!atomic_read(&pdata->fifo_ready)) | ||
750 | return -ENODEV; | ||
751 | if (size < sizeof(struct mma8x5x_fifo)) { | ||
752 | printk(KERN_ERR"the buffer leght less than need\n"); | ||
753 | return -ENOMEM; | ||
754 | } | ||
755 | if (!copy_to_user(buf, &pdata->fifo, sizeof(struct mma8x5x_fifo))) { | ||
756 | atomic_set(&pdata->fifo_ready, 0); | ||
757 | return size; | ||
758 | } | ||
759 | return -ENOMEM ; | ||
760 | } | ||
761 | static unsigned int mma8x5x_poll(struct file *file, | ||
762 | struct poll_table_struct *wait) | ||
763 | { | ||
764 | struct mma8x5x_data *pdata = file->private_data; | ||
765 | poll_wait(file, &pdata->fifo_wq, wait); | ||
766 | if (atomic_read(&pdata->fifo_ready)) | ||
767 | return POLLIN | POLLRDNORM; | ||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static const struct file_operations mma8x5x_fops = { | ||
772 | .owner = THIS_MODULE, | ||
773 | .open = mma8x5x_open, | ||
774 | .read = mma8x5x_read, | ||
775 | .poll = mma8x5x_poll, | ||
776 | }; | ||
777 | |||
778 | static struct miscdevice mma8x5x_dev = { | ||
779 | .minor = MISC_DYNAMIC_MINOR, | ||
780 | .name = "mma8x5x", | ||
781 | .fops = &mma8x5x_fops, | ||
782 | }; | ||
783 | |||
784 | static int mma8x5x_probe(struct i2c_client *client, | ||
785 | const struct i2c_device_id *id) | ||
786 | { | ||
787 | int result, chip_id; | ||
788 | struct input_dev *idev; | ||
789 | struct mma8x5x_data *pdata; | ||
790 | struct i2c_adapter *adapter; | ||
791 | struct device_node *of_node = client->dev.of_node; | ||
792 | u32 pos = 0; | ||
793 | struct regulator *vdd, *vdd_io; | ||
794 | u32 irq_flag; | ||
795 | struct irq_data *irq_data; | ||
796 | |||
797 | vdd = devm_regulator_get(&client->dev, "vdd"); | ||
798 | if (!IS_ERR(vdd)) { | ||
799 | result = regulator_enable(vdd); | ||
800 | if (result) { | ||
801 | dev_err(&client->dev, "vdd set voltage error\n"); | ||
802 | return result; | ||
803 | } | ||
804 | } | ||
805 | |||
806 | vdd_io = devm_regulator_get(&client->dev, "vddio"); | ||
807 | if (!IS_ERR(vdd_io)) { | ||
808 | result = regulator_enable(vdd_io); | ||
809 | if (result) { | ||
810 | dev_err(&client->dev, "vddio set voltage error\n"); | ||
811 | return result; | ||
812 | } | ||
813 | } | ||
814 | adapter = to_i2c_adapter(client->dev.parent); | ||
815 | result = i2c_check_functionality(adapter, | ||
816 | I2C_FUNC_SMBUS_BYTE | | ||
817 | I2C_FUNC_SMBUS_BYTE_DATA); | ||
818 | if (!result) | ||
819 | goto err_out; | ||
820 | |||
821 | chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I); | ||
822 | |||
823 | if (!mma8x5x_check_id(chip_id)) { | ||
824 | dev_err(&client->dev, | ||
825 | "read chip ID 0x%x is not equal to 0x%x,0x%x,0x%x,0x%x,0x%x!\n", | ||
826 | chip_id, MMA8451_ID, MMA8452_ID, | ||
827 | MMA8453_ID, MMA8652_ID, MMA8653_ID); | ||
828 | result = -EINVAL; | ||
829 | goto err_out; | ||
830 | } | ||
831 | pdata = kzalloc(sizeof(struct mma8x5x_data), GFP_KERNEL); | ||
832 | if (!pdata) { | ||
833 | result = -ENOMEM; | ||
834 | dev_err(&client->dev, "alloc data memory error!\n"); | ||
835 | goto err_out; | ||
836 | } | ||
837 | |||
838 | /* Initialize the MMA8X5X chip */ | ||
839 | memset(pdata, 0, sizeof(struct mma8x5x_data)); | ||
840 | pdata->client = client; | ||
841 | pdata->chip_id = chip_id; | ||
842 | pdata->mode = MODE_2G; | ||
843 | pdata->fifo_wakeup = 0; | ||
844 | pdata->fifo_timeout = 0; | ||
845 | |||
846 | result = of_property_read_u32(of_node, "position", &pos); | ||
847 | if (result) | ||
848 | pos = 1; | ||
849 | pdata->position = (int)pos; | ||
850 | p_mma8x5x_data = pdata; | ||
851 | mutex_init(&pdata->data_lock); | ||
852 | i2c_set_clientdata(client, pdata); | ||
853 | |||
854 | mma8x5x_device_init(client); | ||
855 | |||
856 | idev = input_allocate_device(); | ||
857 | if (!idev) { | ||
858 | result = -ENOMEM; | ||
859 | dev_err(&client->dev, "alloc input device failed!\n"); | ||
860 | goto err_alloc_input_device; | ||
861 | } | ||
862 | idev->name = "FreescaleAccelerometer"; | ||
863 | idev->uniq = mma8x5x_id2name(pdata->chip_id); | ||
864 | idev->id.bustype = BUS_I2C; | ||
865 | idev->evbit[0] = BIT_MASK(EV_ABS); | ||
866 | input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0); | ||
867 | input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0); | ||
868 | input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0); | ||
869 | dev_set_drvdata(&idev->dev, pdata); | ||
870 | pdata->idev = idev; | ||
871 | result = input_register_device(pdata->idev); | ||
872 | if (result) { | ||
873 | dev_err(&client->dev, "register input device failed!\n"); | ||
874 | goto err_register_input_device; | ||
875 | } | ||
876 | pdata->delay = POLL_INTERVAL; | ||
877 | INIT_DELAYED_WORK(&pdata->work, mma8x5x_dev_poll); | ||
878 | result = sysfs_create_group(&idev->dev.kobj, &mma8x5x_attr_group); | ||
879 | if (result) { | ||
880 | dev_err(&client->dev, "create device file failed!\n"); | ||
881 | result = -EINVAL; | ||
882 | goto err_create_sysfs; | ||
883 | } | ||
884 | init_waitqueue_head(&pdata->fifo_wq); | ||
885 | |||
886 | if (client->irq) { | ||
887 | irq_data = irq_get_irq_data(client->irq); | ||
888 | irq_flag = irqd_get_trigger_type(irq_data); | ||
889 | irq_flag |= IRQF_ONESHOT; | ||
890 | result = request_threaded_irq(client->irq, NULL, | ||
891 | mma8x5x_irq_handler, | ||
892 | irq_flag, | ||
893 | client->dev.driver->name, | ||
894 | pdata); | ||
895 | if (result < 0) { | ||
896 | dev_err(&client->dev, | ||
897 | "failed to register MMA8x5x irq %d!\n", | ||
898 | client->irq); | ||
899 | goto err_register_irq; | ||
900 | } else { | ||
901 | result = misc_register(&mma8x5x_dev); | ||
902 | if (result) { | ||
903 | dev_err(&client->dev, | ||
904 | "register fifo device error\n"); | ||
905 | goto err_reigster_dev; | ||
906 | } | ||
907 | } | ||
908 | |||
909 | result = of_property_read_u32(of_node, | ||
910 | "interrupt-route", | ||
911 | &pdata->int_pin); | ||
912 | if (result) { | ||
913 | result = -EINVAL; | ||
914 | dev_err(&client->dev, | ||
915 | "Can't find interrupt-pin value\n"); | ||
916 | goto err_reigster_dev; | ||
917 | |||
918 | } | ||
919 | if (pdata->int_pin == 0 || pdata->int_pin > 2) { | ||
920 | result = -EINVAL; | ||
921 | dev_err(&client->dev, | ||
922 | "The interrupt-pin value is invalid\n"); | ||
923 | goto err_reigster_dev; | ||
924 | } | ||
925 | } | ||
926 | printk(KERN_INFO"mma8x5x device driver probe successfully\n"); | ||
927 | return 0; | ||
928 | err_reigster_dev: | ||
929 | free_irq(client->irq, pdata); | ||
930 | err_register_irq: | ||
931 | sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group); | ||
932 | err_create_sysfs: | ||
933 | input_unregister_device(pdata->idev); | ||
934 | err_register_input_device: | ||
935 | input_free_device(idev); | ||
936 | err_alloc_input_device: | ||
937 | kfree(pdata); | ||
938 | err_out: | ||
939 | return result; | ||
940 | } | ||
941 | |||
942 | static int mma8x5x_remove(struct i2c_client *client) | ||
943 | { | ||
944 | struct mma8x5x_data *pdata = i2c_get_clientdata(client); | ||
945 | struct input_dev *idev = pdata->idev; | ||
946 | mma8x5x_device_stop(client); | ||
947 | if (pdata) { | ||
948 | sysfs_remove_group(&idev->dev.kobj, &mma8x5x_attr_group); | ||
949 | input_unregister_device(pdata->idev); | ||
950 | input_free_device(pdata->idev); | ||
951 | kfree(pdata); | ||
952 | } | ||
953 | return 0; | ||
954 | } | ||
955 | |||
956 | #ifdef CONFIG_PM_SLEEP | ||
957 | static int mma8x5x_suspend(struct device *dev) | ||
958 | { | ||
959 | struct i2c_client *client = to_i2c_client(dev); | ||
960 | struct mma8x5x_data *pdata = i2c_get_clientdata(client); | ||
961 | if (pdata->fifo_timeout <= 0) { | ||
962 | if (pdata->active == MMA_ACTIVED) | ||
963 | mma8x5x_device_stop(client); | ||
964 | } else { | ||
965 | if (pdata->active == MMA_ACTIVED) { | ||
966 | if (pdata->fifo_wakeup) { | ||
967 | /*10s timeout , overwrite*/ | ||
968 | mma8x5x_fifo_setting(pdata, 10000, 0); | ||
969 | mma8x5x_fifo_interrupt(client, 1); | ||
970 | } else { | ||
971 | mma8x5x_fifo_interrupt(client, 0); | ||
972 | /*10s timeout , overwrite*/ | ||
973 | mma8x5x_fifo_setting(pdata, 10000, 1); | ||
974 | } | ||
975 | } | ||
976 | } | ||
977 | return 0; | ||
978 | } | ||
979 | |||
980 | static int mma8x5x_resume(struct device *dev) | ||
981 | { | ||
982 | int val = 0; | ||
983 | struct i2c_client *client = to_i2c_client(dev); | ||
984 | struct mma8x5x_data *pdata = i2c_get_clientdata(client); | ||
985 | if (pdata->fifo_timeout <= 0) { | ||
986 | if (pdata->active == MMA_ACTIVED) { | ||
987 | val = i2c_smbus_read_byte_data(client, | ||
988 | MMA8X5X_CTRL_REG1); | ||
989 | i2c_smbus_write_byte_data(client, | ||
990 | MMA8X5X_CTRL_REG1, val | 0x01); | ||
991 | } | ||
992 | } else { | ||
993 | if (pdata->active == MMA_ACTIVED) { | ||
994 | mma8x5x_fifo_interrupt(client, 1); | ||
995 | /*Awake from suspend*/ | ||
996 | pdata->awaken = 1; | ||
997 | } | ||
998 | } | ||
999 | return 0; | ||
1000 | |||
1001 | } | ||
1002 | #endif | ||
1003 | |||
1004 | static const struct i2c_device_id mma8x5x_id[] = { | ||
1005 | {"mma8451", 0}, | ||
1006 | {"mma8452", 0}, | ||
1007 | {"mma8453", 0}, | ||
1008 | {"mma8652", 0}, | ||
1009 | {"mma8653", 0}, | ||
1010 | {} | ||
1011 | }; | ||
1012 | |||
1013 | MODULE_DEVICE_TABLE(i2c, mma8x5x_id); | ||
1014 | |||
1015 | static SIMPLE_DEV_PM_OPS(mma8x5x_pm_ops, mma8x5x_suspend, mma8x5x_resume); | ||
1016 | static struct i2c_driver mma8x5x_driver = { | ||
1017 | .class = I2C_CLASS_HWMON, | ||
1018 | .driver = { | ||
1019 | .name = "mma8x5x", | ||
1020 | .owner = THIS_MODULE, | ||
1021 | .pm = &mma8x5x_pm_ops, | ||
1022 | }, | ||
1023 | .probe = mma8x5x_probe, | ||
1024 | .remove = mma8x5x_remove, | ||
1025 | .id_table = mma8x5x_id, | ||
1026 | .detect = mma8x5x_detect, | ||
1027 | .address_list = normal_i2c, | ||
1028 | }; | ||
1029 | |||
1030 | |||
1031 | module_i2c_driver(mma8x5x_driver); | ||
1032 | |||
1033 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
1034 | MODULE_DESCRIPTION("MMA8X5X 3-Axis Orientation/Motion Detection Sensor driver"); | ||
1035 | MODULE_LICENSE("GPL"); | ||
1036 | |||