aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/inv_mpu/mpuirq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/inv_mpu/mpuirq.c')
-rw-r--r--drivers/misc/inv_mpu/mpuirq.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c
new file mode 100644
index 00000000000..d8b721e4346
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.c
@@ -0,0 +1,254 @@
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
40#include <linux/mpu.h>
41#include "mpuirq.h"
42#include "mldl_cfg.h"
43
44#define MPUIRQ_NAME "mpuirq"
45
46/* function which gets accel data and sends it to MPU */
47
48DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait);
49
50struct mpuirq_dev_data {
51 struct i2c_client *mpu_client;
52 struct miscdevice *dev;
53 int irq;
54 int pid;
55 int accel_divider;
56 int data_ready;
57 int timeout;
58};
59
60static struct mpuirq_dev_data mpuirq_dev_data;
61static struct mpuirq_data mpuirq_data;
62static char *interface = MPUIRQ_NAME;
63
64static int mpuirq_open(struct inode *inode, struct file *file)
65{
66 dev_dbg(mpuirq_dev_data.dev->this_device,
67 "%s current->pid %d\n", __func__, current->pid);
68 mpuirq_dev_data.pid = current->pid;
69 file->private_data = &mpuirq_dev_data;
70 return 0;
71}
72
73/* close function - called when the "file" /dev/mpuirq is closed in userspace */
74static int mpuirq_release(struct inode *inode, struct file *file)
75{
76 dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n");
77 return 0;
78}
79
80/* read function called when from /dev/mpuirq is read */
81static ssize_t mpuirq_read(struct file *file,
82 char *buf, size_t count, loff_t *ppos)
83{
84 int len, err;
85 struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
86
87 if (!mpuirq_dev_data.data_ready &&
88 mpuirq_dev_data.timeout && (!(file->f_flags & O_NONBLOCK))) {
89 wait_event_interruptible_timeout(mpuirq_wait,
90 mpuirq_dev_data.data_ready,
91 mpuirq_dev_data.timeout);
92 }
93
94 if (mpuirq_dev_data.data_ready && NULL != buf
95 && count >= sizeof(mpuirq_data)) {
96 err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data));
97 mpuirq_data.data_type = 0;
98 } else {
99 return 0;
100 }
101 if (err != 0) {
102 dev_err(p_mpuirq_dev_data->dev->this_device,
103 "Copy to user returned %d\n", err);
104 return -EFAULT;
105 }
106 mpuirq_dev_data.data_ready = 0;
107 len = sizeof(mpuirq_data);
108 return len;
109}
110
111unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll)
112{
113 int mask = 0;
114
115 poll_wait(file, &mpuirq_wait, poll);
116 if (mpuirq_dev_data.data_ready)
117 mask |= POLLIN | POLLRDNORM;
118 return mask;
119}
120
121/* ioctl - I/O control */
122static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
123{
124 int retval = 0;
125 int data;
126
127 switch (cmd) {
128 case MPUIRQ_SET_TIMEOUT:
129 mpuirq_dev_data.timeout = arg;
130 break;
131
132 case MPUIRQ_GET_INTERRUPT_CNT:
133 data = mpuirq_data.interruptcount - 1;
134 if (mpuirq_data.interruptcount > 1)
135 mpuirq_data.interruptcount = 1;
136
137 if (copy_to_user((int *)arg, &data, sizeof(int)))
138 return -EFAULT;
139 break;
140 case MPUIRQ_GET_IRQ_TIME:
141 if (copy_to_user((int *)arg, &mpuirq_data.irqtime,
142 sizeof(mpuirq_data.irqtime)))
143 return -EFAULT;
144 mpuirq_data.irqtime = 0;
145 break;
146 case MPUIRQ_SET_FREQUENCY_DIVIDER:
147 mpuirq_dev_data.accel_divider = arg;
148 break;
149 default:
150 retval = -EINVAL;
151 }
152 return retval;
153}
154
155static irqreturn_t mpuirq_handler(int irq, void *dev_id)
156{
157 static int mycount;
158 struct timeval irqtime;
159 mycount++;
160
161 mpuirq_data.interruptcount++;
162
163 /* wake up (unblock) for reading data from userspace */
164 /* and ignore first interrupt generated in module init */
165 mpuirq_dev_data.data_ready = 1;
166
167 do_gettimeofday(&irqtime);
168 mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32);
169 mpuirq_data.irqtime += irqtime.tv_usec;
170 mpuirq_data.data_type = MPUIRQ_DATA_TYPE_MPU_IRQ;
171 mpuirq_data.data = 0;
172
173 wake_up_interruptible(&mpuirq_wait);
174
175 return IRQ_HANDLED;
176
177}
178
179/* define which file operations are supported */
180const struct file_operations mpuirq_fops = {
181 .owner = THIS_MODULE,
182 .read = mpuirq_read,
183 .poll = mpuirq_poll,
184
185 .unlocked_ioctl = mpuirq_ioctl,
186 .open = mpuirq_open,
187 .release = mpuirq_release,
188};
189
190static struct miscdevice mpuirq_device = {
191 .minor = MISC_DYNAMIC_MINOR,
192 .name = MPUIRQ_NAME,
193 .fops = &mpuirq_fops,
194};
195
196int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg,
197 unsigned long irq_flags)
198{
199
200 int res;
201
202 mpuirq_dev_data.mpu_client = mpu_client;
203
204 dev_info(&mpu_client->adapter->dev,
205 "Module Param interface = %s\n", interface);
206
207 mpuirq_dev_data.irq = mpu_client->irq;
208 mpuirq_dev_data.pid = 0;
209 mpuirq_dev_data.accel_divider = -1;
210 mpuirq_dev_data.data_ready = 0;
211 mpuirq_dev_data.timeout = 0;
212 mpuirq_dev_data.dev = &mpuirq_device;
213
214 if (mpuirq_dev_data.irq) {
215 irq_flags |= IRQF_SHARED;
216 res =
217 request_irq(mpuirq_dev_data.irq, mpuirq_handler, irq_flags,
218 interface, &mpuirq_dev_data.irq);
219 if (res) {
220 dev_err(&mpu_client->adapter->dev,
221 "myirqtest: cannot register IRQ %d\n",
222 mpuirq_dev_data.irq);
223 } else {
224 res = misc_register(&mpuirq_device);
225 if (res < 0) {
226 dev_err(&mpu_client->adapter->dev,
227 "misc_register returned %d\n", res);
228 free_irq(mpuirq_dev_data.irq,
229 &mpuirq_dev_data.irq);
230 }
231 }
232
233 } else {
234 res = 0;
235 }
236
237 return res;
238}
239EXPORT_SYMBOL(mpuirq_init);
240
241void mpuirq_exit(void)
242{
243 if (mpuirq_dev_data.irq > 0)
244 free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq);
245
246 dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME);
247 misc_deregister(&mpuirq_device);
248
249 return;
250}
251EXPORT_SYMBOL(mpuirq_exit);
252
253module_param(interface, charp, S_IRUGO | S_IWUSR);
254MODULE_PARM_DESC(interface, "The Interface name");