2 * The DSP56001 Device Driver, saviour of the Free World(tm)
4 * Authors: Fredrik Noring <noring@nocrew.org>
5 * lars brinkhoff <lars@nocrew.org>
6 * Tomas Berndtsson <tomas@nocrew.org>
8 * First version May 1996
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
17 * Hmm... there must be something here :)
19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
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
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 */
34 #include <linux/init.h>
35 #include <linux/device.h>
37 #include <asm/atarihw.h>
38 #include <asm/traps.h>
39 #include <asm/uaccess.h> /* For put_user and get_user */
41 #include <asm/dsp56k.h>
44 #define DSP56K_DEV_56001 0 /* The only device so far */
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)
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
55 #define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
56 #define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
58 #define handshake(count, maxio, timeout, ENABLE, f) \
62 m = min_t(unsigned long, count, maxio); \
63 for (i = 0; i < m; i++) { \
64 for (t = 0; t < timeout && !ENABLE; t++) \
71 if (m == maxio) msleep(20); \
78 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
80 if(!DSP56K_TRANSMIT) { \
88 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
90 if(!DSP56K_RECEIVE) { \
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;
138 static struct dsp56k_device {
139 unsigned long in_use;
141 int tx_wsize, rx_wsize;
144 static struct class *dsp56k_class;
146 static int dsp56k_reset(void)
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;
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;
165 static int dsp56k_upload(u_char __user *bin, int len)
173 for (i = 0; i < sizeof_bootstrap/3; i++) {
175 dsp56k_host_interface.data.b[1] = *p++;
176 dsp56k_host_interface.data.b[2] = *p++;
177 dsp56k_host_interface.data.b[3] = *p++;
179 for (; i < 512; i++) {
181 dsp56k_host_interface.data.b[1] = 0;
182 dsp56k_host_interface.data.b[2] = 0;
183 dsp56k_host_interface.data.b[3] = 0;
186 for (i = 0; i < len; i++) {
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++);
194 dsp56k_host_interface.data.l = 3; /* Magic execute */
199 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
202 struct inode *inode = file->f_path.dentry->d_inode;
203 int dev = iminor(inode) & 0x0f;
207 case DSP56K_DEV_56001:
212 /* Don't do anything if nothing is to be done */
213 if (!count) return 0;
216 switch (dsp56k.rx_wsize) {
219 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
220 put_user(dsp56k_host_interface.data.b[3], buf+n++));
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++));
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++));
247 data = (long __user *) buf;
248 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
249 put_user(dsp56k_host_interface.data.l, data+n++));
257 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
262 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
265 struct inode *inode = file->f_path.dentry->d_inode;
266 int dev = iminor(inode) & 0x0f;
270 case DSP56K_DEV_56001:
274 /* Don't do anything if nothing is to be done */
275 if (!count) return 0;
278 switch (dsp56k.tx_wsize) {
281 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
282 get_user(dsp56k_host_interface.data.b[3], buf+n++));
287 const short __user *data;
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++));
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++));
306 const long __user *data;
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++));
319 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
324 static int dsp56k_ioctl(struct inode *inode, struct file *file,
325 unsigned int cmd, unsigned long arg)
327 int dev = iminor(inode) & 0x0f;
328 void __user *argp = (void __user *)arg;
332 case DSP56K_DEV_56001:
339 struct dsp56k_upload __user *binary = argp;
341 if(get_user(len, &binary->len) < 0)
343 if(get_user(bin, &binary->bin) < 0)
347 return -EINVAL; /* nothing to upload?!? */
349 if (len > DSP56K_MAX_BINARY_LENGTH) {
353 r = dsp56k_upload(bin, len);
360 case DSP56K_SET_TX_WSIZE:
361 if (arg > 4 || arg < 1)
363 dsp56k.tx_wsize = (int) arg;
365 case DSP56K_SET_RX_WSIZE:
366 if (arg > 4 || arg < 1)
368 dsp56k.rx_wsize = (int) arg;
370 case DSP56K_HOST_FLAGS:
372 int dir, out, status;
373 struct dsp56k_host_flags __user *hf = argp;
375 if(get_user(dir, &hf->dir) < 0)
377 if(get_user(out, &hf->out) < 0)
380 if ((dir & 0x1) && (out & 0x1))
381 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
383 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
384 if ((dir & 0x2) && (out & 0x2))
385 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
387 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
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;
395 return put_user(status, &hf->status);
397 case DSP56K_HOST_CMD:
398 if (arg > 31 || arg < 0)
400 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
409 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
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???
419 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
421 int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
425 case DSP56K_DEV_56001:
426 /* poll_wait(file, ???, wait); */
427 return POLLIN | POLLRDNORM | POLLOUT;
430 printk("DSP56k driver: Unknown minor device: %d\n", dev);
436 static int dsp56k_open(struct inode *inode, struct file *file)
438 int dev = iminor(inode) & 0x0f;
442 case DSP56K_DEV_56001:
444 if (test_and_set_bit(0, &dsp56k.in_use))
447 dsp56k.timeout = TIMEOUT;
448 dsp56k.maxio = MAXIO;
449 dsp56k.rx_wsize = dsp56k.tx_wsize = 4;
454 /* Zero host flags */
455 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
456 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
467 static int dsp56k_release(struct inode *inode, struct file *file)
469 int dev = iminor(inode) & 0x0f;
473 case DSP56K_DEV_56001:
474 clear_bit(0, &dsp56k.in_use);
477 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
484 static const struct file_operations dsp56k_fops = {
485 .owner = THIS_MODULE,
487 .write = dsp56k_write,
488 .ioctl = dsp56k_ioctl,
490 .release = dsp56k_release,
494 /****** Init and module functions ******/
496 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
498 static int __init dsp56k_init_driver(void)
502 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
503 printk("DSP56k driver: Hardware not present\n");
507 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
508 printk("DSP56k driver: Unable to register driver\n");
511 dsp56k_class = class_create(THIS_MODULE, "dsp56k");
512 if (IS_ERR(dsp56k_class)) {
513 err = PTR_ERR(dsp56k_class);
516 device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), "dsp56k");
522 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
526 module_init(dsp56k_init_driver);
528 static void __exit dsp56k_cleanup_driver(void)
530 device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
531 class_destroy(dsp56k_class);
532 unregister_chrdev(DSP56K_MAJOR, "dsp56k");
534 module_exit(dsp56k_cleanup_driver);
536 MODULE_LICENSE("GPL");