]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/tahvo-user.c
Merge branch 'omap-fixes'
[linux-2.6-omap-h63xx.git] / drivers / cbus / tahvo-user.c
1 /**
2  * drivers/cbus/tahvo-user.c
3  *
4  * Tahvo user space interface functions
5  *
6  * Copyright (C) 2004, 2005 Nokia Corporation
7  *
8  * Written by Mikko Ylinen <mikko.k.ylinen@nokia.com>
9  *
10  * This file is subject to the terms and conditions of the GNU General
11  * Public License. See the file "COPYING" in the main directory of this
12  * archive for more details.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include <linux/types.h>
25 #include <linux/kernel.h>
26 #include <linux/interrupt.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/fs.h>
30 #include <linux/miscdevice.h>
31 #include <linux/poll.h>
32 #include <linux/list.h>
33 #include <linux/spinlock.h>
34 #include <linux/mutex.h>
35
36 #include <asm/uaccess.h>
37
38 #include "tahvo.h"
39
40 #include "user_retu_tahvo.h"
41
42 /* Maximum size of IRQ node buffer/pool */
43 #define TAHVO_MAX_IRQ_BUF_LEN   16
44
45 #define PFX                     "tahvo-user: "
46
47 /* Bitmap for marking the interrupt sources as having the handlers */
48 static u32 tahvo_irq_bits;
49
50 /* For allowing only one user process to subscribe to the tahvo interrupts */
51 static struct file *tahvo_irq_subscr = NULL;
52
53 /* For poll and IRQ passing */
54 struct tahvo_irq {
55         u32 id;
56         struct list_head node;
57 };
58
59 static spinlock_t tahvo_irqs_lock;
60 static struct tahvo_irq *tahvo_irq_block;
61 static LIST_HEAD(tahvo_irqs);
62 static LIST_HEAD(tahvo_irqs_reserve);
63
64 /* Wait queue - used when user wants to read the device */
65 DECLARE_WAIT_QUEUE_HEAD(tahvo_user_waitqueue);
66
67 /* Semaphore to protect irq subscription sequence */
68 static struct mutex tahvo_mutex;
69
70 /* This array specifies TAHVO register types (read/write/toggle) */
71 static const u8 tahvo_access_bits[] = {
72         1,
73         4,
74         1,
75         3,
76         3,
77         3,
78         3,
79         3,
80         3,
81         3,
82         3,
83         3,
84         3,
85         1
86 };
87
88 /*
89  * The handler for all TAHVO interrupts.
90  *
91  * arg is the interrupt source in TAHVO.
92  */
93 static void tahvo_user_irq_handler(unsigned long arg)
94 {
95         struct tahvo_irq *irq;
96
97         /* user has to re-enable the interrupt once ready
98          * for receiving them again */
99         tahvo_disable_irq(arg);
100         tahvo_ack_irq(arg);
101
102         spin_lock(&tahvo_irqs_lock);
103         if (list_empty(&tahvo_irqs_reserve)) {
104                 spin_unlock(&tahvo_irqs_lock);
105                 return;
106         }
107         irq = list_entry((&tahvo_irqs_reserve)->next, struct tahvo_irq, node);
108         irq->id = arg;
109         list_move_tail(&irq->node, &tahvo_irqs);
110         spin_unlock(&tahvo_irqs_lock);
111
112         /* wake up waiting thread */
113         wake_up(&tahvo_user_waitqueue);
114 }
115
116 /*
117  * This routine sets up the interrupt handler and marks an interrupt source
118  * in TAHVO as a candidate for signal delivery to the user process.
119  */
120 static int tahvo_user_subscribe_to_irq(int id, struct file *filp)
121 {
122         int ret;
123
124         mutex_lock(&tahvo_mutex);
125         if ((tahvo_irq_subscr != NULL) && (tahvo_irq_subscr != filp)) {
126                 mutex_unlock(&tahvo_mutex);
127                 return -EBUSY;
128         }
129         /* Store the file pointer of the first user process registering IRQs */
130         tahvo_irq_subscr = filp;
131         mutex_unlock(&tahvo_mutex);
132
133         if (tahvo_irq_bits & (1 << id))
134                 return 0;
135
136         ret = tahvo_request_irq(id, tahvo_user_irq_handler, id, "");
137         if (ret < 0)
138                 return ret;
139
140         /* Mark that this interrupt has a handler */
141         tahvo_irq_bits |= 1 << id;
142
143         return 0;
144 }
145
146 /*
147  * Unregister all TAHVO interrupt handlers
148  */
149 static void tahvo_unreg_irq_handlers(void)
150 {
151         int id;
152
153         if (!tahvo_irq_bits)
154                 return;
155
156         for (id = 0; id < MAX_TAHVO_IRQ_HANDLERS; id++)
157                 if (tahvo_irq_bits & (1 << id))
158                         tahvo_free_irq(id);
159
160         tahvo_irq_bits = 0;
161 }
162
163 /*
164  * Write to TAHVO register.
165  * Returns 0 upon success, a negative error value otherwise.
166  */
167 static int tahvo_user_write_with_mask(u32 field, u16 value)
168 {
169         u32 mask;
170         u32 reg;
171         u_short tmp;
172         unsigned long flags;
173
174         mask = MASK(field);
175         reg = REG(field);
176
177         /* Detect bad mask and reg */
178         if (mask == 0 || reg > TAHVO_REG_MAX ||
179             tahvo_access_bits[reg] == READ_ONLY) {
180                 printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
181                        reg, mask);
182                 return -EINVAL;
183         }
184
185         /* Justify value according to mask */
186         while (!(mask & 1)) {
187                 value = value << 1;
188                 mask = mask >> 1;
189         }
190
191         spin_lock_irqsave(&tahvo_lock, flags);
192         if (tahvo_access_bits[reg] == TOGGLE) {
193                 /* No need to detect previous content of register */
194                 tmp = 0;
195         } else {
196                 /* Read current value of register */
197                 tmp = tahvo_read_reg(reg);
198         }
199         /* Generate a new value */
200         tmp = (tmp & ~MASK(field)) | (value & MASK(field));
201         /* Write data to TAHVO */
202         tahvo_write_reg(reg, tmp);
203         spin_unlock_irqrestore(&tahvo_lock, flags);
204
205         return 0;
206 }
207
208 /*
209  * Read TAHVO register.
210  */
211 static u32 tahvo_user_read_with_mask(u32 field)
212 {
213         u_short value;
214         u32 mask, reg;
215
216         mask = MASK(field);
217         reg = REG(field);
218
219         /* Detect bad mask and reg */
220         if (mask == 0 || reg > TAHVO_REG_MAX) {
221                 printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n",
222                        reg, mask);
223                 return -EINVAL;
224         }
225
226         /* Read the register */
227         value = tahvo_read_reg(reg) & mask;
228
229         /* Right justify value */
230         while (!(mask & 1)) {
231                 value = value >> 1;
232                 mask = mask >> 1;
233         }
234
235         return value;
236 }
237
238 /*
239  * Close device
240  */
241 static int tahvo_close(struct inode *inode, struct file *filp)
242 {
243         /* Unregister all interrupts that have been registered */
244         if (tahvo_irq_subscr == filp) {
245                 tahvo_unreg_irq_handlers();
246                 tahvo_irq_subscr = NULL;
247         }
248
249         return 0;
250 }
251
252 /*
253  * Device control (ioctl)
254  */
255 static int tahvo_ioctl(struct inode *inode, struct file *filp,
256                        unsigned int cmd, unsigned long arg)
257 {
258         struct retu_tahvo_write_parms par;
259         int ret;
260
261         switch (cmd) {
262         case URT_IOCT_IRQ_SUBSCR:
263                 return tahvo_user_subscribe_to_irq(arg, filp);
264         case TAHVO_IOCH_READ:
265                 return tahvo_user_read_with_mask(arg);
266         case TAHVO_IOCX_WRITE:
267                 ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
268                 if (ret)
269                         printk(KERN_ERR "copy_from_user failed: %d\n", ret);
270                 par.result = tahvo_user_write_with_mask(par.field, par.value);
271                 ret = copy_to_user((void __user *) arg, &par, sizeof(par));
272                 if (ret)
273                         printk(KERN_ERR "copy_to_user failed: %d\n", ret);
274                 break;
275         default:
276                 return -ENOIOCTLCMD;
277         }
278         return 0;
279 }
280
281 /*
282  * Read from device
283  */
284 static ssize_t tahvo_read(struct file *filp, char *buf, size_t count,
285                           loff_t * offp)
286 {
287         struct tahvo_irq *irq;
288
289         u32 nr, i;
290
291         /* read not permitted if neither filp nor anyone has registered IRQs */
292         if (tahvo_irq_subscr != filp)
293                 return -EPERM;
294
295         if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0))
296                 return -EINVAL;
297
298         nr = count / sizeof(u32);
299
300         for (i = 0; i < nr; i++) {
301                 unsigned long flags;
302                 u32 irq_id;
303                 int ret;
304
305                 ret = wait_event_interruptible(tahvo_user_waitqueue,
306                                                !list_empty(&tahvo_irqs));
307                 if (ret < 0)
308                         return ret;
309
310                 spin_lock_irqsave(&tahvo_irqs_lock, flags);
311                 irq = list_entry((&tahvo_irqs)->next, struct tahvo_irq, node);
312                 irq_id = irq->id;
313                 list_move(&irq->node, &tahvo_irqs_reserve);
314                 spin_unlock_irqrestore(&tahvo_irqs_lock, flags);
315
316                 ret = copy_to_user(buf + i * sizeof(irq_id), &irq_id,
317                                   sizeof(irq_id));
318                 if (ret)
319                         printk(KERN_ERR "copy_to_user failed: %d\n", ret);
320         }
321
322         return count;
323 }
324
325 /*
326  * Poll method
327  */
328 static unsigned tahvo_poll(struct file *filp, struct poll_table_struct *pt)
329 {
330         if (!list_empty(&tahvo_irqs))
331                 return POLLIN;
332
333         poll_wait(filp, &tahvo_user_waitqueue, pt);
334
335         if (!list_empty(&tahvo_irqs))
336                 return POLLIN;
337         else
338                 return 0;
339 }
340
341 static struct file_operations tahvo_user_fileops = {
342         .owner = THIS_MODULE,
343         .ioctl = tahvo_ioctl,
344         .read = tahvo_read,
345         .release = tahvo_close,
346         .poll = tahvo_poll
347 };
348
349 static struct miscdevice tahvo_device = {
350         .minor = MISC_DYNAMIC_MINOR,
351         .name = "tahvo",
352         .fops = &tahvo_user_fileops
353 };
354
355 /*
356  * Initialization
357  *
358  * @return 0 if successful, error value otherwise.
359  */
360 int tahvo_user_init(void)
361 {
362         struct tahvo_irq *irq;
363         int res, i;
364
365         irq = kmalloc(sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN, GFP_KERNEL);
366         if (irq == NULL) {
367                 printk(KERN_ERR PFX "kmalloc failed\n");
368                 return -ENOMEM;
369         }
370         memset(irq, 0, sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN);
371         for (i = 0; i < TAHVO_MAX_IRQ_BUF_LEN; i++)
372                 list_add(&irq[i].node, &tahvo_irqs_reserve);
373
374         tahvo_irq_block = irq;
375
376         spin_lock_init(&tahvo_irqs_lock);
377         mutex_init(&tahvo_mutex);
378
379         /* Request a misc device */
380         res = misc_register(&tahvo_device);
381         if (res < 0) {
382                 printk(KERN_ERR PFX "unable to register misc device for %s\n",
383                        tahvo_device.name);
384                 kfree(irq);
385                 return res;
386         }
387
388         return 0;
389 }
390
391 /*
392  * Cleanup.
393  */
394 void tahvo_user_cleanup(void)
395 {
396         /* Unregister our misc device */
397         misc_deregister(&tahvo_device);
398         /* Unregister and disable all TAHVO interrupts */
399         tahvo_unreg_irq_handlers();
400         kfree(tahvo_irq_block);
401 }
402
403 MODULE_DESCRIPTION("Tahvo ASIC user space functions");
404 MODULE_LICENSE("GPL");
405 MODULE_AUTHOR("Mikko Ylinen");