]>
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> | |
1f32ca31 BH |
8 | * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. |
9 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | |
1da177e4 LT |
10 | * |
11 | * Heavily based on PCI quirks handling which is | |
12 | * | |
13 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> | |
14 | */ | |
15 | ||
1da177e4 LT |
16 | #include <linux/types.h> |
17 | #include <linux/kernel.h> | |
18 | #include <linux/string.h> | |
19 | #include <linux/slab.h> | |
1da177e4 | 20 | #include <linux/pnp.h> |
a1e7e636 | 21 | #include <linux/io.h> |
a05d0781 | 22 | #include <linux/kallsyms.h> |
1da177e4 LT |
23 | #include "base.h" |
24 | ||
1f32ca31 BH |
25 | static void quirk_awe32_add_ports(struct pnp_dev *dev, |
26 | struct pnp_option *option, | |
27 | unsigned int offset) | |
28 | { | |
29 | struct pnp_option *new_option; | |
30 | ||
31 | new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL); | |
32 | if (!new_option) { | |
33 | dev_err(&dev->dev, "couldn't add ioport region to option set " | |
34 | "%d\n", pnp_option_set(option)); | |
35 | return; | |
36 | } | |
37 | ||
38 | *new_option = *option; | |
39 | new_option->u.port.min += offset; | |
40 | new_option->u.port.max += offset; | |
41 | list_add(&new_option->list, &option->list); | |
42 | ||
43 | dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d\n", | |
44 | (unsigned long long) new_option->u.port.min, | |
45 | (unsigned long long) new_option->u.port.max, | |
46 | pnp_option_set(option)); | |
47 | } | |
48 | ||
1da177e4 LT |
49 | static void quirk_awe32_resources(struct pnp_dev *dev) |
50 | { | |
1f32ca31 BH |
51 | struct pnp_option *option; |
52 | unsigned int set = ~0; | |
1da177e4 LT |
53 | |
54 | /* | |
1f32ca31 BH |
55 | * Add two extra ioport regions (at offset 0x400 and 0x800 from the |
56 | * one given) to every dependent option set. | |
1da177e4 | 57 | */ |
1f32ca31 BH |
58 | list_for_each_entry(option, &dev->options, list) { |
59 | if (pnp_option_is_dependent(option) && | |
60 | pnp_option_set(option) != set) { | |
61 | set = pnp_option_set(option); | |
62 | quirk_awe32_add_ports(dev, option, 0x800); | |
63 | quirk_awe32_add_ports(dev, option, 0x400); | |
1da177e4 | 64 | } |
1da177e4 | 65 | } |
1da177e4 LT |
66 | } |
67 | ||
68 | static void quirk_cmi8330_resources(struct pnp_dev *dev) | |
69 | { | |
1f32ca31 BH |
70 | struct pnp_option *option; |
71 | struct pnp_irq *irq; | |
72 | struct pnp_dma *dma; | |
1da177e4 | 73 | |
1f32ca31 BH |
74 | list_for_each_entry(option, &dev->options, list) { |
75 | if (!pnp_option_is_dependent(option)) | |
76 | continue; | |
1da177e4 | 77 | |
1f32ca31 BH |
78 | if (option->type == IORESOURCE_IRQ) { |
79 | irq = &option->u.irq; | |
80 | bitmap_zero(irq->map.bits, PNP_IRQ_NR); | |
81 | __set_bit(5, irq->map.bits); | |
82 | __set_bit(7, irq->map.bits); | |
83 | __set_bit(10, irq->map.bits); | |
84 | dev_info(&dev->dev, "set possible IRQs in " | |
85 | "option set %d to 5, 7, 10\n", | |
86 | pnp_option_set(option)); | |
87 | } else if (option->type == IORESOURCE_DMA) { | |
88 | dma = &option->u.dma; | |
9dd78466 | 89 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == |
1f32ca31 BH |
90 | IORESOURCE_DMA_8BIT && |
91 | dma->map != 0x0A) { | |
92 | dev_info(&dev->dev, "changing possible " | |
93 | "DMA channel mask in option set %d " | |
94 | "from %#02x to 0x0A (1, 3)\n", | |
95 | pnp_option_set(option), dma->map); | |
96 | dma->map = 0x0A; | |
97 | } | |
7aefff51 | 98 | } |
1da177e4 | 99 | } |
1da177e4 LT |
100 | } |
101 | ||
102 | static void quirk_sb16audio_resources(struct pnp_dev *dev) | |
103 | { | |
1f32ca31 BH |
104 | struct pnp_option *option; |
105 | unsigned int prev_option_flags = ~0, n = 0; | |
1da177e4 | 106 | struct pnp_port *port; |
1da177e4 LT |
107 | |
108 | /* | |
1f32ca31 | 109 | * The default range on the OPL port for these devices is 0x388-0x388. |
1da177e4 LT |
110 | * Here we increase that range so that two such cards can be |
111 | * auto-configured. | |
112 | */ | |
1f32ca31 BH |
113 | list_for_each_entry(option, &dev->options, list) { |
114 | if (prev_option_flags != option->flags) { | |
115 | prev_option_flags = option->flags; | |
116 | n = 0; | |
117 | } | |
1da177e4 | 118 | |
1f32ca31 BH |
119 | if (pnp_option_is_dependent(option) && |
120 | option->type == IORESOURCE_IO) { | |
121 | n++; | |
122 | port = &option->u.port; | |
123 | if (n == 3 && port->min == port->max) { | |
124 | port->max += 0x70; | |
125 | dev_info(&dev->dev, "increased option port " | |
126 | "range from %#llx-%#llx to " | |
127 | "%#llx-%#llx\n", | |
128 | (unsigned long long) port->min, | |
129 | (unsigned long long) port->min, | |
130 | (unsigned long long) port->min, | |
131 | (unsigned long long) port->max); | |
132 | } | |
133 | } | |
1da177e4 | 134 | } |
1da177e4 LT |
135 | } |
136 | ||
1f32ca31 BH |
137 | static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, |
138 | unsigned int set) | |
3b73a223 | 139 | { |
1f32ca31 BH |
140 | struct pnp_option *tail = NULL, *first_new_option = NULL; |
141 | struct pnp_option *option, *new_option; | |
142 | unsigned int flags; | |
3b73a223 | 143 | |
1f32ca31 BH |
144 | list_for_each_entry(option, &dev->options, list) { |
145 | if (pnp_option_is_dependent(option)) | |
146 | tail = option; | |
147 | } | |
148 | if (!tail) { | |
149 | dev_err(&dev->dev, "no dependent option sets\n"); | |
150 | return NULL; | |
151 | } | |
d5ebde6e | 152 | |
1f32ca31 BH |
153 | flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); |
154 | list_for_each_entry(option, &dev->options, list) { | |
155 | if (pnp_option_is_dependent(option) && | |
156 | pnp_option_set(option) == set) { | |
157 | new_option = kmalloc(sizeof(struct pnp_option), | |
158 | GFP_KERNEL); | |
159 | if (!new_option) { | |
160 | dev_err(&dev->dev, "couldn't clone dependent " | |
161 | "set %d\n", set); | |
162 | return NULL; | |
163 | } | |
3b73a223 | 164 | |
1f32ca31 BH |
165 | *new_option = *option; |
166 | new_option->flags = flags; | |
167 | if (!first_new_option) | |
168 | first_new_option = new_option; | |
3b73a223 | 169 | |
1f32ca31 BH |
170 | list_add(&new_option->list, &tail->list); |
171 | tail = new_option; | |
3b73a223 | 172 | } |
3b73a223 | 173 | } |
3b73a223 | 174 | |
1f32ca31 | 175 | return first_new_option; |
3b73a223 RH |
176 | } |
177 | ||
1f32ca31 BH |
178 | |
179 | static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) | |
3b73a223 | 180 | { |
1f32ca31 BH |
181 | struct pnp_option *new_option; |
182 | unsigned int num_sets, i, set; | |
3b73a223 RH |
183 | struct pnp_irq *irq; |
184 | ||
1f32ca31 BH |
185 | num_sets = dev->num_dependent_sets; |
186 | for (i = 0; i < num_sets; i++) { | |
187 | new_option = pnp_clone_dependent_set(dev, i); | |
188 | if (!new_option) | |
189 | return; | |
3b73a223 | 190 | |
1f32ca31 BH |
191 | set = pnp_option_set(new_option); |
192 | while (new_option && pnp_option_set(new_option) == set) { | |
193 | if (new_option->type == IORESOURCE_IRQ) { | |
194 | irq = &new_option->u.irq; | |
195 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; | |
196 | } | |
197 | dbg_pnp_show_option(dev, new_option); | |
198 | new_option = list_entry(new_option->list.next, | |
199 | struct pnp_option, list); | |
200 | } | |
3b73a223 | 201 | |
1f32ca31 BH |
202 | dev_info(&dev->dev, "added dependent option set %d (same as " |
203 | "set %d except IRQ optional)\n", set, i); | |
204 | } | |
3b73a223 RH |
205 | } |
206 | ||
1f32ca31 | 207 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) |
3b73a223 | 208 | { |
1f32ca31 BH |
209 | struct pnp_option *option; |
210 | struct pnp_irq *irq = NULL; | |
211 | unsigned int independent_irqs = 0; | |
212 | ||
213 | list_for_each_entry(option, &dev->options, list) { | |
214 | if (option->type == IORESOURCE_IRQ && | |
215 | !pnp_option_is_dependent(option)) { | |
216 | independent_irqs++; | |
217 | irq = &option->u.irq; | |
218 | } | |
219 | } | |
3b73a223 | 220 | |
1f32ca31 | 221 | if (independent_irqs != 1) |
3b73a223 RH |
222 | return; |
223 | ||
1f32ca31 BH |
224 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; |
225 | dev_info(&dev->dev, "made independent IRQ optional\n"); | |
3b73a223 | 226 | } |
0509ad5e BH |
227 | |
228 | #include <linux/pci.h> | |
229 | ||
230 | static void quirk_system_pci_resources(struct pnp_dev *dev) | |
231 | { | |
232 | struct pci_dev *pdev = NULL; | |
53052feb | 233 | struct resource *res; |
0509ad5e BH |
234 | resource_size_t pnp_start, pnp_end, pci_start, pci_end; |
235 | int i, j; | |
236 | ||
237 | /* | |
238 | * Some BIOSes have PNP motherboard devices with resources that | |
239 | * partially overlap PCI BARs. The PNP system driver claims these | |
240 | * motherboard resources, which prevents the normal PCI driver from | |
241 | * requesting them later. | |
242 | * | |
243 | * This patch disables the PNP resources that conflict with PCI BARs | |
244 | * so they won't be claimed by the PNP system driver. | |
245 | */ | |
246 | for_each_pci_dev(pdev) { | |
247 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | |
b563cf59 | 248 | unsigned long type; |
999ed65a RH |
249 | |
250 | type = pci_resource_flags(pdev, i) & | |
251 | (IORESOURCE_IO | IORESOURCE_MEM); | |
252 | if (!type || pci_resource_len(pdev, i) == 0) | |
0509ad5e BH |
253 | continue; |
254 | ||
255 | pci_start = pci_resource_start(pdev, i); | |
256 | pci_end = pci_resource_end(pdev, i); | |
95ab3669 | 257 | for (j = 0; |
999ed65a | 258 | (res = pnp_get_resource(dev, type, j)); j++) { |
aee3ad81 | 259 | if (res->start == 0 && res->end == 0) |
0509ad5e BH |
260 | continue; |
261 | ||
95ab3669 BH |
262 | pnp_start = res->start; |
263 | pnp_end = res->end; | |
0509ad5e BH |
264 | |
265 | /* | |
266 | * If the PNP region doesn't overlap the PCI | |
267 | * region at all, there's no problem. | |
268 | */ | |
269 | if (pnp_end < pci_start || pnp_start > pci_end) | |
270 | continue; | |
271 | ||
272 | /* | |
273 | * If the PNP region completely encloses (or is | |
274 | * at least as large as) the PCI region, that's | |
275 | * also OK. For example, this happens when the | |
276 | * PNP device describes a bridge with PCI | |
277 | * behind it. | |
278 | */ | |
279 | if (pnp_start <= pci_start && | |
280 | pnp_end >= pci_end) | |
281 | continue; | |
282 | ||
283 | /* | |
284 | * Otherwise, the PNP region overlaps *part* of | |
285 | * the PCI region, and that might prevent a PCI | |
286 | * driver from requesting its resources. | |
287 | */ | |
c7dabef8 BH |
288 | dev_warn(&dev->dev, |
289 | "disabling %pR because it overlaps " | |
290 | "%s BAR %d %pR\n", res, | |
9a007b37 | 291 | pci_name(pdev), i, &pdev->resource[i]); |
4b34fe15 | 292 | res->flags |= IORESOURCE_DISABLED; |
0509ad5e BH |
293 | } |
294 | } | |
295 | } | |
296 | } | |
297 | ||
1da177e4 LT |
298 | /* |
299 | * PnP Quirks | |
300 | * Cards or devices that need some tweaking due to incomplete resource info | |
301 | */ | |
302 | ||
303 | static struct pnp_fixup pnp_fixups[] = { | |
304 | /* Soundblaster awe io port quirk */ | |
9dd78466 BH |
305 | {"CTL0021", quirk_awe32_resources}, |
306 | {"CTL0022", quirk_awe32_resources}, | |
307 | {"CTL0023", quirk_awe32_resources}, | |
1da177e4 | 308 | /* CMI 8330 interrupt and dma fix */ |
9dd78466 | 309 | {"@X@0001", quirk_cmi8330_resources}, |
1da177e4 | 310 | /* Soundblaster audio device io port range quirk */ |
9dd78466 BH |
311 | {"CTL0001", quirk_sb16audio_resources}, |
312 | {"CTL0031", quirk_sb16audio_resources}, | |
313 | {"CTL0041", quirk_sb16audio_resources}, | |
314 | {"CTL0042", quirk_sb16audio_resources}, | |
315 | {"CTL0043", quirk_sb16audio_resources}, | |
316 | {"CTL0044", quirk_sb16audio_resources}, | |
317 | {"CTL0045", quirk_sb16audio_resources}, | |
1f32ca31 | 318 | /* Add IRQ-optional MPU options */ |
3b73a223 | 319 | {"ADS7151", quirk_ad1815_mpu_resources}, |
1f32ca31 BH |
320 | {"ADS7181", quirk_add_irq_optional_dependent_sets}, |
321 | {"AZT0002", quirk_add_irq_optional_dependent_sets}, | |
3b73a223 | 322 | /* PnP resources that might overlap PCI BARs */ |
0509ad5e BH |
323 | {"PNP0c01", quirk_system_pci_resources}, |
324 | {"PNP0c02", quirk_system_pci_resources}, | |
9dd78466 | 325 | {""} |
1da177e4 LT |
326 | }; |
327 | ||
328 | void pnp_fixup_device(struct pnp_dev *dev) | |
329 | { | |
726a7a3d | 330 | struct pnp_fixup *f; |
a05d0781 | 331 | |
726a7a3d RH |
332 | for (f = pnp_fixups; *f->id; f++) { |
333 | if (!compare_pnp_id(dev->id, f->id)) | |
334 | continue; | |
2f53432c | 335 | pnp_dbg(&dev->dev, "%s: calling %pF\n", f->id, |
668b21de | 336 | f->quirk_function); |
726a7a3d | 337 | f->quirk_function(dev); |
1da177e4 LT |
338 | } |
339 | } |