rk fb: extend output mutex lock in win config
[firefly-linux-kernel-4.4.55.git] / drivers / input / magnetometer / mmc328x.c
1 /*
2  * Copyright (C) 2010 MEMSIC, Inc.
3  *
4  * Initial Code:
5  *      Robbie Cao
6  *      Dale Hou
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
21  *
22  */
23
24 #include <linux/kernel.h>
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/slab.h>
28 #include <linux/jiffies.h>
29 #include <linux/i2c.h>
30 #include <linux/i2c-dev.h>
31 #include <linux/miscdevice.h>
32 #include <linux/mutex.h>
33 #include <linux/mm.h>
34 #include <linux/device.h>
35 #include <linux/fs.h>
36 #include <linux/delay.h>
37 #include <linux/sysctl.h>
38 #include <asm/uaccess.h>
39
40 #include "mmc328x.h"
41
42 #define DEBUG                   0
43 #define MAX_FAILURE_COUNT       3
44 #define READMD                  1
45
46 #define MMC328X_DELAY_TM        10      /* ms */
47 #define MMC328X_DELAY_RM        10      /* ms */
48 #define MMC328X_DELAY_STDN      1       /* ms */
49
50 #define MMC328X_RETRY_COUNT     3
51 #define MMC328X_RESET_INTV      10
52
53 #define MMC328X_DEV_NAME        "mmc328x"
54
55 static u32 read_idx = 0;
56
57 static struct i2c_client *this_client;
58
59 static int mmc328x_i2c_rx_data(char *buf, int len)
60 {
61         uint8_t i;
62         struct i2c_msg msgs[] = {
63                 {
64                         .addr   = this_client->addr,
65                         .flags  = 0,
66                         .len    = 1,
67                         .buf    = buf,
68                         .scl_rate = 200*1000,
69                         .udelay = 100,
70                 },
71                 {
72                         .addr   = this_client->addr,
73                         .flags  = I2C_M_RD,
74                         .len    = len,
75                         .buf    = buf,
76                         .scl_rate = 200*1000,
77                         .udelay = 100,
78                 }
79         };
80
81         for (i = 0; i < MMC328X_RETRY_COUNT; i++) {
82                 if (i2c_transfer(this_client->adapter, msgs, 2) >= 0) {
83                         break;
84                 }
85                 mdelay(10);
86         }
87
88         if (i >= MMC328X_RETRY_COUNT) {
89                 pr_err("%s: retry over %d\n", __FUNCTION__, MMC328X_RETRY_COUNT);
90                 return -EIO;
91         }
92
93         return 0;
94 }
95
96 static int mmc328x_i2c_tx_data(char *buf, int len)
97 {
98         uint8_t i;
99         struct i2c_msg msg[] = {
100                 {
101                         .addr   = this_client->addr,
102                         .flags  = 0,
103                         .len    = len,
104                         .buf    = buf,
105                 }
106         };
107         
108         for (i = 0; i < MMC328X_RETRY_COUNT; i++) {
109                 if (i2c_transfer(this_client->adapter, msg, 1) >= 0) {
110                         break;
111                 }
112                 mdelay(10);
113         }
114
115         if (i >= MMC328X_RETRY_COUNT) {
116                 pr_err("%s: retry over %d\n", __FUNCTION__, MMC328X_RETRY_COUNT);
117                 return -EIO;
118         }
119         return 0;
120 }
121
122 static int mmc328x_open(struct inode *inode, struct file *file)
123 {
124         return nonseekable_open(inode, file);
125 }
126
127 static int mmc328x_release(struct inode *inode, struct file *file)
128 {
129         return 0;
130 }
131
132 static int mmc328x_ioctl(struct inode *inode, struct file *file, 
133         unsigned int cmd, unsigned long arg)
134 {
135         void __user *pa = (void __user *)arg;
136         unsigned char data[16] = {0};
137         int vec[3] = {0};
138         int MD_times = 0;
139
140         switch (cmd) {
141         case MMC328X_IOC_TM:
142                 data[0] = MMC328X_REG_CTRL;
143                 data[1] = MMC328X_CTRL_TM;
144                 if (mmc328x_i2c_tx_data(data, 2) < 0) {
145                         return -EFAULT;
146                 }
147                 /* wait TM done for coming data read */
148                 msleep(MMC328X_DELAY_TM);
149                 break;
150         case MMC328X_IOC_RM:
151                 data[0] = MMC328X_REG_CTRL;
152                 data[1] = MMC328X_CTRL_RM;
153                 if (mmc328x_i2c_tx_data(data, 2) < 0) {
154                         return -EFAULT;
155                 }
156                 /* wait external capacitor charging done for next SET/RESET */
157                 msleep(MMC328X_DELAY_RM);
158                 break;
159         case MMC328X_IOC_READ:
160                 data[0] = MMC328X_REG_DATA;
161                 if (mmc328x_i2c_rx_data(data, 6) < 0) {
162                         return -EFAULT;
163                 }
164                 vec[0] = data[1] << 8 | data[0];
165                 vec[1] = data[3] << 8 | data[2];
166                 vec[2] = data[5] << 8 | data[4];
167         #if DEBUG
168                 printk("[X - %04x] [Y - %04x] [Z - %04x]\n", 
169                         vec[0], vec[1], vec[2]);
170         #endif
171                 if (copy_to_user(pa, vec, sizeof(vec))) {
172                         return -EFAULT;
173                 }
174                 break;
175         case MMC328X_IOC_READXYZ:
176                 /* do RM every MMC328X_RESET_INTV times read */
177                 if (!(read_idx % MMC328X_RESET_INTV)) {
178                         /* RM */
179                         data[0] = MMC328X_REG_CTRL;
180                         data[1] = MMC328X_CTRL_RM;
181                         /* not check return value here, assume it always OK */
182                         mmc328x_i2c_tx_data(data, 2);
183                         /* wait external capacitor charging done for next RM */
184                         msleep(MMC328X_DELAY_RM);
185                 }
186                 /* send TM cmd before read */
187                 data[0] = MMC328X_REG_CTRL;
188                 data[1] = MMC328X_CTRL_TM;
189                 /* not check return value here, assume it always OK */
190                 mmc328x_i2c_tx_data(data, 2);
191                 /* wait TM done for coming data read */
192                 msleep(MMC328X_DELAY_TM);
193 #if READMD
194                 /* Read MD */
195                 data[0] = MMC328X_REG_DS;
196                 if (mmc328x_i2c_rx_data(data, 1) < 0) {
197                         return -EFAULT;
198                 }
199                 while (!(data[0] & 0x01)) {
200                         msleep(1);
201                         /* Read MD again*/
202                         data[0] = MMC328X_REG_DS;
203                         if (mmc328x_i2c_rx_data(data, 1) < 0) {
204                                 return -EFAULT;
205                         }
206                         if (data[0] & 0x01) break;
207                         MD_times++;
208                         if (MD_times > 2) {
209                 #if DEBUG
210                                 printk("TM not work!!");
211                 #endif
212                                 return -EFAULT;
213                         }
214                 }
215 #endif          
216                 /* read xyz raw data */
217                 read_idx++;
218                 data[0] = MMC328X_REG_DATA;
219                 if (mmc328x_i2c_rx_data(data, 6) < 0) {
220                         return -EFAULT;
221                 }
222                 vec[0] = data[1] << 8 | data[0];
223                 vec[1] = data[3] << 8 | data[2];
224                 vec[2] = data[5] << 8 | data[4];
225         #if DEBUG
226                 printk("[X - %04x] [Y - %04x] [Z - %04x]\n", 
227                         vec[0], vec[1], vec[2]);
228         #endif
229                 if (copy_to_user(pa, vec, sizeof(vec))) {
230                         return -EFAULT;
231                 }
232
233                 break;
234         default:
235                 break;
236         }
237
238         return 0;
239 }
240
241 static ssize_t mmc328x_show(struct device *dev, struct device_attribute *attr, char *buf)
242 {
243         ssize_t ret = 0;
244
245         sprintf(buf, "MMC328X");
246         ret = strlen(buf) + 1;
247
248         return ret;
249 }
250
251 static DEVICE_ATTR(mmc328x, S_IRUGO, mmc328x_show, NULL);
252
253 static struct file_operations mmc328x_fops = {
254         .owner          = THIS_MODULE,
255         .open           = mmc328x_open,
256         .release        = mmc328x_release,
257         .ioctl          = mmc328x_ioctl,
258 };
259
260 static struct miscdevice mmc328x_device = {
261         .minor = MISC_DYNAMIC_MINOR,
262         .name = MMC328X_DEV_NAME,
263         .fops = &mmc328x_fops,
264 };
265
266 static int mmc328x_probe(struct i2c_client *client, const struct i2c_device_id *id)
267 {
268         unsigned char data[16] = {0};
269         int res = 0;
270
271         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
272                 pr_err("%s: functionality check failed\n", __FUNCTION__);
273                 res = -ENODEV;
274                 goto out;
275         }
276         this_client = client;
277
278         res = misc_register(&mmc328x_device);
279         if (res) {
280                 pr_err("%s: mmc328x_device register failed\n", __FUNCTION__);
281                 goto out;
282         }
283         res = device_create_file(&client->dev, &dev_attr_mmc328x);
284         if (res) {
285                 pr_err("%s: device_create_file failed\n", __FUNCTION__);
286                 goto out_deregister;
287         }
288
289         /* send ST cmd to mag sensor first of all */
290         data[0] = MMC328X_REG_CTRL;
291         data[1] = MMC328X_CTRL_RM;
292         if (mmc328x_i2c_tx_data(data, 2) < 0) {
293                 /* assume RM always success */
294         }
295         /* wait external capacitor charging done for next RM */
296         msleep(MMC328X_DELAY_RM);
297
298         return 0;
299
300 out_deregister:
301         misc_deregister(&mmc328x_device);
302 out:
303         return res;
304 }
305
306 static int mmc328x_remove(struct i2c_client *client)
307 {
308         device_remove_file(&client->dev, &dev_attr_mmc328x);
309         misc_deregister(&mmc328x_device);
310
311         return 0;
312 }
313
314 static const struct i2c_device_id mmc328x_id[] = {
315         { MMC328X_I2C_NAME, 0 },
316         { }
317 };
318
319 static struct i2c_driver mmc328x_driver = {
320         .probe          = mmc328x_probe,
321         .remove         = mmc328x_remove,
322         .id_table       = mmc328x_id,
323         .driver         = {
324                 .owner  = THIS_MODULE,
325                 .name   = MMC328X_I2C_NAME,
326         },
327 };
328
329
330 static int __init mmc328x_init(void)
331 {
332         pr_info("mmc328x driver: init\n");
333         return i2c_add_driver(&mmc328x_driver);
334 }
335
336 static void __exit mmc328x_exit(void)
337 {
338         pr_info("mmc328x driver: exit\n");
339         i2c_del_driver(&mmc328x_driver);
340 }
341
342 module_init(mmc328x_init);
343 module_exit(mmc328x_exit);
344
345 MODULE_AUTHOR("Dale Hou<byhou@memsic.com>");
346 MODULE_DESCRIPTION("MEMSIC MMC328X Magnetic Sensor Driver");
347 MODULE_LICENSE("GPL");
348