diff options
Diffstat (limited to 'drivers/misc/inv_mpu/slaveirq.c')
-rw-r--r-- | drivers/misc/inv_mpu/slaveirq.c | 268 |
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 | |||
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 | EXPORT_SYMBOL(slaveirq_init); | ||
253 | |||
254 | void 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 | } | ||
268 | EXPORT_SYMBOL(slaveirq_exit); | ||