Merge branch 'master' of /home/trondmy/kernel/linux-2.6/
[linux-2.6] / drivers / cdrom / isp16.c
1 /* -- ISP16 cdrom detection and configuration
2  *
3  *    Copyright (c) 1995,1996 Eric van der Maarel <H.T.M.v.d.Maarel@marin.nl>
4  *
5  *    Version 0.6
6  *
7  *    History:
8  *    0.5 First release.
9  *        Was included in the sjcd and optcd cdrom drivers.
10  *    0.6 First "stand-alone" version.
11  *        Removed sound configuration.
12  *        Added "module" support.
13  *
14  *      9 November 1999 -- Make kernel-parameter implementation work with 2.3.x 
15  *                         Removed init_module & cleanup_module in favor of 
16  *                         module_init & module_exit.
17  *                         Torben Mathiasen <tmm@image.dk>
18  *
19  *     19 June 2004     -- check_region() converted to request_region()
20  *                         and return statement cleanups.
21  *                          - Jesper Juhl
22  *
23  *    Detect cdrom interface on ISP16 sound card.
24  *    Configure cdrom interface.
25  *
26  *    Algorithm for the card with OPTi 82C928 taken
27  *    from the CDSETUP.SYS driver for MSDOS,
28  *    by OPTi Computers, version 2.03.
29  *    Algorithm for the card with OPTi 82C929 as communicated
30  *    to me by Vadim Model and Leo Spiekman.
31  *
32  *    This program is free software; you can redistribute it and/or modify
33  *    it under the terms of the GNU General Public License as published by
34  *    the Free Software Foundation; either version 2 of the License, or
35  *    (at your option) any later version.
36  *
37  *    This program is distributed in the hope that it will be useful,
38  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
39  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40  *    GNU General Public License for more details.
41  *
42  *    You should have received a copy of the GNU General Public License
43  *    along with this program; if not, write to the Free Software
44  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
45  *
46  */
47
48 #define ISP16_VERSION_MAJOR 0
49 #define ISP16_VERSION_MINOR 6
50
51 #include <linux/module.h>
52
53 #include <linux/fs.h>
54 #include <linux/kernel.h>
55 #include <linux/string.h>
56 #include <linux/ioport.h>
57 #include <linux/init.h>
58 #include <asm/io.h>
59 #include "isp16.h"
60
61 static short isp16_detect(void);
62 static short isp16_c928__detect(void);
63 static short isp16_c929__detect(void);
64 static short isp16_cdi_config(int base, u_char drive_type, int irq,
65                               int dma);
66 static short isp16_type;        /* dependent on type of interface card */
67 static u_char isp16_ctrl;
68 static u_short isp16_enable_port;
69
70 static int isp16_cdrom_base = ISP16_CDROM_IO_BASE;
71 static int isp16_cdrom_irq = ISP16_CDROM_IRQ;
72 static int isp16_cdrom_dma = ISP16_CDROM_DMA;
73 static char *isp16_cdrom_type = ISP16_CDROM_TYPE;
74
75 module_param(isp16_cdrom_base, int, 0);
76 module_param(isp16_cdrom_irq, int, 0);
77 module_param(isp16_cdrom_dma, int, 0);
78 module_param(isp16_cdrom_type, charp, 0);
79
80 #define ISP16_IN(p) (outb(isp16_ctrl,ISP16_CTRL_PORT), inb(p))
81 #define ISP16_OUT(p,b) (outb(isp16_ctrl,ISP16_CTRL_PORT), outb(b,p))
82
83 #ifndef MODULE
84
85 static int
86 __init isp16_setup(char *str)
87 {
88         int ints[4];
89
90         (void) get_options(str, ARRAY_SIZE(ints), ints);
91         if (ints[0] > 0)
92                 isp16_cdrom_base = ints[1];
93         if (ints[0] > 1)
94                 isp16_cdrom_irq = ints[2];
95         if (ints[0] > 2)
96                 isp16_cdrom_dma = ints[3];
97         if (str)
98                 isp16_cdrom_type = str;
99
100         return 1;
101 }
102
103 __setup("isp16=", isp16_setup);
104
105 #endif                          /* MODULE */
106
107 /*
108  *  ISP16 initialisation.
109  *
110  */
111 static int __init isp16_init(void)
112 {
113         u_char expected_drive;
114
115         printk(KERN_INFO
116                "ISP16: configuration cdrom interface, version %d.%d.\n",
117                ISP16_VERSION_MAJOR, ISP16_VERSION_MINOR);
118
119         if (!strcmp(isp16_cdrom_type, "noisp16")) {
120                 printk("ISP16: no cdrom interface configured.\n");
121                 return 0;
122         }
123
124         if (!request_region(ISP16_IO_BASE, ISP16_IO_SIZE, "isp16")) {
125                 printk("ISP16: i/o ports already in use.\n");
126                 goto out;
127         }
128
129         if ((isp16_type = isp16_detect()) < 0) {
130                 printk("ISP16: no cdrom interface found.\n");
131                 goto cleanup_out;
132         }
133
134         printk(KERN_INFO
135                "ISP16: cdrom interface (with OPTi 82C92%d chip) detected.\n",
136                (isp16_type == 2) ? 9 : 8);
137
138         if (!strcmp(isp16_cdrom_type, "Sanyo"))
139                 expected_drive =
140                     (isp16_type ? ISP16_SANYO1 : ISP16_SANYO0);
141         else if (!strcmp(isp16_cdrom_type, "Sony"))
142                 expected_drive = ISP16_SONY;
143         else if (!strcmp(isp16_cdrom_type, "Panasonic"))
144                 expected_drive =
145                     (isp16_type ? ISP16_PANASONIC1 : ISP16_PANASONIC0);
146         else if (!strcmp(isp16_cdrom_type, "Mitsumi"))
147                 expected_drive = ISP16_MITSUMI;
148         else {
149                 printk("ISP16: %s not supported by cdrom interface.\n",
150                        isp16_cdrom_type);
151                 goto cleanup_out;
152         }
153
154         if (isp16_cdi_config(isp16_cdrom_base, expected_drive,
155                              isp16_cdrom_irq, isp16_cdrom_dma) < 0) {
156                 printk
157                     ("ISP16: cdrom interface has not been properly configured.\n");
158                 goto cleanup_out;
159         }
160         printk(KERN_INFO
161                "ISP16: cdrom interface set up with io base 0x%03X, irq %d, dma %d,"
162                " type %s.\n", isp16_cdrom_base, isp16_cdrom_irq,
163                isp16_cdrom_dma, isp16_cdrom_type);
164         return 0;
165
166 cleanup_out:
167         release_region(ISP16_IO_BASE, ISP16_IO_SIZE);
168 out:
169         return -EIO;
170 }
171
172 static short __init isp16_detect(void)
173 {
174
175         if (isp16_c929__detect() >= 0)
176                 return 2;
177         else
178                 return (isp16_c928__detect());
179 }
180
181 static short __init isp16_c928__detect(void)
182 {
183         u_char ctrl;
184         u_char enable_cdrom;
185         u_char io;
186         short i = -1;
187
188         isp16_ctrl = ISP16_C928__CTRL;
189         isp16_enable_port = ISP16_C928__ENABLE_PORT;
190
191         /* read' and write' are a special read and write, respectively */
192
193         /* read' ISP16_CTRL_PORT, clear last two bits and write' back the result */
194         ctrl = ISP16_IN(ISP16_CTRL_PORT) & 0xFC;
195         ISP16_OUT(ISP16_CTRL_PORT, ctrl);
196
197         /* read' 3,4 and 5-bit from the cdrom enable port */
198         enable_cdrom = ISP16_IN(ISP16_C928__ENABLE_PORT) & 0x38;
199
200         if (!(enable_cdrom & 0x20)) {   /* 5-bit not set */
201                 /* read' last 2 bits of ISP16_IO_SET_PORT */
202                 io = ISP16_IN(ISP16_IO_SET_PORT) & 0x03;
203                 if (((io & 0x01) << 1) == (io & 0x02)) {        /* bits are the same */
204                         if (io == 0) {  /* ...the same and 0 */
205                                 i = 0;
206                                 enable_cdrom |= 0x20;
207                         } else {        /* ...the same and 1 *//* my card, first time 'round */
208                                 i = 1;
209                                 enable_cdrom |= 0x28;
210                         }
211                         ISP16_OUT(ISP16_C928__ENABLE_PORT, enable_cdrom);
212                 } else {        /* bits are not the same */
213                         ISP16_OUT(ISP16_CTRL_PORT, ctrl);
214                         return i;       /* -> not detected: possibly incorrect conclusion */
215                 }
216         } else if (enable_cdrom == 0x20)
217                 i = 0;
218         else if (enable_cdrom == 0x28)  /* my card, already initialised */
219                 i = 1;
220
221         ISP16_OUT(ISP16_CTRL_PORT, ctrl);
222
223         return i;
224 }
225
226 static short __init isp16_c929__detect(void)
227 {
228         u_char ctrl;
229         u_char tmp;
230
231         isp16_ctrl = ISP16_C929__CTRL;
232         isp16_enable_port = ISP16_C929__ENABLE_PORT;
233
234         /* read' and write' are a special read and write, respectively */
235
236         /* read' ISP16_CTRL_PORT and save */
237         ctrl = ISP16_IN(ISP16_CTRL_PORT);
238
239         /* write' zero to the ctrl port and get response */
240         ISP16_OUT(ISP16_CTRL_PORT, 0);
241         tmp = ISP16_IN(ISP16_CTRL_PORT);
242
243         if (tmp != 2)           /* isp16 with 82C929 not detected */
244                 return -1;
245
246         /* restore ctrl port value */
247         ISP16_OUT(ISP16_CTRL_PORT, ctrl);
248
249         return 2;
250 }
251
252 static short __init
253 isp16_cdi_config(int base, u_char drive_type, int irq, int dma)
254 {
255         u_char base_code;
256         u_char irq_code;
257         u_char dma_code;
258         u_char i;
259
260         if ((drive_type == ISP16_MITSUMI) && (dma != 0))
261                 printk("ISP16: Mitsumi cdrom drive has no dma support.\n");
262
263         switch (base) {
264         case 0x340:
265                 base_code = ISP16_BASE_340;
266                 break;
267         case 0x330:
268                 base_code = ISP16_BASE_330;
269                 break;
270         case 0x360:
271                 base_code = ISP16_BASE_360;
272                 break;
273         case 0x320:
274                 base_code = ISP16_BASE_320;
275                 break;
276         default:
277                 printk
278                     ("ISP16: base address 0x%03X not supported by cdrom interface.\n",
279                      base);
280                 return -1;
281         }
282         switch (irq) {
283         case 0:
284                 irq_code = ISP16_IRQ_X;
285                 break;          /* disable irq */
286         case 5:
287                 irq_code = ISP16_IRQ_5;
288                 printk("ISP16: irq 5 shouldn't be used by cdrom interface,"
289                        " due to possible conflicts with the sound card.\n");
290                 break;
291         case 7:
292                 irq_code = ISP16_IRQ_7;
293                 printk("ISP16: irq 7 shouldn't be used by cdrom interface,"
294                        " due to possible conflicts with the sound card.\n");
295                 break;
296         case 3:
297                 irq_code = ISP16_IRQ_3;
298                 break;
299         case 9:
300                 irq_code = ISP16_IRQ_9;
301                 break;
302         case 10:
303                 irq_code = ISP16_IRQ_10;
304                 break;
305         case 11:
306                 irq_code = ISP16_IRQ_11;
307                 break;
308         default:
309                 printk("ISP16: irq %d not supported by cdrom interface.\n",
310                        irq);
311                 return -1;
312         }
313         switch (dma) {
314         case 0:
315                 dma_code = ISP16_DMA_X;
316                 break;          /* disable dma */
317         case 1:
318                 printk("ISP16: dma 1 cannot be used by cdrom interface,"
319                        " due to conflict with the sound card.\n");
320                 return -1;
321                 break;
322         case 3:
323                 dma_code = ISP16_DMA_3;
324                 break;
325         case 5:
326                 dma_code = ISP16_DMA_5;
327                 break;
328         case 6:
329                 dma_code = ISP16_DMA_6;
330                 break;
331         case 7:
332                 dma_code = ISP16_DMA_7;
333                 break;
334         default:
335                 printk("ISP16: dma %d not supported by cdrom interface.\n",
336                        dma);
337                 return -1;
338         }
339
340         if (drive_type != ISP16_SONY && drive_type != ISP16_PANASONIC0 &&
341             drive_type != ISP16_PANASONIC1 && drive_type != ISP16_SANYO0 &&
342             drive_type != ISP16_SANYO1 && drive_type != ISP16_MITSUMI &&
343             drive_type != ISP16_DRIVE_X) {
344                 printk
345                     ("ISP16: drive type (code 0x%02X) not supported by cdrom"
346                      " interface.\n", drive_type);
347                 return -1;
348         }
349
350         /* set type of interface */
351         i = ISP16_IN(ISP16_DRIVE_SET_PORT) & ISP16_DRIVE_SET_MASK;      /* clear some bits */
352         ISP16_OUT(ISP16_DRIVE_SET_PORT, i | drive_type);
353
354         /* enable cdrom on interface with 82C929 chip */
355         if (isp16_type > 1)
356                 ISP16_OUT(isp16_enable_port, ISP16_ENABLE_CDROM);
357
358         /* set base address, irq and dma */
359         i = ISP16_IN(ISP16_IO_SET_PORT) & ISP16_IO_SET_MASK;    /* keep some bits */
360         ISP16_OUT(ISP16_IO_SET_PORT, i | base_code | irq_code | dma_code);
361
362         return 0;
363 }
364
365 static void __exit isp16_exit(void)
366 {
367         release_region(ISP16_IO_BASE, ISP16_IO_SIZE);
368         printk(KERN_INFO "ISP16: module released.\n");
369 }
370
371 module_init(isp16_init);
372 module_exit(isp16_exit);
373
374 MODULE_LICENSE("GPL");