aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/slaveirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/inv_mpu/slaveirq.c')
-rw-r--r--drivers/misc/inv_mpu/slaveirq.c268
1 files changed, 268 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c
new file mode 100644
index 00000000000..22cfa4e458e
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.c
@@ -0,0 +1,268 @@
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
48struct 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 */
62static 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
75static 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 */
84static 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
114static 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 */
128static 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
161static 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 */
185static 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
200int 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
244out_misc_register:
245 free_irq(data->irq, data);
246out_request_irq:
247 kfree(pdata->irq_data);
248 pdata->irq_data = NULL;
249
250 return res;
251}
252EXPORT_SYMBOL(slaveirq_init);
253
254void slaveirq_exit(struct ext_slave_platform_data *pdata)
255{
256 struct slaveirq_dev_data *data = pdata->irq_data;
257
258 if (!pdata->irq_data || data->irq <= 0)
259 return;
260
261 dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name);
262
263 free_irq(data->irq, data);
264 misc_deregister(&data->dev);
265 kfree(pdata->irq_data);
266 pdata->irq_data = NULL;
267}
268EXPORT_SYMBOL(slaveirq_exit);