1 /*
2  *  This file contains quirk handling code for ISAPnP devices
3  *  Some devices do not report all their resources, and need to have extra
4  *  resources added. This is most easily accomplished at initialisation time
5  *  when building up the resource structure for the first time.
6  *
7  *  Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk>
8  *
9  *  Heavily based on PCI quirks handling which is
10  *
11  *  Copyright (c) 1999 Martin Mares <mj@ucw.cz>
12  */
13 
14 #include <linux/types.h>
15 #include <linux/kernel.h>
16 #include <linux/pci.h>
17 #include <linux/init.h>
18 #include <linux/isapnp.h>
19 #include <linux/string.h>
20 
21 #if 0
22 #define ISAPNP_DEBUG
23 #endif
24 
quirk_awe32_resources(struct pci_dev * dev)25 static void __init quirk_awe32_resources(struct pci_dev *dev)
26 {
27 	struct isapnp_port *port, *port2, *port3;
28 	struct isapnp_resources *res = dev->sysdata;
29 
30 	/*
31 	 * Unfortunately the isapnp_add_port_resource is too tightly bound
32 	 * into the PnP discovery sequence, and cannot be used. Link in the
33 	 * two extra ports (at offset 0x400 and 0x800 from the one given) by
34 	 * hand.
35 	 */
36 	for ( ; res ; res = res->alt ) {
37 		port2 = isapnp_alloc(sizeof(struct isapnp_port));
38 		port3 = isapnp_alloc(sizeof(struct isapnp_port));
39 		if (!port2 || !port3)
40 			return;
41 		port = res->port;
42 		memcpy(port2, port, sizeof(struct isapnp_port));
43 		memcpy(port3, port, sizeof(struct isapnp_port));
44 		port->next = port2;
45 		port2->next = port3;
46 		port2->min += 0x400;
47 		port2->max += 0x400;
48 		port3->min += 0x800;
49 		port3->max += 0x800;
50 	}
51 	printk(KERN_INFO "isapnp: AWE32 quirk - adding two ports\n");
52 }
53 
quirk_cmi8330_resources(struct pci_dev * dev)54 static void __init quirk_cmi8330_resources(struct pci_dev *dev)
55 {
56 	struct isapnp_resources *res = dev->sysdata;
57 
58 	for ( ; res ; res = res->alt ) {
59 
60 		struct isapnp_irq *irq;
61 		struct isapnp_dma *dma;
62 
63 		for( irq = res->irq; irq; irq = irq->next )	// Valid irqs are 5, 7, 10
64 			irq->map = 0x04A0;						// 0000 0100 1010 0000
65 
66 		for( dma = res->dma; dma; dma = dma->next ) // Valid 8bit dma channels are 1,3
67 			if( ( dma->flags & IORESOURCE_DMA_TYPE_MASK ) == IORESOURCE_DMA_8BIT )
68 				dma->map = 0x000A;
69 	}
70 	printk(KERN_INFO "isapnp: CMI8330 quirk - fixing interrupts and dma\n");
71 }
72 
quirk_sb16audio_resources(struct pci_dev * dev)73 static void __init quirk_sb16audio_resources(struct pci_dev *dev)
74 {
75 	struct isapnp_port *port;
76 	struct isapnp_resources *res = dev->sysdata;
77 	int    changed = 0;
78 
79 	/*
80 	 * The default range on the mpu port for these devices is 0x388-0x388.
81 	 * Here we increase that range so that two such cards can be
82 	 * auto-configured.
83 	 */
84 
85 	for( ; res ; res = res->alt ) {
86 		port = res->port;
87 		if(!port)
88 			continue;
89 		port = port->next;
90 		if(!port)
91 			continue;
92 		port = port->next;
93 		if(!port)
94 			continue;
95 		if(port->min != port->max)
96 			continue;
97 		port->max += 0x70;
98 		changed = 1;
99 	}
100 	if(changed)
101 		printk(KERN_INFO "isapnp: SB audio device quirk - increasing port range\n");
102 	return;
103 }
104 
105 /*
106  *  ISAPnP Quirks
107  *  Cards or devices that need some tweaking due to broken hardware
108  */
109 
110 static struct isapnp_fixup isapnp_fixups[] __initdata = {
111 	/* Soundblaster awe io port quirk */
112 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0021),
113 		quirk_awe32_resources },
114 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0022),
115 		quirk_awe32_resources },
116 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0023),
117 		quirk_awe32_resources },
118 	/* CMI 8330 interrupt and dma fix */
119 	{ ISAPNP_VENDOR('@','X','@'), ISAPNP_DEVICE(0x0001),
120 		quirk_cmi8330_resources },
121 	/* Soundblaster audio device io port range quirk */
122 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0001),
123 		quirk_sb16audio_resources },
124 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0031),
125 		quirk_sb16audio_resources },
126 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0041),
127 		quirk_sb16audio_resources },
128 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042),
129 		quirk_sb16audio_resources },
130 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043),
131 		quirk_sb16audio_resources },
132 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044),
133 		quirk_sb16audio_resources },
134 	{ ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0045),
135 		quirk_sb16audio_resources },
136 	{ 0 }
137 };
138 
isapnp_fixup_device(struct pci_dev * dev)139 void isapnp_fixup_device(struct pci_dev *dev)
140 {
141 	int i = 0;
142 
143 	while (isapnp_fixups[i].vendor != 0) {
144 		if ((isapnp_fixups[i].vendor == dev->vendor) &&
145 		    (isapnp_fixups[i].device == dev->device)) {
146 #ifdef ISAPNP_DEBUG
147 			printk(KERN_DEBUG "isapnp: Calling quirk for %02x:%02x\n",
148 			       dev->bus->number, dev->devfn);
149 #endif
150 			isapnp_fixups[i].quirk_function(dev);
151 		}
152 		i++;
153 	}
154 }
155 
156