Merge remote-tracking branch 'stable/linux-3.0.y' into develop-3.0-jb
[firefly-linux-kernel-4.4.55.git] / drivers / misc / inv_mpu / slaveirq.c
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 #include <linux/interrupt.h>
20 #include <linux/module.h>
21 #include <linux/moduleparam.h>
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/stat.h>
25 #include <linux/irq.h>
26 #include <linux/signal.h>
27 #include <linux/miscdevice.h>
28 #include <linux/i2c.h>
29 #include <linux/i2c-dev.h>
30 #include <linux/poll.h>
31
32 #include <linux/errno.h>
33 #include <linux/fs.h>
34 #include <linux/mm.h>
35 #include <linux/sched.h>
36 #include <linux/wait.h>
37 #include <linux/uaccess.h>
38 #include <linux/io.h>
39 #include <linux/wait.h>
40 #include <linux/slab.h>
41
42 #include <linux/mpu.h>
43 #include "slaveirq.h"
44 #include "mldl_cfg.h"
45
46 /* function which gets slave data and sends it to SLAVE */
47
48 struct slaveirq_dev_data {
49         struct miscdevice dev;
50         struct i2c_client *slave_client;
51         struct mpuirq_data data;
52         wait_queue_head_t slaveirq_wait;
53         int irq;
54         int pid;
55         int data_ready;
56         int timeout;
57 };
58
59 /* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
60  * drivers: misc: pass miscdevice pointer via file private data
61  */
62 static int slaveirq_open(struct inode *inode, struct file *file)
63 {
64         /* Device node is availabe in the file->private_data, this is
65          * exactly what we want so we leave it there */
66         struct slaveirq_dev_data *data =
67             container_of(file->private_data, struct slaveirq_dev_data, dev);
68
69         dev_dbg(data->dev.this_device,
70                 "%s current->pid %d\n", __func__, current->pid);
71         data->pid = current->pid;
72         return 0;
73 }
74
75 static int slaveirq_release(struct inode *inode, struct file *file)
76 {
77         struct slaveirq_dev_data *data =
78             container_of(file->private_data, struct slaveirq_dev_data, dev);
79         dev_dbg(data->dev.this_device, "slaveirq_release\n");
80         return 0;
81 }
82
83 /* read function called when from /dev/slaveirq is read */
84 static ssize_t slaveirq_read(struct file *file,
85                              char *buf, size_t count, loff_t *ppos)
86 {
87         int len, err;
88         struct slaveirq_dev_data *data =
89             container_of(file->private_data, struct slaveirq_dev_data, dev);
90
91         if (!data->data_ready && data->timeout &&
92             !(file->f_flags & O_NONBLOCK)) {
93                 wait_event_interruptible_timeout(data->slaveirq_wait,
94                                                  data->data_ready,
95                                                  data->timeout);
96         }
97
98         if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
99                 err = copy_to_user(buf, &data->data, sizeof(data->data));
100                 data->data.data_type = 0;
101         } else {
102                 return 0;
103         }
104         if (err != 0) {
105                 dev_err(data->dev.this_device,
106                         "Copy to user returned %d\n", err);
107                 return -EFAULT;
108         }
109         data->data_ready = 0;
110         len = sizeof(data->data);
111         return len;
112 }
113
114 static unsigned int slaveirq_poll(struct file *file,
115                                   struct poll_table_struct *poll)
116 {
117         int mask = 0;
118         struct slaveirq_dev_data *data =
119             container_of(file->private_data, struct slaveirq_dev_data, dev);
120
121         poll_wait(file, &data->slaveirq_wait, poll);
122         if (data->data_ready)
123                 mask |= POLLIN | POLLRDNORM;
124         return mask;
125 }
126
127 /* ioctl - I/O control */
128 static long slaveirq_ioctl(struct file *file,
129                            unsigned int cmd, unsigned long arg)
130 {
131         int retval = 0;
132         int tmp;
133         struct slaveirq_dev_data *data =
134             container_of(file->private_data, struct slaveirq_dev_data, dev);
135
136         switch (cmd) {
137         case SLAVEIRQ_SET_TIMEOUT:
138                 data->timeout = arg;
139                 break;
140
141         case SLAVEIRQ_GET_INTERRUPT_CNT:
142                 tmp = data->data.interruptcount - 1;
143                 if (data->data.interruptcount > 1)
144                         data->data.interruptcount = 1;
145
146                 if (copy_to_user((int *)arg, &tmp, sizeof(int)))
147                         return -EFAULT;
148                 break;
149         case SLAVEIRQ_GET_IRQ_TIME:
150                 if (copy_to_user((int *)arg, &data->data.irqtime,
151                                  sizeof(data->data.irqtime)))
152                         return -EFAULT;
153                 data->data.irqtime = 0;
154                 break;
155         default:
156                 retval = -EINVAL;
157         }
158         return retval;
159 }
160
161 static irqreturn_t slaveirq_handler(int irq, void *dev_id)
162 {
163         struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id;
164         static int mycount;
165         struct timeval irqtime;
166         mycount++;
167
168         data->data.interruptcount++;
169
170         /* wake up (unblock) for reading data from userspace */
171         data->data_ready = 1;
172
173         do_gettimeofday(&irqtime);
174         data->data.irqtime = (((long long)irqtime.tv_sec) << 32);
175         data->data.irqtime += irqtime.tv_usec;
176         data->data.data_type |= 1;
177
178         wake_up_interruptible(&data->slaveirq_wait);
179
180         return IRQ_HANDLED;
181
182 }
183
184 /* define which file operations are supported */
185 static const struct file_operations slaveirq_fops = {
186         .owner = THIS_MODULE,
187         .read = slaveirq_read,
188         .poll = slaveirq_poll,
189
190 #if HAVE_COMPAT_IOCTL
191         .compat_ioctl = slaveirq_ioctl,
192 #endif
193 #if HAVE_UNLOCKED_IOCTL
194         .unlocked_ioctl = slaveirq_ioctl,
195 #endif
196         .open = slaveirq_open,
197         .release = slaveirq_release,
198 };
199
200 int slaveirq_init(struct i2c_adapter *slave_adapter,
201                   struct ext_slave_platform_data *pdata, char *name)
202 {
203
204         int res;
205         struct slaveirq_dev_data *data;
206
207         if (!pdata->irq)
208                 return -EINVAL;
209
210         pdata->irq_data = kzalloc(sizeof(*data), GFP_KERNEL);
211         data = (struct slaveirq_dev_data *)pdata->irq_data;
212         if (!data)
213                 return -ENOMEM;
214
215         data->dev.minor = MISC_DYNAMIC_MINOR;
216         data->dev.name = name;
217         data->dev.fops = &slaveirq_fops;
218         data->irq = pdata->irq;
219         data->pid = 0;
220         data->data_ready = 0;
221         data->timeout = 0;
222
223         init_waitqueue_head(&data->slaveirq_wait);
224
225         res = request_irq(data->irq, slaveirq_handler,
226                         IRQF_TRIGGER_RISING | IRQF_SHARED,
227                           data->dev.name, data);
228
229         if (res) {
230                 dev_err(&slave_adapter->dev,
231                         "myirqtest: cannot register IRQ %d\n", data->irq);
232                 goto out_request_irq;
233         }
234
235         res = misc_register(&data->dev);
236         if (res < 0) {
237                 dev_err(&slave_adapter->dev,
238                         "misc_register returned %d\n", res);
239                 goto out_misc_register;
240         }
241
242         return res;
243
244 out_misc_register:
245         free_irq(data->irq, data);
246 out_request_irq:
247         kfree(pdata->irq_data);
248         pdata->irq_data = NULL;
249
250         return res;
251 }
252
253 void slaveirq_exit(struct ext_slave_platform_data *pdata)
254 {
255         struct slaveirq_dev_data *data = pdata->irq_data;
256
257         if (!pdata->irq_data || data->irq <= 0)
258                 return;
259
260         dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name);
261
262         free_irq(data->irq, data);
263         misc_deregister(&data->dev);
264         kfree(pdata->irq_data);
265         pdata->irq_data = NULL;
266 }