aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pnp/manager.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bjorn.helgaas@hp.com>2008-06-27 18:56:57 -0400
committerAndi Kleen <andi@basil.nowhere.org>2008-07-16 17:27:05 -0400
commitaee3ad815dd291a7193ab01da0f1a30c84d00061 (patch)
treeb0549b2a98ddfe6734e1e5f7cedd3958eec18503 /drivers/pnp/manager.c
parent20bfdbba7212d19613b93dcea93f26cb65af91fe (diff)
PNP: replace pnp_resource_table with dynamically allocated resources
PNP used to have a fixed-size pnp_resource_table for tracking the resources used by a device. This table often overflowed, so we've had to increase the table size, which wastes memory because most devices have very few resources. This patch replaces the table with a linked list of resources where the entries are allocated on demand. This removes messages like these: pnpacpi: exceeded the max number of IO resources 00:01: too many I/O port resources References: http://bugzilla.kernel.org/show_bug.cgi?id=9535 http://bugzilla.kernel.org/show_bug.cgi?id=9740 http://lkml.org/lkml/2007/11/30/110 This patch also changes the way PNP uses the IORESOURCE_UNSET, IORESOURCE_AUTO, and IORESOURCE_DISABLED flags. Prior to this patch, the pnp_resource_table entries used the flags like this: IORESOURCE_UNSET This table entry is unused and available for use. When this flag is set, we shouldn't look at anything else in the resource structure. This flag is set when a resource table entry is initialized. IORESOURCE_AUTO This resource was assigned automatically by pnp_assign_{io,mem,etc}(). This flag is set when a resource table entry is initialized and cleared whenever we discover a resource setting by reading an ISAPNP config register, parsing a PNPBIOS resource data stream, parsing an ACPI _CRS list, or interpreting a sysfs "set" command. Resources marked IORESOURCE_AUTO are reinitialized and marked as IORESOURCE_UNSET by pnp_clean_resource_table() in these cases: - before we attempt to assign resources automatically, - if we fail to assign resources automatically, - after disabling a device IORESOURCE_DISABLED Set by pnp_assign_{io,mem,etc}() when automatic assignment fails. Also set by PNPBIOS and PNPACPI for: - invalid IRQs or GSI registration failures - invalid DMA channels - I/O ports above 0x10000 - mem ranges with negative length After this patch, there is no pnp_resource_table, and the resource list entries use the flags like this: IORESOURCE_UNSET This flag is no longer used in PNP. Instead of keeping IORESOURCE_UNSET entries in the resource list, we remove entries from the list and free them. IORESOURCE_AUTO No change in meaning: it still means the resource was assigned automatically by pnp_assign_{port,mem,etc}(), but these functions now set the bit explicitly. We still "clean" a device's resource list in the same places, but rather than reinitializing IORESOURCE_AUTO entries, we just remove them from the list. Note that IORESOURCE_AUTO entries are always at the end of the list, so removing them doesn't reorder other list entries. This is because non-IORESOURCE_AUTO entries are added by the ISAPNP, PNPBIOS, or PNPACPI "get resources" methods and by the sysfs "set" command. In each of these cases, we completely free the resource list first. IORESOURCE_DISABLED In addition to the cases where we used to set this flag, ISAPNP now adds an IORESOURCE_DISABLED resource when it reads a configuration register with a "disabled" value. Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Signed-off-by: Len Brown <len.brown@intel.com> Signed-off-by: Andi Kleen <ak@linux.intel.com>
Diffstat (limited to 'drivers/pnp/manager.c')
-rw-r--r--drivers/pnp/manager.c220
1 files changed, 55 insertions, 165 deletions
diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c
index 90bd9cb65563..165b624081ad 100644
--- a/drivers/pnp/manager.c
+++ b/drivers/pnp/manager.c
@@ -19,40 +19,30 @@ DEFINE_MUTEX(pnp_res_mutex);
19 19
20static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx) 20static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
21{ 21{
22 struct pnp_resource *pnp_res; 22 struct resource *res, local_res;
23 struct resource *res;
24 23
25 pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IO, idx); 24 res = pnp_get_resource(dev, IORESOURCE_IO, idx);
26 if (!pnp_res) { 25 if (res) {
27 dev_err(&dev->dev, "too many I/O port resources\n");
28 /* pretend we were successful so at least the manager won't try again */
29 return 1;
30 }
31
32 res = &pnp_res->res;
33
34 /* check if this resource has been manually set, if so skip */
35 if (!(res->flags & IORESOURCE_AUTO)) {
36 dev_dbg(&dev->dev, " io %d already set to %#llx-%#llx " 26 dev_dbg(&dev->dev, " io %d already set to %#llx-%#llx "
37 "flags %#lx\n", idx, (unsigned long long) res->start, 27 "flags %#lx\n", idx, (unsigned long long) res->start,
38 (unsigned long long) res->end, res->flags); 28 (unsigned long long) res->end, res->flags);
39 return 1; 29 return 1;
40 } 30 }
41 31
42 /* set the initial values */ 32 res = &local_res;
43 res->flags |= rule->flags | IORESOURCE_IO; 33 res->flags = rule->flags | IORESOURCE_AUTO;
44 res->flags &= ~IORESOURCE_UNSET; 34 res->start = 0;
35 res->end = 0;
45 36
46 if (!rule->size) { 37 if (!rule->size) {
47 res->flags |= IORESOURCE_DISABLED; 38 res->flags |= IORESOURCE_DISABLED;
48 dev_dbg(&dev->dev, " io %d disabled\n", idx); 39 dev_dbg(&dev->dev, " io %d disabled\n", idx);
49 return 1; /* skip disabled resource requests */ 40 goto __add;
50 } 41 }
51 42
52 res->start = rule->min; 43 res->start = rule->min;
53 res->end = res->start + rule->size - 1; 44 res->end = res->start + rule->size - 1;
54 45
55 /* run through until pnp_check_port is happy */
56 while (!pnp_check_port(dev, res)) { 46 while (!pnp_check_port(dev, res)) {
57 res->start += rule->align; 47 res->start += rule->align;
58 res->end = res->start + rule->size - 1; 48 res->end = res->start + rule->size - 1;
@@ -61,38 +51,29 @@ static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
61 return 0; 51 return 0;
62 } 52 }
63 } 53 }
64 dev_dbg(&dev->dev, " assign io %d %#llx-%#llx\n", idx, 54
65 (unsigned long long) res->start, (unsigned long long) res->end); 55__add:
56 pnp_add_io_resource(dev, res->start, res->end, res->flags);
66 return 1; 57 return 1;
67} 58}
68 59
69static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx) 60static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
70{ 61{
71 struct pnp_resource *pnp_res; 62 struct resource *res, local_res;
72 struct resource *res;
73 63
74 pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_MEM, idx); 64 res = pnp_get_resource(dev, IORESOURCE_MEM, idx);
75 if (!pnp_res) { 65 if (res) {
76 dev_err(&dev->dev, "too many memory resources\n");
77 /* pretend we were successful so at least the manager won't try again */
78 return 1;
79 }
80
81 res = &pnp_res->res;
82
83 /* check if this resource has been manually set, if so skip */
84 if (!(res->flags & IORESOURCE_AUTO)) {
85 dev_dbg(&dev->dev, " mem %d already set to %#llx-%#llx " 66 dev_dbg(&dev->dev, " mem %d already set to %#llx-%#llx "
86 "flags %#lx\n", idx, (unsigned long long) res->start, 67 "flags %#lx\n", idx, (unsigned long long) res->start,
87 (unsigned long long) res->end, res->flags); 68 (unsigned long long) res->end, res->flags);
88 return 1; 69 return 1;
89 } 70 }
90 71
91 /* set the initial values */ 72 res = &local_res;
92 res->flags |= rule->flags | IORESOURCE_MEM; 73 res->flags = rule->flags | IORESOURCE_AUTO;
93 res->flags &= ~IORESOURCE_UNSET; 74 res->start = 0;
75 res->end = 0;
94 76
95 /* convert pnp flags to standard Linux flags */
96 if (!(rule->flags & IORESOURCE_MEM_WRITEABLE)) 77 if (!(rule->flags & IORESOURCE_MEM_WRITEABLE))
97 res->flags |= IORESOURCE_READONLY; 78 res->flags |= IORESOURCE_READONLY;
98 if (rule->flags & IORESOURCE_MEM_CACHEABLE) 79 if (rule->flags & IORESOURCE_MEM_CACHEABLE)
@@ -105,13 +86,12 @@ static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
105 if (!rule->size) { 86 if (!rule->size) {
106 res->flags |= IORESOURCE_DISABLED; 87 res->flags |= IORESOURCE_DISABLED;
107 dev_dbg(&dev->dev, " mem %d disabled\n", idx); 88 dev_dbg(&dev->dev, " mem %d disabled\n", idx);
108 return 1; /* skip disabled resource requests */ 89 goto __add;
109 } 90 }
110 91
111 res->start = rule->min; 92 res->start = rule->min;
112 res->end = res->start + rule->size - 1; 93 res->end = res->start + rule->size - 1;
113 94
114 /* run through until pnp_check_mem is happy */
115 while (!pnp_check_mem(dev, res)) { 95 while (!pnp_check_mem(dev, res)) {
116 res->start += rule->align; 96 res->start += rule->align;
117 res->end = res->start + rule->size - 1; 97 res->end = res->start + rule->size - 1;
@@ -120,15 +100,15 @@ static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
120 return 0; 100 return 0;
121 } 101 }
122 } 102 }
123 dev_dbg(&dev->dev, " assign mem %d %#llx-%#llx\n", idx, 103
124 (unsigned long long) res->start, (unsigned long long) res->end); 104__add:
105 pnp_add_mem_resource(dev, res->start, res->end, res->flags);
125 return 1; 106 return 1;
126} 107}
127 108
128static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx) 109static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
129{ 110{
130 struct pnp_resource *pnp_res; 111 struct resource *res, local_res;
131 struct resource *res;
132 int i; 112 int i;
133 113
134 /* IRQ priority: this table is good for i386 */ 114 /* IRQ priority: this table is good for i386 */
@@ -136,58 +116,48 @@ static int pnp_assign_irq(struct pnp_dev *dev, struct pnp_irq *rule, int idx)
136 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2 116 5, 10, 11, 12, 9, 14, 15, 7, 3, 4, 13, 0, 1, 6, 8, 2
137 }; 117 };
138 118
139 pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_IRQ, idx); 119 res = pnp_get_resource(dev, IORESOURCE_IRQ, idx);
140 if (!pnp_res) { 120 if (res) {
141 dev_err(&dev->dev, "too many IRQ resources\n");
142 /* pretend we were successful so at least the manager won't try again */
143 return 1;
144 }
145
146 res = &pnp_res->res;
147
148 /* check if this resource has been manually set, if so skip */
149 if (!(res->flags & IORESOURCE_AUTO)) {
150 dev_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n", 121 dev_dbg(&dev->dev, " irq %d already set to %d flags %#lx\n",
151 idx, (int) res->start, res->flags); 122 idx, (int) res->start, res->flags);
152 return 1; 123 return 1;
153 } 124 }
154 125
155 /* set the initial values */ 126 res = &local_res;
156 res->flags |= rule->flags | IORESOURCE_IRQ; 127 res->flags = rule->flags | IORESOURCE_AUTO;
157 res->flags &= ~IORESOURCE_UNSET; 128 res->start = -1;
129 res->end = -1;
158 130
159 if (bitmap_empty(rule->map, PNP_IRQ_NR)) { 131 if (bitmap_empty(rule->map, PNP_IRQ_NR)) {
160 res->flags |= IORESOURCE_DISABLED; 132 res->flags |= IORESOURCE_DISABLED;
161 dev_dbg(&dev->dev, " irq %d disabled\n", idx); 133 dev_dbg(&dev->dev, " irq %d disabled\n", idx);
162 return 1; /* skip disabled resource requests */ 134 goto __add;
163 } 135 }
164 136
165 /* TBD: need check for >16 IRQ */ 137 /* TBD: need check for >16 IRQ */
166 res->start = find_next_bit(rule->map, PNP_IRQ_NR, 16); 138 res->start = find_next_bit(rule->map, PNP_IRQ_NR, 16);
167 if (res->start < PNP_IRQ_NR) { 139 if (res->start < PNP_IRQ_NR) {
168 res->end = res->start; 140 res->end = res->start;
169 dev_dbg(&dev->dev, " assign irq %d %d\n", idx, 141 goto __add;
170 (int) res->start);
171 return 1;
172 } 142 }
173 for (i = 0; i < 16; i++) { 143 for (i = 0; i < 16; i++) {
174 if (test_bit(xtab[i], rule->map)) { 144 if (test_bit(xtab[i], rule->map)) {
175 res->start = res->end = xtab[i]; 145 res->start = res->end = xtab[i];
176 if (pnp_check_irq(dev, res)) { 146 if (pnp_check_irq(dev, res))
177 dev_dbg(&dev->dev, " assign irq %d %d\n", idx, 147 goto __add;
178 (int) res->start);
179 return 1;
180 }
181 } 148 }
182 } 149 }
183 dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx); 150 dev_dbg(&dev->dev, " couldn't assign irq %d\n", idx);
184 return 0; 151 return 0;
152
153__add:
154 pnp_add_irq_resource(dev, res->start, res->flags);
155 return 1;
185} 156}
186 157
187static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) 158static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
188{ 159{
189 struct pnp_resource *pnp_res; 160 struct resource *res, local_res;
190 struct resource *res;
191 int i; 161 int i;
192 162
193 /* DMA priority: this table is good for i386 */ 163 /* DMA priority: this table is good for i386 */
@@ -195,127 +165,47 @@ static void pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
195 1, 3, 5, 6, 7, 0, 2, 4 165 1, 3, 5, 6, 7, 0, 2, 4
196 }; 166 };
197 167
198 pnp_res = pnp_get_pnp_resource(dev, IORESOURCE_DMA, idx); 168 res = pnp_get_resource(dev, IORESOURCE_DMA, idx);
199 if (!pnp_res) { 169 if (res) {
200 dev_err(&dev->dev, "too many DMA resources\n");
201 return;
202 }
203
204 res = &pnp_res->res;
205
206 /* check if this resource has been manually set, if so skip */
207 if (!(res->flags & IORESOURCE_AUTO)) {
208 dev_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n", 170 dev_dbg(&dev->dev, " dma %d already set to %d flags %#lx\n",
209 idx, (int) res->start, res->flags); 171 idx, (int) res->start, res->flags);
210 return; 172 return;
211 } 173 }
212 174
213 /* set the initial values */ 175 res = &local_res;
214 res->flags |= rule->flags | IORESOURCE_DMA; 176 res->flags = rule->flags | IORESOURCE_AUTO;
215 res->flags &= ~IORESOURCE_UNSET; 177 res->start = -1;
178 res->end = -1;
216 179
217 for (i = 0; i < 8; i++) { 180 for (i = 0; i < 8; i++) {
218 if (rule->map & (1 << xtab[i])) { 181 if (rule->map & (1 << xtab[i])) {
219 res->start = res->end = xtab[i]; 182 res->start = res->end = xtab[i];
220 if (pnp_check_dma(dev, res)) { 183 if (pnp_check_dma(dev, res))
221 dev_dbg(&dev->dev, " assign dma %d %d\n", idx, 184 goto __add;
222 (int) res->start);
223 return;
224 }
225 } 185 }
226 } 186 }
227#ifdef MAX_DMA_CHANNELS 187#ifdef MAX_DMA_CHANNELS
228 res->start = res->end = MAX_DMA_CHANNELS; 188 res->start = res->end = MAX_DMA_CHANNELS;
229#endif 189#endif
230 res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; 190 res->flags |= IORESOURCE_DISABLED;
231 dev_dbg(&dev->dev, " disable dma %d\n", idx); 191 dev_dbg(&dev->dev, " disable dma %d\n", idx);
232}
233 192
234void pnp_init_resource(struct resource *res) 193__add:
235{ 194 pnp_add_dma_resource(dev, res->start, res->flags);
236 unsigned long type;
237
238 type = res->flags & (IORESOURCE_IO | IORESOURCE_MEM |
239 IORESOURCE_IRQ | IORESOURCE_DMA);
240
241 res->name = NULL;
242 res->flags = type | IORESOURCE_AUTO | IORESOURCE_UNSET;
243 if (type == IORESOURCE_IRQ || type == IORESOURCE_DMA) {
244 res->start = -1;
245 res->end = -1;
246 } else {
247 res->start = 0;
248 res->end = 0;
249 }
250} 195}
251 196
252/**
253 * pnp_init_resources - Resets a resource table to default values.
254 * @table: pointer to the desired resource table
255 */
256void pnp_init_resources(struct pnp_dev *dev) 197void pnp_init_resources(struct pnp_dev *dev)
257{ 198{
258 struct resource *res; 199 pnp_free_resources(dev);
259 int idx;
260
261 for (idx = 0; idx < PNP_MAX_IRQ; idx++) {
262 res = &dev->res->irq[idx].res;
263 res->flags = IORESOURCE_IRQ;
264 pnp_init_resource(res);
265 }
266 for (idx = 0; idx < PNP_MAX_DMA; idx++) {
267 res = &dev->res->dma[idx].res;
268 res->flags = IORESOURCE_DMA;
269 pnp_init_resource(res);
270 }
271 for (idx = 0; idx < PNP_MAX_PORT; idx++) {
272 res = &dev->res->port[idx].res;
273 res->flags = IORESOURCE_IO;
274 pnp_init_resource(res);
275 }
276 for (idx = 0; idx < PNP_MAX_MEM; idx++) {
277 res = &dev->res->mem[idx].res;
278 res->flags = IORESOURCE_MEM;
279 pnp_init_resource(res);
280 }
281} 200}
282 201
283/**
284 * pnp_clean_resources - clears resources that were not manually set
285 * @res: the resources to clean
286 */
287static void pnp_clean_resource_table(struct pnp_dev *dev) 202static void pnp_clean_resource_table(struct pnp_dev *dev)
288{ 203{
289 struct resource *res; 204 struct pnp_resource *pnp_res, *tmp;
290 int idx; 205
291 206 list_for_each_entry_safe(pnp_res, tmp, &dev->resources, list) {
292 for (idx = 0; idx < PNP_MAX_IRQ; idx++) { 207 if (pnp_res->res.flags & IORESOURCE_AUTO)
293 res = &dev->res->irq[idx].res; 208 pnp_free_resource(pnp_res);
294 if (res->flags & IORESOURCE_AUTO) {
295 res->flags = IORESOURCE_IRQ;
296 pnp_init_resource(res);
297 }
298 }
299 for (idx = 0; idx < PNP_MAX_DMA; idx++) {
300 res = &dev->res->dma[idx].res;
301 if (res->flags & IORESOURCE_AUTO) {
302 res->flags = IORESOURCE_DMA;
303 pnp_init_resource(res);
304 }
305 }
306 for (idx = 0; idx < PNP_MAX_PORT; idx++) {
307 res = &dev->res->port[idx].res;
308 if (res->flags & IORESOURCE_AUTO) {
309 res->flags = IORESOURCE_IO;
310 pnp_init_resource(res);
311 }
312 }
313 for (idx = 0; idx < PNP_MAX_MEM; idx++) {
314 res = &dev->res->mem[idx].res;
315 if (res->flags & IORESOURCE_AUTO) {
316 res->flags = IORESOURCE_MEM;
317 pnp_init_resource(res);
318 }
319 } 209 }
320} 210}
321 211