diff options
Diffstat (limited to 'drivers/pnp/quirks.c')
-rw-r--r-- | drivers/pnp/quirks.c | 307 |
1 files changed, 156 insertions, 151 deletions
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index 1ff3bb585ab2..55f55ed72dc7 100644 --- a/drivers/pnp/quirks.c +++ b/drivers/pnp/quirks.c | |||
@@ -5,6 +5,8 @@ | |||
5 | * when building up the resource structure for the first time. | 5 | * when building up the resource structure for the first time. |
6 | * | 6 | * |
7 | * Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk> | 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> | ||
8 | * | 10 | * |
9 | * Heavily based on PCI quirks handling which is | 11 | * Heavily based on PCI quirks handling which is |
10 | * | 12 | * |
@@ -20,203 +22,207 @@ | |||
20 | #include <linux/kallsyms.h> | 22 | #include <linux/kallsyms.h> |
21 | #include "base.h" | 23 | #include "base.h" |
22 | 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 | |||
23 | static void quirk_awe32_resources(struct pnp_dev *dev) | 49 | static void quirk_awe32_resources(struct pnp_dev *dev) |
24 | { | 50 | { |
25 | struct pnp_port *port, *port2, *port3; | 51 | struct pnp_option *option; |
26 | struct pnp_option *res = dev->dependent; | 52 | unsigned int set = ~0; |
27 | 53 | ||
28 | /* | 54 | /* |
29 | * Unfortunately the isapnp_add_port_resource is too tightly bound | 55 | * Add two extra ioport regions (at offset 0x400 and 0x800 from the |
30 | * into the PnP discovery sequence, and cannot be used. Link in the | 56 | * one given) to every dependent option set. |
31 | * two extra ports (at offset 0x400 and 0x800 from the one given) by | ||
32 | * hand. | ||
33 | */ | 57 | */ |
34 | for (; res; res = res->next) { | 58 | list_for_each_entry(option, &dev->options, list) { |
35 | port2 = pnp_alloc(sizeof(struct pnp_port)); | 59 | if (pnp_option_is_dependent(option) && |
36 | if (!port2) | 60 | pnp_option_set(option) != set) { |
37 | return; | 61 | set = pnp_option_set(option); |
38 | port3 = pnp_alloc(sizeof(struct pnp_port)); | 62 | quirk_awe32_add_ports(dev, option, 0x800); |
39 | if (!port3) { | 63 | quirk_awe32_add_ports(dev, option, 0x400); |
40 | kfree(port2); | ||
41 | return; | ||
42 | } | 64 | } |
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 | dev_info(&dev->dev, | ||
53 | "AWE32 quirk - added ioports 0x%lx and 0x%lx\n", | ||
54 | (unsigned long)port2->min, | ||
55 | (unsigned long)port3->min); | ||
56 | } | 65 | } |
57 | } | 66 | } |
58 | 67 | ||
59 | static void quirk_cmi8330_resources(struct pnp_dev *dev) | 68 | static void quirk_cmi8330_resources(struct pnp_dev *dev) |
60 | { | 69 | { |
61 | struct pnp_option *res = dev->dependent; | 70 | struct pnp_option *option; |
62 | unsigned long tmp; | 71 | struct pnp_irq *irq; |
63 | 72 | struct pnp_dma *dma; | |
64 | for (; res; res = res->next) { | ||
65 | |||
66 | struct pnp_irq *irq; | ||
67 | struct pnp_dma *dma; | ||
68 | 73 | ||
69 | for (irq = res->irq; irq; irq = irq->next) { // Valid irqs are 5, 7, 10 | 74 | list_for_each_entry(option, &dev->options, list) { |
70 | tmp = 0x04A0; | 75 | if (!pnp_option_is_dependent(option)) |
71 | bitmap_copy(irq->map, &tmp, 16); // 0000 0100 1010 0000 | 76 | continue; |
72 | } | ||
73 | 77 | ||
74 | for (dma = res->dma; dma; dma = dma->next) // Valid 8bit dma channels are 1,3 | 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; | ||
75 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == | 89 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == |
76 | IORESOURCE_DMA_8BIT) | 90 | IORESOURCE_DMA_8BIT && |
77 | dma->map = 0x000A; | 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 | } | ||
78 | } | 99 | } |
79 | dev_info(&dev->dev, "CMI8330 quirk - forced possible IRQs to 5, 7, 10 " | ||
80 | "and DMA channels to 1, 3\n"); | ||
81 | } | 100 | } |
82 | 101 | ||
83 | static void quirk_sb16audio_resources(struct pnp_dev *dev) | 102 | static void quirk_sb16audio_resources(struct pnp_dev *dev) |
84 | { | 103 | { |
104 | struct pnp_option *option; | ||
105 | unsigned int prev_option_flags = ~0, n = 0; | ||
85 | struct pnp_port *port; | 106 | struct pnp_port *port; |
86 | struct pnp_option *res = dev->dependent; | ||
87 | int changed = 0; | ||
88 | 107 | ||
89 | /* | 108 | /* |
90 | * The default range on the mpu port for these devices is 0x388-0x388. | 109 | * The default range on the OPL port for these devices is 0x388-0x388. |
91 | * Here we increase that range so that two such cards can be | 110 | * Here we increase that range so that two such cards can be |
92 | * auto-configured. | 111 | * auto-configured. |
93 | */ | 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 | } | ||
94 | 118 | ||
95 | for (; res; res = res->next) { | 119 | if (pnp_option_is_dependent(option) && |
96 | port = res->port; | 120 | option->type == IORESOURCE_IO) { |
97 | if (!port) | 121 | n++; |
98 | continue; | 122 | port = &option->u.port; |
99 | port = port->next; | 123 | if (n == 3 && port->min == port->max) { |
100 | if (!port) | 124 | port->max += 0x70; |
101 | continue; | 125 | dev_info(&dev->dev, "increased option port " |
102 | port = port->next; | 126 | "range from %#llx-%#llx to " |
103 | if (!port) | 127 | "%#llx-%#llx\n", |
104 | continue; | 128 | (unsigned long long) port->min, |
105 | if (port->min != port->max) | 129 | (unsigned long long) port->min, |
106 | continue; | 130 | (unsigned long long) port->min, |
107 | port->max += 0x70; | 131 | (unsigned long long) port->max); |
108 | changed = 1; | 132 | } |
133 | } | ||
109 | } | 134 | } |
110 | if (changed) | ||
111 | dev_info(&dev->dev, "SB audio device quirk - increased port range\n"); | ||
112 | } | 135 | } |
113 | 136 | ||
114 | static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) | 137 | static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, |
138 | unsigned int set) | ||
115 | { | 139 | { |
116 | struct pnp_option *head = NULL; | 140 | struct pnp_option *tail = NULL, *first_new_option = NULL; |
117 | struct pnp_option *prev = NULL; | 141 | struct pnp_option *option, *new_option; |
118 | struct pnp_option *res; | 142 | unsigned int flags; |
119 | |||
120 | /* | ||
121 | * Build a functional IRQ-less variant of each MPU option. | ||
122 | */ | ||
123 | |||
124 | for (res = dev->dependent; res; res = res->next) { | ||
125 | struct pnp_option *curr; | ||
126 | struct pnp_port *port; | ||
127 | struct pnp_port *copy; | ||
128 | 143 | ||
129 | port = res->port; | 144 | list_for_each_entry(option, &dev->options, list) { |
130 | if (!port || !res->irq) | 145 | if (pnp_option_is_dependent(option)) |
131 | continue; | 146 | tail = option; |
147 | } | ||
148 | if (!tail) { | ||
149 | dev_err(&dev->dev, "no dependent option sets\n"); | ||
150 | return NULL; | ||
151 | } | ||
132 | 152 | ||
133 | copy = pnp_alloc(sizeof *copy); | 153 | flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); |
134 | if (!copy) | 154 | list_for_each_entry(option, &dev->options, list) { |
135 | break; | 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 | } | ||
136 | 164 | ||
137 | copy->min = port->min; | 165 | *new_option = *option; |
138 | copy->max = port->max; | 166 | new_option->flags = flags; |
139 | copy->align = port->align; | 167 | if (!first_new_option) |
140 | copy->size = port->size; | 168 | first_new_option = new_option; |
141 | copy->flags = port->flags; | ||
142 | 169 | ||
143 | curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); | 170 | list_add(&new_option->list, &tail->list); |
144 | if (!curr) { | 171 | tail = new_option; |
145 | kfree(copy); | ||
146 | break; | ||
147 | } | 172 | } |
148 | curr->port = copy; | ||
149 | |||
150 | if (prev) | ||
151 | prev->next = curr; | ||
152 | else | ||
153 | head = curr; | ||
154 | prev = curr; | ||
155 | } | 173 | } |
156 | if (head) | ||
157 | dev_info(&dev->dev, "adding IRQ-less MPU options\n"); | ||
158 | 174 | ||
159 | return head; | 175 | return first_new_option; |
160 | } | 176 | } |
161 | 177 | ||
162 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) | 178 | |
179 | static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) | ||
163 | { | 180 | { |
164 | struct pnp_option *res; | 181 | struct pnp_option *new_option; |
182 | unsigned int num_sets, i, set; | ||
165 | struct pnp_irq *irq; | 183 | struct pnp_irq *irq; |
166 | 184 | ||
167 | /* | 185 | num_sets = dev->num_dependent_sets; |
168 | * Distribute the independent IRQ over the dependent options | 186 | for (i = 0; i < num_sets; i++) { |
169 | */ | 187 | new_option = pnp_clone_dependent_set(dev, i); |
170 | 188 | if (!new_option) | |
171 | res = dev->independent; | 189 | return; |
172 | if (!res) | ||
173 | return; | ||
174 | |||
175 | irq = res->irq; | ||
176 | if (!irq || irq->next) | ||
177 | return; | ||
178 | |||
179 | res = dev->dependent; | ||
180 | if (!res) | ||
181 | return; | ||
182 | |||
183 | while (1) { | ||
184 | struct pnp_irq *copy; | ||
185 | |||
186 | copy = pnp_alloc(sizeof *copy); | ||
187 | if (!copy) | ||
188 | break; | ||
189 | |||
190 | memcpy(copy->map, irq->map, sizeof copy->map); | ||
191 | copy->flags = irq->flags; | ||
192 | 190 | ||
193 | copy->next = res->irq; /* Yes, this is NULL */ | 191 | set = pnp_option_set(new_option); |
194 | res->irq = copy; | 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 | } | ||
195 | 201 | ||
196 | if (!res->next) | 202 | dev_info(&dev->dev, "added dependent option set %d (same as " |
197 | break; | 203 | "set %d except IRQ optional)\n", set, i); |
198 | res = res->next; | ||
199 | } | 204 | } |
200 | kfree(irq); | ||
201 | |||
202 | res->next = quirk_isapnp_mpu_options(dev); | ||
203 | |||
204 | res = dev->independent; | ||
205 | res->irq = NULL; | ||
206 | } | 205 | } |
207 | 206 | ||
208 | static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) | 207 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) |
209 | { | 208 | { |
210 | struct pnp_option *res; | 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 | } | ||
211 | 220 | ||
212 | res = dev->dependent; | 221 | if (independent_irqs != 1) |
213 | if (!res) | ||
214 | return; | 222 | return; |
215 | 223 | ||
216 | while (res->next) | 224 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; |
217 | res = res->next; | 225 | dev_info(&dev->dev, "made independent IRQ optional\n"); |
218 | |||
219 | res->next = quirk_isapnp_mpu_options(dev); | ||
220 | } | 226 | } |
221 | 227 | ||
222 | #include <linux/pci.h> | 228 | #include <linux/pci.h> |
@@ -248,8 +254,7 @@ static void quirk_system_pci_resources(struct pnp_dev *dev) | |||
248 | for (j = 0; | 254 | for (j = 0; |
249 | (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); | 255 | (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); |
250 | j++) { | 256 | j++) { |
251 | if (res->flags & IORESOURCE_UNSET || | 257 | if (res->start == 0 && res->end == 0) |
252 | (res->start == 0 && res->end == 0)) | ||
253 | continue; | 258 | continue; |
254 | 259 | ||
255 | pnp_start = res->start; | 260 | pnp_start = res->start; |
@@ -312,10 +317,10 @@ static struct pnp_fixup pnp_fixups[] = { | |||
312 | {"CTL0043", quirk_sb16audio_resources}, | 317 | {"CTL0043", quirk_sb16audio_resources}, |
313 | {"CTL0044", quirk_sb16audio_resources}, | 318 | {"CTL0044", quirk_sb16audio_resources}, |
314 | {"CTL0045", quirk_sb16audio_resources}, | 319 | {"CTL0045", quirk_sb16audio_resources}, |
315 | /* Add IRQ-less MPU options */ | 320 | /* Add IRQ-optional MPU options */ |
316 | {"ADS7151", quirk_ad1815_mpu_resources}, | 321 | {"ADS7151", quirk_ad1815_mpu_resources}, |
317 | {"ADS7181", quirk_isapnp_mpu_resources}, | 322 | {"ADS7181", quirk_add_irq_optional_dependent_sets}, |
318 | {"AZT0002", quirk_isapnp_mpu_resources}, | 323 | {"AZT0002", quirk_add_irq_optional_dependent_sets}, |
319 | /* PnP resources that might overlap PCI BARs */ | 324 | /* PnP resources that might overlap PCI BARs */ |
320 | {"PNP0c01", quirk_system_pci_resources}, | 325 | {"PNP0c01", quirk_system_pci_resources}, |
321 | {"PNP0c02", quirk_system_pci_resources}, | 326 | {"PNP0c02", quirk_system_pci_resources}, |