aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pnp/quirks.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bjorn.helgaas@hp.com>2008-06-27 18:57:17 -0400
committerAndi Kleen <andi@basil.nowhere.org>2008-07-16 17:27:07 -0400
commit1f32ca31e7409d37c1b25e5f81840fb184380cdf (patch)
treee587c85b46b04dbbb5987e2a4986ab174f3bd6fa /drivers/pnp/quirks.c
parentbbe413b4fc7f791248c7ee00ce7b3778491a3700 (diff)
PNP: convert resource options to single linked list
ISAPNP, PNPBIOS, and ACPI describe the "possible resource settings" of a device, i.e., the possibilities an OS bus driver has when it assigns I/O port, MMIO, and other resources to the device. PNP used to maintain this "possible resource setting" information in one independent option structure and a list of dependent option structures for each device. Each of these option structures had lists of I/O, memory, IRQ, and DMA resources, for example: dev independent options ind-io0 -> ind-io1 ... ind-mem0 -> ind-mem1 ... ... dependent option set 0 dep0-io0 -> dep0-io1 ... dep0-mem0 -> dep0-mem1 ... ... dependent option set 1 dep1-io0 -> dep1-io1 ... dep1-mem0 -> dep1-mem1 ... ... ... This data structure was designed for ISAPNP, where the OS configures device resource settings by writing directly to configuration registers. The OS can write the registers in arbitrary order much like it writes PCI BARs. However, for PNPBIOS and ACPI devices, the OS uses firmware interfaces that perform device configuration, and it is important to pass the desired settings to those interfaces in the correct order. The OS learns the correct order by using firmware interfaces that return the "current resource settings" and "possible resource settings," but the option structures above doesn't store the ordering information. This patch replaces the independent and dependent lists with a single list of options. For example, a device might have possible resource settings like this: dev options ind-io0 -> dep0-io0 -> dep1->io0 -> ind-io1 ... All the possible settings are in the same list, in the order they come from the firmware "possible resource settings" list. Each entry is tagged with an independent/dependent flag. Dependent entries also have a "set number" and an optional priority value. All dependent entries must be assigned from the same set. For example, the OS can use all the entries from dependent set 0, or all the entries from dependent set 1, but it cannot mix entries from set 0 with entries from set 1. Prior to this patch PNP didn't keep track of the order of this list, and it assigned all independent options first, then all dependent ones. Using the example above, that resulted in a "desired configuration" list like this: ind->io0 -> ind->io1 -> depN-io0 ... instead of the list the firmware expects, which looks like this: ind->io0 -> depN-io0 -> ind-io1 ... Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Andi Kleen <ak@linux.intel.com> Acked-by: Rene Herman <rene.herman@gmail.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/pnp/quirks.c')
-rw-r--r--drivers/pnp/quirks.c290
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
25static 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
23static void quirk_awe32_resources(struct pnp_dev *dev) 49static 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
59static void quirk_cmi8330_resources(struct pnp_dev *dev) 68static 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
86static void quirk_sb16audio_resources(struct pnp_dev *dev) 102static 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
117static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) 137static 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
177static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) 178
179static 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
194static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) 207static 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},