Merge ../linus
[linux-2.6] / drivers / char / dsp56k.c
1 /*
2  * The DSP56001 Device Driver, saviour of the Free World(tm)
3  *
4  * Authors: Fredrik Noring   <noring@nocrew.org>
5  *          lars brinkhoff   <lars@nocrew.org>
6  *          Tomas Berndtsson <tomas@nocrew.org>
7  *
8  * First version May 1996
9  *
10  * History:
11  *  97-01-29   Tomas Berndtsson,
12  *               Integrated with Linux 2.1.21 kernel sources.
13  *  97-02-15   Tomas Berndtsson,
14  *               Fixed for kernel 2.1.26
15  *
16  * BUGS:
17  *  Hmm... there must be something here :)
18  *
19  * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
20  *
21  * This file is subject to the terms and conditions of the GNU General Public
22  * License.  See the file COPYING in the main directory of this archive
23  * for more details.
24  */
25
26 #include <linux/module.h>
27 #include <linux/slab.h> /* for kmalloc() and kfree() */
28 #include <linux/sched.h>        /* for struct wait_queue etc */
29 #include <linux/major.h>
30 #include <linux/types.h>
31 #include <linux/errno.h>
32 #include <linux/delay.h>        /* guess what */
33 #include <linux/fs.h>
34 #include <linux/mm.h>
35 #include <linux/init.h>
36 #include <linux/smp_lock.h>
37 #include <linux/device.h>
38
39 #include <asm/atarihw.h>
40 #include <asm/traps.h>
41 #include <asm/uaccess.h>        /* For put_user and get_user */
42
43 #include <asm/dsp56k.h>
44
45 /* minor devices */
46 #define DSP56K_DEV_56001        0    /* The only device so far */
47
48 #define TIMEOUT    10   /* Host port timeout in number of tries */
49 #define MAXIO    2048   /* Maximum number of words before sleep */
50 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
51
52 #define DSP56K_TX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
53 #define DSP56K_RX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
54 #define DSP56K_TX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
55 #define DSP56K_RX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
56
57 #define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
58 #define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
59
60 #define handshake(count, maxio, timeout, ENABLE, f) \
61 { \
62         long i, t, m; \
63         while (count > 0) { \
64                 m = min_t(unsigned long, count, maxio); \
65                 for (i = 0; i < m; i++) { \
66                         for (t = 0; t < timeout && !ENABLE; t++) \
67                                 msleep(20); \
68                         if(!ENABLE) \
69                                 return -EIO; \
70                         f; \
71                 } \
72                 count -= m; \
73                 if (m == maxio) msleep(20); \
74         } \
75 }
76
77 #define tx_wait(n) \
78 { \
79         int t; \
80         for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
81                 msleep(10); \
82         if(!DSP56K_TRANSMIT) { \
83                 return -EIO; \
84         } \
85 }
86
87 #define rx_wait(n) \
88 { \
89         int t; \
90         for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
91                 msleep(10); \
92         if(!DSP56K_RECEIVE) { \
93                 return -EIO; \
94         } \
95 }
96
97 /* DSP56001 bootstrap code */
98 static char bootstrap[] = {
99         0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118         0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
119         0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
120         0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
121         0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
122         0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
123         0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
124         0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
125         0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
126         0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
127         0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
128         0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
129         0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
130         0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
131         0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
132         0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
133         0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
134         0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
135         0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
136         0xf0, 0x80, 0x00, 0x7e, 0xad};
137 static int sizeof_bootstrap = 375;
138
139
140 static struct dsp56k_device {
141         long in_use;
142         long maxio, timeout;
143         int tx_wsize, rx_wsize;
144 } dsp56k;
145
146 static struct class *dsp56k_class;
147
148 static int dsp56k_reset(void)
149 {
150         u_char status;
151         
152         /* Power down the DSP */
153         sound_ym.rd_data_reg_sel = 14;
154         status = sound_ym.rd_data_reg_sel & 0xef;
155         sound_ym.wd_data = status;
156         sound_ym.wd_data = status | 0x10;
157   
158         udelay(10);
159   
160         /* Power up the DSP */
161         sound_ym.rd_data_reg_sel = 14;
162         sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
163
164         return 0;
165 }
166
167 static int dsp56k_upload(u_char __user *bin, int len)
168 {
169         int i;
170         u_char *p;
171         
172         dsp56k_reset();
173   
174         p = bootstrap;
175         for (i = 0; i < sizeof_bootstrap/3; i++) {
176                 /* tx_wait(10); */
177                 dsp56k_host_interface.data.b[1] = *p++;
178                 dsp56k_host_interface.data.b[2] = *p++;
179                 dsp56k_host_interface.data.b[3] = *p++;
180         }
181         for (; i < 512; i++) {
182                 /* tx_wait(10); */
183                 dsp56k_host_interface.data.b[1] = 0;
184                 dsp56k_host_interface.data.b[2] = 0;
185                 dsp56k_host_interface.data.b[3] = 0;
186         }
187   
188         for (i = 0; i < len; i++) {
189                 tx_wait(10);
190                 get_user(dsp56k_host_interface.data.b[1], bin++);
191                 get_user(dsp56k_host_interface.data.b[2], bin++);
192                 get_user(dsp56k_host_interface.data.b[3], bin++);
193         }
194
195         tx_wait(10);
196         dsp56k_host_interface.data.l = 3;    /* Magic execute */
197
198         return 0;
199 }
200
201 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
202                            loff_t *ppos)
203 {
204         struct inode *inode = file->f_path.dentry->d_inode;
205         int dev = iminor(inode) & 0x0f;
206
207         switch(dev)
208         {
209         case DSP56K_DEV_56001:
210         {
211
212                 long n;
213
214                 /* Don't do anything if nothing is to be done */
215                 if (!count) return 0;
216
217                 n = 0;
218                 switch (dsp56k.rx_wsize) {
219                 case 1:  /* 8 bit */
220                 {
221                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
222                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
223                         return n;
224                 }
225                 case 2:  /* 16 bit */
226                 {
227                         short __user *data;
228
229                         count /= 2;
230                         data = (short __user *) buf;
231                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
232                                   put_user(dsp56k_host_interface.data.w[1], data+n++));
233                         return 2*n;
234                 }
235                 case 3:  /* 24 bit */
236                 {
237                         count /= 3;
238                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
239                                   put_user(dsp56k_host_interface.data.b[1], buf+n++);
240                                   put_user(dsp56k_host_interface.data.b[2], buf+n++);
241                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
242                         return 3*n;
243                 }
244                 case 4:  /* 32 bit */
245                 {
246                         long __user *data;
247
248                         count /= 4;
249                         data = (long __user *) buf;
250                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
251                                   put_user(dsp56k_host_interface.data.l, data+n++));
252                         return 4*n;
253                 }
254                 }
255                 return -EFAULT;
256         }
257
258         default:
259                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
260                 return -ENXIO;
261         }
262 }
263
264 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
265                             loff_t *ppos)
266 {
267         struct inode *inode = file->f_path.dentry->d_inode;
268         int dev = iminor(inode) & 0x0f;
269
270         switch(dev)
271         {
272         case DSP56K_DEV_56001:
273         {
274                 long n;
275
276                 /* Don't do anything if nothing is to be done */
277                 if (!count) return 0;
278
279                 n = 0;
280                 switch (dsp56k.tx_wsize) {
281                 case 1:  /* 8 bit */
282                 {
283                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
284                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
285                         return n;
286                 }
287                 case 2:  /* 16 bit */
288                 {
289                         const short __user *data;
290
291                         count /= 2;
292                         data = (const short __user *)buf;
293                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
294                                   get_user(dsp56k_host_interface.data.w[1], data+n++));
295                         return 2*n;
296                 }
297                 case 3:  /* 24 bit */
298                 {
299                         count /= 3;
300                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
301                                   get_user(dsp56k_host_interface.data.b[1], buf+n++);
302                                   get_user(dsp56k_host_interface.data.b[2], buf+n++);
303                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
304                         return 3*n;
305                 }
306                 case 4:  /* 32 bit */
307                 {
308                         const long __user *data;
309
310                         count /= 4;
311                         data = (const long __user *)buf;
312                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
313                                   get_user(dsp56k_host_interface.data.l, data+n++));
314                         return 4*n;
315                 }
316                 }
317
318                 return -EFAULT;
319         }
320         default:
321                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
322                 return -ENXIO;
323         }
324 }
325
326 static int dsp56k_ioctl(struct inode *inode, struct file *file,
327                         unsigned int cmd, unsigned long arg)
328 {
329         int dev = iminor(inode) & 0x0f;
330         void __user *argp = (void __user *)arg;
331
332         switch(dev)
333         {
334         case DSP56K_DEV_56001:
335
336                 switch(cmd) {
337                 case DSP56K_UPLOAD:
338                 {
339                         char __user *bin;
340                         int r, len;
341                         struct dsp56k_upload __user *binary = argp;
342     
343                         if(get_user(len, &binary->len) < 0)
344                                 return -EFAULT;
345                         if(get_user(bin, &binary->bin) < 0)
346                                 return -EFAULT;
347                 
348                         if (len == 0) {
349                                 return -EINVAL;      /* nothing to upload?!? */
350                         }
351                         if (len > DSP56K_MAX_BINARY_LENGTH) {
352                                 return -EINVAL;
353                         }
354     
355                         r = dsp56k_upload(bin, len);
356                         if (r < 0) {
357                                 return r;
358                         }
359     
360                         break;
361                 }
362                 case DSP56K_SET_TX_WSIZE:
363                         if (arg > 4 || arg < 1)
364                                 return -EINVAL;
365                         dsp56k.tx_wsize = (int) arg;
366                         break;
367                 case DSP56K_SET_RX_WSIZE:
368                         if (arg > 4 || arg < 1)
369                                 return -EINVAL;
370                         dsp56k.rx_wsize = (int) arg;
371                         break;
372                 case DSP56K_HOST_FLAGS:
373                 {
374                         int dir, out, status;
375                         struct dsp56k_host_flags __user *hf = argp;
376     
377                         if(get_user(dir, &hf->dir) < 0)
378                                 return -EFAULT;
379                         if(get_user(out, &hf->out) < 0)
380                                 return -EFAULT;
381
382                         if ((dir & 0x1) && (out & 0x1))
383                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
384                         else if (dir & 0x1)
385                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
386                         if ((dir & 0x2) && (out & 0x2))
387                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
388                         else if (dir & 0x2)
389                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
390
391                         status = 0;
392                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
393                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
394                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
395                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
396
397                         return put_user(status, &hf->status);
398                 }
399                 case DSP56K_HOST_CMD:
400                         if (arg > 31 || arg < 0)
401                                 return -EINVAL;
402                         dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
403                                                              DSP56K_CVR_HC);
404                         break;
405                 default:
406                         return -EINVAL;
407                 }
408                 return 0;
409
410         default:
411                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
412                 return -ENXIO;
413         }
414 }
415
416 /* As of 2.1.26 this should be dsp56k_poll,
417  * but how do I then check device minor number?
418  * Do I need this function at all???
419  */
420 #if 0
421 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
422 {
423         int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
424
425         switch(dev)
426         {
427         case DSP56K_DEV_56001:
428                 /* poll_wait(file, ???, wait); */
429                 return POLLIN | POLLRDNORM | POLLOUT;
430
431         default:
432                 printk("DSP56k driver: Unknown minor device: %d\n", dev);
433                 return 0;
434         }
435 }
436 #endif
437
438 static int dsp56k_open(struct inode *inode, struct file *file)
439 {
440         int dev = iminor(inode) & 0x0f;
441
442         switch(dev)
443         {
444         case DSP56K_DEV_56001:
445
446                 if (test_and_set_bit(0, &dsp56k.in_use))
447                         return -EBUSY;
448
449                 dsp56k.timeout = TIMEOUT;
450                 dsp56k.maxio = MAXIO;
451                 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
452
453                 DSP56K_TX_INT_OFF;
454                 DSP56K_RX_INT_OFF;
455
456                 /* Zero host flags */
457                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
458                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
459
460                 break;
461
462         default:
463                 return -ENODEV;
464         }
465
466         return 0;
467 }
468
469 static int dsp56k_release(struct inode *inode, struct file *file)
470 {
471         int dev = iminor(inode) & 0x0f;
472
473         switch(dev)
474         {
475         case DSP56K_DEV_56001:
476                 clear_bit(0, &dsp56k.in_use);
477                 break;
478         default:
479                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
480                 return -ENXIO;
481         }
482
483         return 0;
484 }
485
486 static const struct file_operations dsp56k_fops = {
487         .owner          = THIS_MODULE,
488         .read           = dsp56k_read,
489         .write          = dsp56k_write,
490         .ioctl          = dsp56k_ioctl,
491         .open           = dsp56k_open,
492         .release        = dsp56k_release,
493 };
494
495
496 /****** Init and module functions ******/
497
498 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
499
500 static int __init dsp56k_init_driver(void)
501 {
502         int err = 0;
503
504         if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
505                 printk("DSP56k driver: Hardware not present\n");
506                 return -ENODEV;
507         }
508
509         if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
510                 printk("DSP56k driver: Unable to register driver\n");
511                 return -ENODEV;
512         }
513         dsp56k_class = class_create(THIS_MODULE, "dsp56k");
514         if (IS_ERR(dsp56k_class)) {
515                 err = PTR_ERR(dsp56k_class);
516                 goto out_chrdev;
517         }
518         class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
519
520         printk(banner);
521         goto out;
522
523 out_chrdev:
524         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
525 out:
526         return err;
527 }
528 module_init(dsp56k_init_driver);
529
530 static void __exit dsp56k_cleanup_driver(void)
531 {
532         class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
533         class_destroy(dsp56k_class);
534         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
535 }
536 module_exit(dsp56k_cleanup_driver);
537
538 MODULE_LICENSE("GPL");