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
61static short isp16_detect(void);
62static short isp16_c928__detect(void);
63static short isp16_c929__detect(void);
64static short isp16_cdi_config(int base, u_char drive_type, int irq,
65			      int dma);
66static short isp16_type;	/* dependent on type of interface card */
67static u_char isp16_ctrl;
68static u_short isp16_enable_port;
69
70static int isp16_cdrom_base = ISP16_CDROM_IO_BASE;
71static int isp16_cdrom_irq = ISP16_CDROM_IRQ;
72static int isp16_cdrom_dma = ISP16_CDROM_DMA;
73static char *isp16_cdrom_type = ISP16_CDROM_TYPE;
74
75module_param(isp16_cdrom_base, int, 0);
76module_param(isp16_cdrom_irq, int, 0);
77module_param(isp16_cdrom_dma, int, 0);
78module_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
85static 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 */
111static 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
166cleanup_out:
167	release_region(ISP16_IO_BASE, ISP16_IO_SIZE);
168out:
169	return -EIO;
170}
171
172static 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
181static 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
226static 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
252static short __init
253isp16_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
365static 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
371module_init(isp16_init);
372module_exit(isp16_exit);
373
374MODULE_LICENSE("GPL");
375