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