Root/
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 | * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. |
9 | * Bjorn Helgaas <bjorn.helgaas@hp.com> |
10 | * |
11 | * Heavily based on PCI quirks handling which is |
12 | * |
13 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> |
14 | */ |
15 | |
16 | #include <linux/types.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/string.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/pnp.h> |
21 | #include <linux/io.h> |
22 | #include <linux/kallsyms.h> |
23 | #include "base.h" |
24 | |
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 | |
49 | static void quirk_awe32_resources(struct pnp_dev *dev) |
50 | { |
51 | struct pnp_option *option; |
52 | unsigned int set = ~0; |
53 | |
54 | /* |
55 | * Add two extra ioport regions (at offset 0x400 and 0x800 from the |
56 | * one given) to every dependent option set. |
57 | */ |
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); |
64 | } |
65 | } |
66 | } |
67 | |
68 | static void quirk_cmi8330_resources(struct pnp_dev *dev) |
69 | { |
70 | struct pnp_option *option; |
71 | struct pnp_irq *irq; |
72 | struct pnp_dma *dma; |
73 | |
74 | list_for_each_entry(option, &dev->options, list) { |
75 | if (!pnp_option_is_dependent(option)) |
76 | continue; |
77 | |
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; |
89 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == |
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 | } |
98 | } |
99 | } |
100 | } |
101 | |
102 | static void quirk_sb16audio_resources(struct pnp_dev *dev) |
103 | { |
104 | struct pnp_option *option; |
105 | unsigned int prev_option_flags = ~0, n = 0; |
106 | struct pnp_port *port; |
107 | |
108 | /* |
109 | * The default range on the OPL port for these devices is 0x388-0x388. |
110 | * Here we increase that range so that two such cards can be |
111 | * auto-configured. |
112 | */ |
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 | } |
118 | |
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 | } |
134 | } |
135 | } |
136 | |
137 | static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, |
138 | unsigned int set) |
139 | { |
140 | struct pnp_option *tail = NULL, *first_new_option = NULL; |
141 | struct pnp_option *option, *new_option; |
142 | unsigned int flags; |
143 | |
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 | } |
152 | |
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 | } |
164 | |
165 | *new_option = *option; |
166 | new_option->flags = flags; |
167 | if (!first_new_option) |
168 | first_new_option = new_option; |
169 | |
170 | list_add(&new_option->list, &tail->list); |
171 | tail = new_option; |
172 | } |
173 | } |
174 | |
175 | return first_new_option; |
176 | } |
177 | |
178 | |
179 | static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) |
180 | { |
181 | struct pnp_option *new_option; |
182 | unsigned int num_sets, i, set; |
183 | struct pnp_irq *irq; |
184 | |
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; |
190 | |
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 | } |
201 | |
202 | dev_info(&dev->dev, "added dependent option set %d (same as " |
203 | "set %d except IRQ optional)\n", set, i); |
204 | } |
205 | } |
206 | |
207 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) |
208 | { |
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 | } |
220 | |
221 | if (independent_irqs != 1) |
222 | return; |
223 | |
224 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; |
225 | dev_info(&dev->dev, "made independent IRQ optional\n"); |
226 | } |
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; |
233 | struct resource *res; |
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++) { |
248 | unsigned long type; |
249 | |
250 | type = pci_resource_flags(pdev, i) & |
251 | (IORESOURCE_IO | IORESOURCE_MEM); |
252 | if (!type || pci_resource_len(pdev, i) == 0) |
253 | continue; |
254 | |
255 | pci_start = pci_resource_start(pdev, i); |
256 | pci_end = pci_resource_end(pdev, i); |
257 | for (j = 0; |
258 | (res = pnp_get_resource(dev, type, j)); j++) { |
259 | if (res->start == 0 && res->end == 0) |
260 | continue; |
261 | |
262 | pnp_start = res->start; |
263 | pnp_end = res->end; |
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 | */ |
288 | dev_warn(&dev->dev, |
289 | "disabling %pR because it overlaps " |
290 | "%s BAR %d %pR\n", res, |
291 | pci_name(pdev), i, &pdev->resource[i]); |
292 | res->flags |= IORESOURCE_DISABLED; |
293 | } |
294 | } |
295 | } |
296 | } |
297 | |
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 */ |
305 | {"CTL0021", quirk_awe32_resources}, |
306 | {"CTL0022", quirk_awe32_resources}, |
307 | {"CTL0023", quirk_awe32_resources}, |
308 | /* CMI 8330 interrupt and dma fix */ |
309 | {"@X@0001", quirk_cmi8330_resources}, |
310 | /* Soundblaster audio device io port range quirk */ |
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}, |
318 | /* Add IRQ-optional MPU options */ |
319 | {"ADS7151", quirk_ad1815_mpu_resources}, |
320 | {"ADS7181", quirk_add_irq_optional_dependent_sets}, |
321 | {"AZT0002", quirk_add_irq_optional_dependent_sets}, |
322 | /* PnP resources that might overlap PCI BARs */ |
323 | {"PNP0c01", quirk_system_pci_resources}, |
324 | {"PNP0c02", quirk_system_pci_resources}, |
325 | {""} |
326 | }; |
327 | |
328 | void pnp_fixup_device(struct pnp_dev *dev) |
329 | { |
330 | struct pnp_fixup *f; |
331 | |
332 | for (f = pnp_fixups; *f->id; f++) { |
333 | if (!compare_pnp_id(dev->id, f->id)) |
334 | continue; |
335 | pnp_dbg(&dev->dev, "%s: calling %pF\n", f->id, |
336 | f->quirk_function); |
337 | f->quirk_function(dev); |
338 | } |
339 | } |
340 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9