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