]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/cbus/tahvo-user.c
[PATCH] ARM: OMAP: tahvo_user: sem2mutex conversion
[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
260         switch (cmd) {
261         case URT_IOCT_IRQ_SUBSCR:
262                 return tahvo_user_subscribe_to_irq(arg, filp);
263         case TAHVO_IOCH_READ:
264                 return tahvo_user_read_with_mask(arg);
265         case TAHVO_IOCX_WRITE:
266                 copy_from_user(&par, (void __user *) arg, sizeof(par));
267                 par.result = tahvo_user_write_with_mask(par.field, par.value);
268                 copy_to_user((void __user *) arg, &par, sizeof(par));
269                 break;
270         default:
271                 return -ENOIOCTLCMD;
272         }
273         return 0;
274 }
275
276 /*
277  * Read from device
278  */
279 static ssize_t tahvo_read(struct file *filp, char *buf, size_t count,
280                           loff_t * offp)
281 {
282         struct tahvo_irq *irq;
283
284         u32 nr, i;
285
286         /* read not permitted if neither filp nor anyone has registered IRQs */
287         if (tahvo_irq_subscr != filp)
288                 return -EPERM;
289
290         if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0))
291                 return -EINVAL;
292
293         nr = count / sizeof(u32);
294
295         for (i = 0; i < nr; i++) {
296                 unsigned long flags;
297                 u32 irq_id;
298                 int ret;
299
300                 ret = wait_event_interruptible(tahvo_user_waitqueue,
301                                                !list_empty(&tahvo_irqs));
302                 if (ret < 0)
303                         return ret;
304
305                 spin_lock_irqsave(&tahvo_irqs_lock, flags);
306                 irq = list_entry((&tahvo_irqs)->next, struct tahvo_irq, node);
307                 irq_id = irq->id;
308                 list_move(&irq->node, &tahvo_irqs_reserve);
309                 spin_unlock_irqrestore(&tahvo_irqs_lock, flags);
310
311                 copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id));
312         }
313
314         return count;
315 }
316
317 /*
318  * Poll method
319  */
320 static unsigned tahvo_poll(struct file *filp, struct poll_table_struct *pt)
321 {
322         if (!list_empty(&tahvo_irqs))
323                 return POLLIN;
324
325         poll_wait(filp, &tahvo_user_waitqueue, pt);
326
327         if (!list_empty(&tahvo_irqs))
328                 return POLLIN;
329         else
330                 return 0;
331 }
332
333 static struct file_operations tahvo_user_fileops = {
334         .owner = THIS_MODULE,
335         .ioctl = tahvo_ioctl,
336         .read = tahvo_read,
337         .release = tahvo_close,
338         .poll = tahvo_poll
339 };
340
341 static struct miscdevice tahvo_device = {
342         .minor = MISC_DYNAMIC_MINOR,
343         .name = "tahvo",
344         .fops = &tahvo_user_fileops
345 };
346
347 /*
348  * Initialization
349  *
350  * @return 0 if successful, error value otherwise.
351  */
352 int tahvo_user_init(void)
353 {
354         struct tahvo_irq *irq;
355         int res, i;
356
357         irq = kmalloc(sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN, GFP_KERNEL);
358         if (irq == NULL) {
359                 printk(KERN_ERR PFX "kmalloc failed\n");
360                 return -ENOMEM;
361         }
362         memset(irq, 0, sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN);
363         for (i = 0; i < TAHVO_MAX_IRQ_BUF_LEN; i++)
364                 list_add(&irq[i].node, &tahvo_irqs_reserve);
365
366         tahvo_irq_block = irq;
367
368         spin_lock_init(&tahvo_irqs_lock);
369         mutex_init(&tahvo_mutex);
370
371         /* Request a misc device */
372         res = misc_register(&tahvo_device);
373         if (res < 0) {
374                 printk(KERN_ERR PFX "unable to register misc device for %s\n",
375                        tahvo_device.name);
376                 kfree(irq);
377                 return res;
378         }
379
380         return 0;
381 }
382
383 /*
384  * Cleanup.
385  */
386 void tahvo_user_cleanup(void)
387 {
388         /* Unregister our misc device */
389         misc_deregister(&tahvo_device);
390         /* Unregister and disable all TAHVO interrupts */
391         tahvo_unreg_irq_handlers();
392         kfree(tahvo_irq_block);
393 }
394
395 MODULE_DESCRIPTION("Tahvo ASIC user space functions");
396 MODULE_LICENSE("GPL");
397 MODULE_AUTHOR("Mikko Ylinen");