Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This file contains quirk handling code for PnP 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 | ||
1da177e4 LT |
14 | #include <linux/types.h> |
15 | #include <linux/kernel.h> | |
16 | #include <linux/string.h> | |
17 | #include <linux/slab.h> | |
1da177e4 | 18 | #include <linux/pnp.h> |
a1e7e636 | 19 | #include <linux/io.h> |
1da177e4 LT |
20 | #include "base.h" |
21 | ||
22 | ||
23 | static void quirk_awe32_resources(struct pnp_dev *dev) | |
24 | { | |
25 | struct pnp_port *port, *port2, *port3; | |
26 | struct pnp_option *res = dev->dependent; | |
27 | ||
28 | /* | |
29 | * Unfortunately the isapnp_add_port_resource is too tightly bound | |
30 | * into the PnP discovery sequence, and cannot be used. Link in the | |
31 | * two extra ports (at offset 0x400 and 0x800 from the one given) by | |
32 | * hand. | |
33 | */ | |
34 | for ( ; res ; res = res->next ) { | |
35 | port2 = pnp_alloc(sizeof(struct pnp_port)); | |
36 | if (!port2) | |
37 | return; | |
38 | port3 = pnp_alloc(sizeof(struct pnp_port)); | |
39 | if (!port3) { | |
40 | kfree(port2); | |
41 | return; | |
42 | } | |
43 | port = res->port; | |
44 | memcpy(port2, port, sizeof(struct pnp_port)); | |
45 | memcpy(port3, port, sizeof(struct pnp_port)); | |
46 | port->next = port2; | |
47 | port2->next = port3; | |
48 | port2->min += 0x400; | |
49 | port2->max += 0x400; | |
50 | port3->min += 0x800; | |
51 | port3->max += 0x800; | |
52 | } | |
53 | printk(KERN_INFO "pnp: AWE32 quirk - adding two ports\n"); | |
54 | } | |
55 | ||
56 | static void quirk_cmi8330_resources(struct pnp_dev *dev) | |
57 | { | |
58 | struct pnp_option *res = dev->dependent; | |
59 | unsigned long tmp; | |
60 | ||
61 | for ( ; res ; res = res->next ) { | |
62 | ||
63 | struct pnp_irq *irq; | |
64 | struct pnp_dma *dma; | |
65 | ||
66 | for( irq = res->irq; irq; irq = irq->next ) { // Valid irqs are 5, 7, 10 | |
67 | tmp = 0x04A0; | |
68 | bitmap_copy(irq->map, &tmp, 16); // 0000 0100 1010 0000 | |
69 | } | |
70 | ||
71 | for( dma = res->dma; dma; dma = dma->next ) // Valid 8bit dma channels are 1,3 | |
72 | if( ( dma->flags & IORESOURCE_DMA_TYPE_MASK ) == IORESOURCE_DMA_8BIT ) | |
73 | dma->map = 0x000A; | |
74 | } | |
75 | printk(KERN_INFO "pnp: CMI8330 quirk - fixing interrupts and dma\n"); | |
76 | } | |
77 | ||
78 | static void quirk_sb16audio_resources(struct pnp_dev *dev) | |
79 | { | |
80 | struct pnp_port *port; | |
81 | struct pnp_option *res = dev->dependent; | |
82 | int changed = 0; | |
83 | ||
84 | /* | |
85 | * The default range on the mpu port for these devices is 0x388-0x388. | |
86 | * Here we increase that range so that two such cards can be | |
87 | * auto-configured. | |
88 | */ | |
89 | ||
90 | for( ; res ; res = res->next ) { | |
91 | port = res->port; | |
92 | if(!port) | |
93 | continue; | |
94 | port = port->next; | |
95 | if(!port) | |
96 | continue; | |
97 | port = port->next; | |
98 | if(!port) | |
99 | continue; | |
100 | if(port->min != port->max) | |
101 | continue; | |
102 | port->max += 0x70; | |
103 | changed = 1; | |
104 | } | |
105 | if(changed) | |
106 | printk(KERN_INFO "pnp: SB audio device quirk - increasing port range\n"); | |
107 | return; | |
108 | } | |
109 | ||
172d0496 | 110 | static int quirk_smc_fir_enabled(struct pnp_dev *dev) |
a1e7e636 | 111 | { |
172d0496 BH |
112 | unsigned long firbase; |
113 | u8 bank, high, low, chip; | |
114 | ||
115 | if (!pnp_port_valid(dev, 1)) | |
116 | return 0; | |
117 | ||
118 | firbase = pnp_port_start(dev, 1); | |
119 | ||
120 | /* Select register bank 3 */ | |
121 | bank = inb(firbase + 7); | |
122 | bank &= 0xf0; | |
123 | bank |= 3; | |
124 | outb(bank, firbase + 7); | |
125 | ||
126 | high = inb(firbase + 0); | |
127 | low = inb(firbase + 1); | |
128 | chip = inb(firbase + 2); | |
129 | ||
130 | /* This corresponds to the check in smsc_ircc_present() */ | |
131 | if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2)) | |
132 | return 1; | |
133 | ||
134 | return 0; | |
135 | } | |
a1e7e636 | 136 | |
172d0496 BH |
137 | static void quirk_smc_enable(struct pnp_dev *dev) |
138 | { | |
41a53114 BH |
139 | struct resource fir, sir, irq; |
140 | ||
141 | pnp_activate_dev(dev); | |
142 | if (quirk_smc_fir_enabled(dev)) | |
a1e7e636 BH |
143 | return; |
144 | ||
145 | /* | |
172d0496 BH |
146 | * Sometimes the BIOS claims the device is enabled, but it reports |
147 | * the wrong FIR resources or doesn't properly configure ISA or LPC | |
148 | * bridges on the way to the device. | |
a1e7e636 | 149 | * |
172d0496 BH |
150 | * HP nc6000 and nc8000/nw8000 laptops have known problems like |
151 | * this. Fortunately, they do fix things up if we auto-configure | |
152 | * the device using its _PRS and _SRS methods. | |
a1e7e636 | 153 | */ |
41a53114 BH |
154 | dev_err(&dev->dev, "%s not responding at SIR 0x%lx, FIR 0x%lx; " |
155 | "auto-configuring\n", dev->id->id, | |
156 | (unsigned long) pnp_port_start(dev, 0), | |
157 | (unsigned long) pnp_port_start(dev, 1)); | |
172d0496 BH |
158 | |
159 | pnp_disable_dev(dev); | |
160 | pnp_init_resource_table(&dev->res); | |
161 | pnp_auto_config_dev(dev); | |
162 | pnp_activate_dev(dev); | |
41a53114 BH |
163 | if (quirk_smc_fir_enabled(dev)) { |
164 | dev_err(&dev->dev, "responds at SIR 0x%lx, FIR 0x%lx\n", | |
165 | (unsigned long) pnp_port_start(dev, 0), | |
166 | (unsigned long) pnp_port_start(dev, 1)); | |
167 | return; | |
168 | } | |
169 | ||
170 | /* | |
171 | * The Toshiba Portege 4000 _CRS reports the FIR region first, | |
172 | * followed by the SIR region. The BIOS will configure the bridge, | |
173 | * but only if we call _SRS with SIR first, then FIR. It also | |
174 | * reports the IRQ as active high, when it is really active low. | |
175 | */ | |
176 | dev_err(&dev->dev, "not responding at SIR 0x%lx, FIR 0x%lx; " | |
177 | "swapping SIR/FIR and reconfiguring\n", | |
178 | (unsigned long) pnp_port_start(dev, 0), | |
179 | (unsigned long) pnp_port_start(dev, 1)); | |
180 | ||
181 | /* | |
182 | * Clear IORESOURCE_AUTO so pnp_activate_dev() doesn't reassign | |
183 | * these resources any more. | |
184 | */ | |
185 | fir = dev->res.port_resource[0]; | |
186 | sir = dev->res.port_resource[1]; | |
187 | fir.flags &= ~IORESOURCE_AUTO; | |
188 | sir.flags &= ~IORESOURCE_AUTO; | |
189 | ||
190 | irq = dev->res.irq_resource[0]; | |
191 | irq.flags &= ~IORESOURCE_AUTO; | |
192 | irq.flags &= ~IORESOURCE_BITS; | |
193 | irq.flags |= IORESOURCE_IRQ_LOWEDGE; | |
194 | ||
195 | pnp_disable_dev(dev); | |
196 | dev->res.port_resource[0] = sir; | |
197 | dev->res.port_resource[1] = fir; | |
198 | dev->res.irq_resource[0] = irq; | |
199 | pnp_activate_dev(dev); | |
200 | ||
201 | if (quirk_smc_fir_enabled(dev)) { | |
202 | dev_err(&dev->dev, "responds at SIR 0x%lx, FIR 0x%lx\n", | |
203 | (unsigned long) pnp_port_start(dev, 0), | |
204 | (unsigned long) pnp_port_start(dev, 1)); | |
205 | return; | |
206 | } | |
172d0496 | 207 | |
41a53114 BH |
208 | dev_err(&dev->dev, "giving up; try \"smsc-ircc2.nopnp\" and " |
209 | "email bjorn.helgaas@hp.com\n"); | |
a1e7e636 BH |
210 | } |
211 | ||
212 | ||
1da177e4 LT |
213 | /* |
214 | * PnP Quirks | |
215 | * Cards or devices that need some tweaking due to incomplete resource info | |
216 | */ | |
217 | ||
218 | static struct pnp_fixup pnp_fixups[] = { | |
219 | /* Soundblaster awe io port quirk */ | |
220 | { "CTL0021", quirk_awe32_resources }, | |
221 | { "CTL0022", quirk_awe32_resources }, | |
222 | { "CTL0023", quirk_awe32_resources }, | |
223 | /* CMI 8330 interrupt and dma fix */ | |
224 | { "@X@0001", quirk_cmi8330_resources }, | |
225 | /* Soundblaster audio device io port range quirk */ | |
226 | { "CTL0001", quirk_sb16audio_resources }, | |
227 | { "CTL0031", quirk_sb16audio_resources }, | |
228 | { "CTL0041", quirk_sb16audio_resources }, | |
229 | { "CTL0042", quirk_sb16audio_resources }, | |
230 | { "CTL0043", quirk_sb16audio_resources }, | |
231 | { "CTL0044", quirk_sb16audio_resources }, | |
232 | { "CTL0045", quirk_sb16audio_resources }, | |
a1e7e636 | 233 | { "SMCf010", quirk_smc_enable }, |
1da177e4 LT |
234 | { "" } |
235 | }; | |
236 | ||
237 | void pnp_fixup_device(struct pnp_dev *dev) | |
238 | { | |
239 | int i = 0; | |
240 | ||
241 | while (*pnp_fixups[i].id) { | |
242 | if (compare_pnp_id(dev->id,pnp_fixups[i].id)) { | |
243 | pnp_dbg("Calling quirk for %s", | |
244 | dev->dev.bus_id); | |
245 | pnp_fixups[i].quirk_function(dev); | |
246 | } | |
247 | i++; | |
248 | } | |
249 | } |