Merge branch 'upstream-fixes'
[linux-2.6] / arch / mips / kernel / rtlx.c
1 /*
2  * Copyright (C) 2005 MIPS Technologies, Inc.  All rights reserved.
3  * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
4  *
5  *  This program is free software; you can distribute it and/or modify it
6  *  under the terms of the GNU General Public License (Version 2) as
7  *  published by the Free Software Foundation.
8  *
9  *  This program is distributed in the hope it will be useful, but WITHOUT
10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  *  for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
17  *
18  */
19
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/fs.h>
23 #include <linux/init.h>
24 #include <linux/interrupt.h>
25 #include <linux/irq.h>
26 #include <linux/poll.h>
27 #include <linux/sched.h>
28 #include <linux/wait.h>
29
30 #include <asm/mipsmtregs.h>
31 #include <asm/bitops.h>
32 #include <asm/cpu.h>
33 #include <asm/processor.h>
34 #include <asm/rtlx.h>
35 #include <asm/uaccess.h>
36
37 #define RTLX_TARG_VPE 1
38
39 static struct rtlx_info *rtlx;
40 static int major;
41 static char module_name[] = "rtlx";
42 static struct irqaction irq;
43 static int irq_num;
44
45 static inline int spacefree(int read, int write, int size)
46 {
47         if (read == write) {
48                 /*
49                  * never fill the buffer completely, so indexes are always
50                  * equal if empty and only empty, or !equal if data available
51                  */
52                 return size - 1;
53         }
54
55         return ((read + size - write) % size) - 1;
56 }
57
58 static struct chan_waitqueues {
59         wait_queue_head_t rt_queue;
60         wait_queue_head_t lx_queue;
61 } channel_wqs[RTLX_CHANNELS];
62
63 extern void *vpe_get_shared(int index);
64
65 static void rtlx_dispatch(struct pt_regs *regs)
66 {
67         do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs);
68 }
69
70 static irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
71 {
72         int i;
73
74         for (i = 0; i < RTLX_CHANNELS; i++) {
75                 struct rtlx_channel *chan = &rtlx->channel[i];
76
77                 if (chan->lx_read != chan->lx_write)
78                         wake_up_interruptible(&channel_wqs[i].lx_queue);
79         }
80
81         return IRQ_HANDLED;
82 }
83
84 /* call when we have the address of the shared structure from the SP side. */
85 static int rtlx_init(struct rtlx_info *rtlxi)
86 {
87         int i;
88
89         if (rtlxi->id != RTLX_ID) {
90                 printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi);
91                 return -ENOEXEC;
92         }
93
94         /* initialise the wait queues */
95         for (i = 0; i < RTLX_CHANNELS; i++) {
96                 init_waitqueue_head(&channel_wqs[i].rt_queue);
97                 init_waitqueue_head(&channel_wqs[i].lx_queue);
98         }
99
100         /* set up for interrupt handling */
101         memset(&irq, 0, sizeof(struct irqaction));
102
103         if (cpu_has_vint)
104                 set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
105
106         irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ;
107         irq.handler = rtlx_interrupt;
108         irq.flags = SA_INTERRUPT;
109         irq.name = "RTLX";
110         irq.dev_id = rtlx;
111         setup_irq(irq_num, &irq);
112
113         rtlx = rtlxi;
114
115         return 0;
116 }
117
118 /* only allow one open process at a time to open each channel */
119 static int rtlx_open(struct inode *inode, struct file *filp)
120 {
121         int minor, ret;
122         struct rtlx_channel *chan;
123
124         /* assume only 1 device at the mo. */
125         minor = MINOR(inode->i_rdev);
126
127         if (rtlx == NULL) {
128                 struct rtlx_info **p;
129                 if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
130                         printk(KERN_ERR "vpe_get_shared is NULL. "
131                                "Has an SP program been loaded?\n");
132                         return -EFAULT;
133                 }
134
135                 if (*p == NULL) {
136                         printk(KERN_ERR "vpe_shared %p %p\n", p, *p);
137                         return -EFAULT;
138                 }
139
140                 if ((ret = rtlx_init(*p)) < 0)
141                         return ret;
142         }
143
144         chan = &rtlx->channel[minor];
145
146         if (test_and_set_bit(RTLX_STATE_OPENED, &chan->lx_state))
147                 return -EBUSY;
148
149         return 0;
150 }
151
152 static int rtlx_release(struct inode *inode, struct file *filp)
153 {
154         int minor = MINOR(inode->i_rdev);
155
156         clear_bit(RTLX_STATE_OPENED, &rtlx->channel[minor].lx_state);
157         smp_mb__after_clear_bit();
158
159         return 0;
160 }
161
162 static unsigned int rtlx_poll(struct file *file, poll_table * wait)
163 {
164         int minor;
165         unsigned int mask = 0;
166         struct rtlx_channel *chan;
167
168         minor = MINOR(file->f_dentry->d_inode->i_rdev);
169         chan = &rtlx->channel[minor];
170
171         poll_wait(file, &channel_wqs[minor].rt_queue, wait);
172         poll_wait(file, &channel_wqs[minor].lx_queue, wait);
173
174         /* data available to read? */
175         if (chan->lx_read != chan->lx_write)
176                 mask |= POLLIN | POLLRDNORM;
177
178         /* space to write */
179         if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size))
180                 mask |= POLLOUT | POLLWRNORM;
181
182         return mask;
183 }
184
185 static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count,
186                          loff_t * ppos)
187 {
188         unsigned long failed;
189         size_t fl = 0L;
190         int minor;
191         struct rtlx_channel *lx;
192         DECLARE_WAITQUEUE(wait, current);
193
194         minor = MINOR(file->f_dentry->d_inode->i_rdev);
195         lx = &rtlx->channel[minor];
196
197         /* data available? */
198         if (lx->lx_write == lx->lx_read) {
199                 if (file->f_flags & O_NONBLOCK)
200                         return 0;       /* -EAGAIN makes cat whinge */
201
202                 /* go to sleep */
203                 add_wait_queue(&channel_wqs[minor].lx_queue, &wait);
204                 set_current_state(TASK_INTERRUPTIBLE);
205
206                 while (lx->lx_write == lx->lx_read)
207                         schedule();
208
209                 set_current_state(TASK_RUNNING);
210                 remove_wait_queue(&channel_wqs[minor].lx_queue, &wait);
211
212                 /* back running */
213         }
214
215         /* find out how much in total */
216         count = min(count,
217                     (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size);
218
219         /* then how much from the read pointer onwards */
220         fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
221
222         failed = copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl);
223         if (failed) {
224                 count = fl - failed;
225                 goto out;
226         }
227
228         /* and if there is anything left at the beginning of the buffer */
229         if (count - fl) {
230                 failed = copy_to_user (buffer + fl, lx->lx_buffer, count - fl);
231                 if (failed) {
232                         count -= failed;
233                         goto out;
234                 }
235         }
236
237 out:
238         /* update the index */
239         lx->lx_read += count;
240         lx->lx_read %= lx->buffer_size;
241
242         return count;
243 }
244
245 static ssize_t rtlx_write(struct file *file, const char __user * buffer,
246                           size_t count, loff_t * ppos)
247 {
248         unsigned long failed;
249         int minor;
250         struct rtlx_channel *rt;
251         size_t fl;
252         DECLARE_WAITQUEUE(wait, current);
253
254         minor = MINOR(file->f_dentry->d_inode->i_rdev);
255         rt = &rtlx->channel[minor];
256
257         /* any space left... */
258         if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) {
259
260                 if (file->f_flags & O_NONBLOCK)
261                         return -EAGAIN;
262
263                 add_wait_queue(&channel_wqs[minor].rt_queue, &wait);
264                 set_current_state(TASK_INTERRUPTIBLE);
265
266                 while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size))
267                         schedule();
268
269                 set_current_state(TASK_RUNNING);
270                 remove_wait_queue(&channel_wqs[minor].rt_queue, &wait);
271         }
272
273         /* total number of bytes to copy */
274         count = min(count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) );
275
276         /* first bit from write pointer to the end of the buffer, or count */
277         fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
278
279         failed = copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl);
280         if (failed) {
281                 count = fl - failed;
282                 goto out;
283         }
284
285         /* if there's any left copy to the beginning of the buffer */
286         if (count - fl) {
287                 failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
288                 if (failed) {
289                         count -= failed;
290                         goto out;
291                 }
292         }
293
294 out:
295         rt->rt_write += count;
296         rt->rt_write %= rt->buffer_size;
297
298         return count;
299 }
300
301 static struct file_operations rtlx_fops = {
302         .owner          = THIS_MODULE,
303         .open           = rtlx_open,
304         .release        = rtlx_release,
305         .write          = rtlx_write,
306         .read           = rtlx_read,
307         .poll           = rtlx_poll
308 };
309
310 static char register_chrdev_failed[] __initdata =
311         KERN_ERR "rtlx_module_init: unable to register device\n";
312
313 static int __init rtlx_module_init(void)
314 {
315         major = register_chrdev(0, module_name, &rtlx_fops);
316         if (major < 0) {
317                 printk(register_chrdev_failed);
318                 return major;
319         }
320
321         return 0;
322 }
323
324 static void __exit rtlx_module_exit(void)
325 {
326         unregister_chrdev(major, module_name);
327 }
328
329 module_init(rtlx_module_init);
330 module_exit(rtlx_module_exit);
331
332 MODULE_DESCRIPTION("MIPS RTLX");
333 MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc.");
334 MODULE_LICENSE("GPL");