diff options
Diffstat (limited to 'drivers/pnp/quirks.c')
-rw-r--r-- | drivers/pnp/quirks.c | 290 |
1 files changed, 155 insertions, 135 deletions
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c index e8515ce0d296..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,189 +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) { | 74 | list_for_each_entry(option, &dev->options, list) { |
70 | /* Valid irqs are 5, 7, 10 */ | 75 | if (!pnp_option_is_dependent(option)) |
71 | tmp = 0x04A0; | 76 | continue; |
72 | bitmap_copy(irq->map.bits, &tmp, 16); | ||
73 | } | ||
74 | 77 | ||
75 | for (dma = res->dma; dma; dma = dma->next) { | 78 | if (option->type == IORESOURCE_IRQ) { |
76 | /* Valid 8bit dma channels are 1,3 */ | 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; | ||
77 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == | 89 | if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == |
78 | IORESOURCE_DMA_8BIT) | 90 | IORESOURCE_DMA_8BIT && |
79 | 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 | } | ||
80 | } | 98 | } |
81 | } | 99 | } |
82 | dev_info(&dev->dev, "CMI8330 quirk - forced possible IRQs to 5, 7, 10 " | ||
83 | "and DMA channels to 1, 3\n"); | ||
84 | } | 100 | } |
85 | 101 | ||
86 | static void quirk_sb16audio_resources(struct pnp_dev *dev) | 102 | static void quirk_sb16audio_resources(struct pnp_dev *dev) |
87 | { | 103 | { |
104 | struct pnp_option *option; | ||
105 | unsigned int prev_option_flags = ~0, n = 0; | ||
88 | struct pnp_port *port; | 106 | struct pnp_port *port; |
89 | struct pnp_option *res = dev->dependent; | ||
90 | int changed = 0; | ||
91 | 107 | ||
92 | /* | 108 | /* |
93 | * 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. |
94 | * 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 |
95 | * auto-configured. | 111 | * auto-configured. |
96 | */ | 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 | } | ||
97 | 118 | ||
98 | for (; res; res = res->next) { | 119 | if (pnp_option_is_dependent(option) && |
99 | port = res->port; | 120 | option->type == IORESOURCE_IO) { |
100 | if (!port) | 121 | n++; |
101 | continue; | 122 | port = &option->u.port; |
102 | port = port->next; | 123 | if (n == 3 && port->min == port->max) { |
103 | if (!port) | 124 | port->max += 0x70; |
104 | continue; | 125 | dev_info(&dev->dev, "increased option port " |
105 | port = port->next; | 126 | "range from %#llx-%#llx to " |
106 | if (!port) | 127 | "%#llx-%#llx\n", |
107 | continue; | 128 | (unsigned long long) port->min, |
108 | if (port->min != port->max) | 129 | (unsigned long long) port->min, |
109 | continue; | 130 | (unsigned long long) port->min, |
110 | port->max += 0x70; | 131 | (unsigned long long) port->max); |
111 | changed = 1; | 132 | } |
133 | } | ||
112 | } | 134 | } |
113 | if (changed) | ||
114 | dev_info(&dev->dev, "SB audio device quirk - increased port range\n"); | ||
115 | } | 135 | } |
116 | 136 | ||
117 | 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) | ||
118 | { | 139 | { |
119 | struct pnp_option *head = NULL; | 140 | struct pnp_option *tail = NULL, *first_new_option = NULL; |
120 | struct pnp_option *prev = NULL; | 141 | struct pnp_option *option, *new_option; |
121 | struct pnp_option *res; | 142 | unsigned int flags; |
122 | |||
123 | /* | ||
124 | * Build a functional IRQ-optional variant of each MPU option. | ||
125 | */ | ||
126 | |||
127 | for (res = dev->dependent; res; res = res->next) { | ||
128 | struct pnp_option *curr; | ||
129 | struct pnp_port *port; | ||
130 | struct pnp_port *copy_port; | ||
131 | struct pnp_irq *irq; | ||
132 | struct pnp_irq *copy_irq; | ||
133 | |||
134 | port = res->port; | ||
135 | irq = res->irq; | ||
136 | if (!port || !irq) | ||
137 | continue; | ||
138 | 143 | ||
139 | copy_port = pnp_alloc(sizeof *copy_port); | 144 | list_for_each_entry(option, &dev->options, list) { |
140 | if (!copy_port) | 145 | if (pnp_option_is_dependent(option)) |
141 | break; | 146 | tail = option; |
142 | 147 | } | |
143 | copy_irq = pnp_alloc(sizeof *copy_irq); | 148 | if (!tail) { |
144 | if (!copy_irq) { | 149 | dev_err(&dev->dev, "no dependent option sets\n"); |
145 | kfree(copy_port); | 150 | return NULL; |
146 | break; | 151 | } |
147 | } | ||
148 | 152 | ||
149 | *copy_port = *port; | 153 | flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); |
150 | copy_port->next = NULL; | 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 | } | ||
151 | 164 | ||
152 | *copy_irq = *irq; | 165 | *new_option = *option; |
153 | copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL; | 166 | new_option->flags = flags; |
154 | copy_irq->next = NULL; | 167 | if (!first_new_option) |
168 | first_new_option = new_option; | ||
155 | 169 | ||
156 | curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); | 170 | list_add(&new_option->list, &tail->list); |
157 | if (!curr) { | 171 | tail = new_option; |
158 | kfree(copy_port); | ||
159 | kfree(copy_irq); | ||
160 | break; | ||
161 | } | 172 | } |
162 | curr->port = copy_port; | ||
163 | curr->irq = copy_irq; | ||
164 | |||
165 | if (prev) | ||
166 | prev->next = curr; | ||
167 | else | ||
168 | head = curr; | ||
169 | prev = curr; | ||
170 | } | 173 | } |
171 | if (head) | ||
172 | dev_info(&dev->dev, "adding IRQ-optional MPU options\n"); | ||
173 | 174 | ||
174 | return head; | 175 | return first_new_option; |
175 | } | 176 | } |
176 | 177 | ||
177 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) | 178 | |
179 | static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) | ||
178 | { | 180 | { |
179 | struct pnp_option *res; | 181 | struct pnp_option *new_option; |
182 | unsigned int num_sets, i, set; | ||
180 | struct pnp_irq *irq; | 183 | struct pnp_irq *irq; |
181 | 184 | ||
182 | res = dev->independent; | 185 | num_sets = dev->num_dependent_sets; |
183 | if (!res) | 186 | for (i = 0; i < num_sets; i++) { |
184 | return; | 187 | new_option = pnp_clone_dependent_set(dev, i); |
188 | if (!new_option) | ||
189 | return; | ||
185 | 190 | ||
186 | irq = res->irq; | 191 | set = pnp_option_set(new_option); |
187 | if (!irq || irq->next) | 192 | while (new_option && pnp_option_set(new_option) == set) { |
188 | return; | 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 | } | ||
189 | 201 | ||
190 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; | 202 | dev_info(&dev->dev, "added dependent option set %d (same as " |
191 | dev_info(&dev->dev, "made independent IRQ optional\n"); | 203 | "set %d except IRQ optional)\n", set, i); |
204 | } | ||
192 | } | 205 | } |
193 | 206 | ||
194 | static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) | 207 | static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) |
195 | { | 208 | { |
196 | 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 | } | ||
197 | 220 | ||
198 | res = dev->dependent; | 221 | if (independent_irqs != 1) |
199 | if (!res) | ||
200 | return; | 222 | return; |
201 | 223 | ||
202 | while (res->next) | 224 | irq->flags |= IORESOURCE_IRQ_OPTIONAL; |
203 | res = res->next; | 225 | dev_info(&dev->dev, "made independent IRQ optional\n"); |
204 | |||
205 | res->next = quirk_isapnp_mpu_options(dev); | ||
206 | } | 226 | } |
207 | 227 | ||
208 | #include <linux/pci.h> | 228 | #include <linux/pci.h> |
@@ -297,10 +317,10 @@ static struct pnp_fixup pnp_fixups[] = { | |||
297 | {"CTL0043", quirk_sb16audio_resources}, | 317 | {"CTL0043", quirk_sb16audio_resources}, |
298 | {"CTL0044", quirk_sb16audio_resources}, | 318 | {"CTL0044", quirk_sb16audio_resources}, |
299 | {"CTL0045", quirk_sb16audio_resources}, | 319 | {"CTL0045", quirk_sb16audio_resources}, |
300 | /* Add IRQ-less MPU options */ | 320 | /* Add IRQ-optional MPU options */ |
301 | {"ADS7151", quirk_ad1815_mpu_resources}, | 321 | {"ADS7151", quirk_ad1815_mpu_resources}, |
302 | {"ADS7181", quirk_isapnp_mpu_resources}, | 322 | {"ADS7181", quirk_add_irq_optional_dependent_sets}, |
303 | {"AZT0002", quirk_isapnp_mpu_resources}, | 323 | {"AZT0002", quirk_add_irq_optional_dependent_sets}, |
304 | /* PnP resources that might overlap PCI BARs */ | 324 | /* PnP resources that might overlap PCI BARs */ |
305 | {"PNP0c01", quirk_system_pci_resources}, | 325 | {"PNP0c01", quirk_system_pci_resources}, |
306 | {"PNP0c02", quirk_system_pci_resources}, | 326 | {"PNP0c02", quirk_system_pci_resources}, |