aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/of_device.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@sunset.davemloft.net>2006-07-13 02:19:31 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-07-13 04:50:15 -0400
commita83f982313bc60dd3d87e50f8c88d6a169e8a5a0 (patch)
treeccbf0bd78ad540d870c295712fd60e49f9206d8a /arch/sparc/kernel/of_device.c
parent9bbd952e7f965757b5c913b6f98ad37a191137bd (diff)
[SPARC]: Fix OF register translations under sub-PCI busses.
There is an implicit assumption in the code that ranges will translate to something that can fit in 2 32-bit cells, or a 64-bit value. For certain kinds of things below PCI this isn't necessarily true. Here is what the relevant OF device hierarchy looks like for one of the serial controllers on an Ultra5: Node 0xf005f1e0 ranges: 00000000.00000000.00000000.000001fe.01000000.00000000.01000000 01000000.00000000.00000000.000001fe.02000000.00000000.01000000 02000000.00000000.00000000.000001ff.00000000.00000001.00000000 03000000.00000000.00000000.000001ff.00000000.00000001.00000000 device_type: 'pci' model: 'SUNW,sabre' Node 0xf005f9d4 device_type: 'pci' model: 'SUNW,simba' Node 0xf0060d24 ranges: 00000010.00000000 82010810.00000000.f0000000 01000000 00000014.00000000 82010814.00000000.f1000000 00800000 name: 'ebus' Node 0xf0062dac reg: 00000014.003083f8.00000008 --> 0x1ff.f13083f8 device_type: 'serial' name: 'su' So the correct translation here is: 1) Match "su" register to second ranges entry of 'ebus', which translates into a PCI triplet "82010814.00000000.f1000000" of size 00800000, which gives us "82010814.00000000.f13083f8". 2) Pass-through "SUNW,simba" since it lacks ranges property 3) Match "82010814.00000000.f13083f8" to third ranges property of PCI controller node 'SUNW,sabre', and we arrive at the final physical MMIO address of "0x1fff13083f8". Due to the 2-cell assumption, we couldn't translate to a PCI 3-cell value, and we couldn't perform a pass-thru on it either. It was easiest to just stop splitting the ranges application operation between two methods, ->map and ->translate, and just let ->map do all the work. That way it would work purely on 32-bit cell arrays instead of having to "return" some value like a u64. It's still not %100 correct because the out-of-range check is still done using the 64 least significant bits of the range and address. But it does work for all the cases I've thrown at it so far. Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc/kernel/of_device.c')
-rw-r--r--arch/sparc/kernel/of_device.c167
1 files changed, 109 insertions, 58 deletions
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c
index bbd3203b86af..5a2faad5d043 100644
--- a/arch/sparc/kernel/of_device.c
+++ b/arch/sparc/kernel/of_device.c
@@ -183,7 +183,7 @@ struct bus_type of_bus_type = {
183}; 183};
184EXPORT_SYMBOL(of_bus_type); 184EXPORT_SYMBOL(of_bus_type);
185 185
186static inline u64 of_read_addr(u32 *cell, int size) 186static inline u64 of_read_addr(const u32 *cell, int size)
187{ 187{
188 u64 r = 0; 188 u64 r = 0;
189 while (size--) 189 while (size--)
@@ -209,8 +209,8 @@ struct of_bus {
209 int (*match)(struct device_node *parent); 209 int (*match)(struct device_node *parent);
210 void (*count_cells)(struct device_node *child, 210 void (*count_cells)(struct device_node *child,
211 int *addrc, int *sizec); 211 int *addrc, int *sizec);
212 u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); 212 int (*map)(u32 *addr, const u32 *range,
213 int (*translate)(u32 *addr, u64 offset, int na); 213 int na, int ns, int pna);
214 unsigned int (*get_flags)(u32 *addr); 214 unsigned int (*get_flags)(u32 *addr);
215}; 215};
216 216
@@ -224,27 +224,49 @@ static void of_bus_default_count_cells(struct device_node *dev,
224 get_cells(dev, addrc, sizec); 224 get_cells(dev, addrc, sizec);
225} 225}
226 226
227static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) 227/* Make sure the least significant 64-bits are in-range. Even
228 * for 3 or 4 cell values it is a good enough approximation.
229 */
230static int of_out_of_range(const u32 *addr, const u32 *base,
231 const u32 *size, int na, int ns)
228{ 232{
229 u64 cp, s, da; 233 u64 a = of_read_addr(addr, na);
234 u64 b = of_read_addr(base, na);
235
236 if (a < b)
237 return 1;
230 238
231 cp = of_read_addr(range, na); 239 b += of_read_addr(size, ns);
232 s = of_read_addr(range + na + pna, ns); 240 if (a >= b)
233 da = of_read_addr(addr, na); 241 return 1;
234 242
235 if (da < cp || da >= (cp + s)) 243 return 0;
236 return OF_BAD_ADDR;
237 return da - cp;
238} 244}
239 245
240static int of_bus_default_translate(u32 *addr, u64 offset, int na) 246static int of_bus_default_map(u32 *addr, const u32 *range,
247 int na, int ns, int pna)
241{ 248{
242 u64 a = of_read_addr(addr, na); 249 u32 result[OF_MAX_ADDR_CELLS];
243 memset(addr, 0, na * 4); 250 int i;
244 a += offset; 251
245 if (na > 1) 252 if (ns > 2) {
246 addr[na - 2] = a >> 32; 253 printk("of_device: Cannot handle size cells (%d) > 2.", ns);
247 addr[na - 1] = a & 0xffffffffu; 254 return -EINVAL;
255 }
256
257 if (of_out_of_range(addr, range, range + na + pna, na, ns))
258 return -EINVAL;
259
260 /* Start with the parent range base. */
261 memcpy(result, range + na, pna * 4);
262
263 /* Add in the child address offset. */
264 for (i = 0; i < na; i++)
265 result[pna - 1 - i] +=
266 (addr[na - 1 - i] -
267 range[na - 1 - i]);
268
269 memcpy(addr, result, pna * 4);
248 270
249 return 0; 271 return 0;
250} 272}
@@ -254,14 +276,26 @@ static unsigned int of_bus_default_get_flags(u32 *addr)
254 return IORESOURCE_MEM; 276 return IORESOURCE_MEM;
255} 277}
256 278
257
258/* 279/*
259 * PCI bus specific translator 280 * PCI bus specific translator
260 */ 281 */
261 282
262static int of_bus_pci_match(struct device_node *np) 283static int of_bus_pci_match(struct device_node *np)
263{ 284{
264 return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex"); 285 if (!strcmp(np->type, "pci") || !strcmp(np->type, "pciex")) {
286 /* Do not do PCI specific frobbing if the
287 * PCI bridge lacks a ranges property. We
288 * want to pass it through up to the next
289 * parent as-is, not with the PCI translate
290 * method which chops off the top address cell.
291 */
292 if (!of_find_property(np, "ranges", NULL))
293 return 0;
294
295 return 1;
296 }
297
298 return 0;
265} 299}
266 300
267static void of_bus_pci_count_cells(struct device_node *np, 301static void of_bus_pci_count_cells(struct device_node *np,
@@ -273,27 +307,32 @@ static void of_bus_pci_count_cells(struct device_node *np,
273 *sizec = 2; 307 *sizec = 2;
274} 308}
275 309
276static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) 310static int of_bus_pci_map(u32 *addr, const u32 *range,
311 int na, int ns, int pna)
277{ 312{
278 u64 cp, s, da; 313 u32 result[OF_MAX_ADDR_CELLS];
314 int i;
279 315
280 /* Check address type match */ 316 /* Check address type match */
281 if ((addr[0] ^ range[0]) & 0x03000000) 317 if ((addr[0] ^ range[0]) & 0x03000000)
282 return OF_BAD_ADDR; 318 return -EINVAL;
283 319
284 /* Read address values, skipping high cell */ 320 if (of_out_of_range(addr + 1, range + 1, range + na + pna,
285 cp = of_read_addr(range + 1, na - 1); 321 na - 1, ns))
286 s = of_read_addr(range + na + pna, ns); 322 return -EINVAL;
287 da = of_read_addr(addr + 1, na - 1);
288 323
289 if (da < cp || da >= (cp + s)) 324 /* Start with the parent range base. */
290 return OF_BAD_ADDR; 325 memcpy(result, range + na, pna * 4);
291 return da - cp;
292}
293 326
294static int of_bus_pci_translate(u32 *addr, u64 offset, int na) 327 /* Add in the child address offset, skipping high cell. */
295{ 328 for (i = 0; i < na - 1; i++)
296 return of_bus_default_translate(addr + 1, offset, na - 1); 329 result[pna - 1 - i] +=
330 (addr[na - 1 - i] -
331 range[na - 1 - i]);
332
333 memcpy(addr, result, pna * 4);
334
335 return 0;
297} 336}
298 337
299static unsigned int of_bus_pci_get_flags(u32 *addr) 338static unsigned int of_bus_pci_get_flags(u32 *addr)
@@ -332,16 +371,11 @@ static void of_bus_sbus_count_cells(struct device_node *child,
332 *sizec = 1; 371 *sizec = 1;
333} 372}
334 373
335static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna) 374static int of_bus_sbus_map(u32 *addr, const u32 *range, int na, int ns, int pna)
336{ 375{
337 return of_bus_default_map(addr, range, na, ns, pna); 376 return of_bus_default_map(addr, range, na, ns, pna);
338} 377}
339 378
340static int of_bus_sbus_translate(u32 *addr, u64 offset, int na)
341{
342 return of_bus_default_translate(addr, offset, na);
343}
344
345static unsigned int of_bus_sbus_get_flags(u32 *addr) 379static unsigned int of_bus_sbus_get_flags(u32 *addr)
346{ 380{
347 return IORESOURCE_MEM; 381 return IORESOURCE_MEM;
@@ -360,7 +394,6 @@ static struct of_bus of_busses[] = {
360 .match = of_bus_pci_match, 394 .match = of_bus_pci_match,
361 .count_cells = of_bus_pci_count_cells, 395 .count_cells = of_bus_pci_count_cells,
362 .map = of_bus_pci_map, 396 .map = of_bus_pci_map,
363 .translate = of_bus_pci_translate,
364 .get_flags = of_bus_pci_get_flags, 397 .get_flags = of_bus_pci_get_flags,
365 }, 398 },
366 /* SBUS */ 399 /* SBUS */
@@ -370,7 +403,6 @@ static struct of_bus of_busses[] = {
370 .match = of_bus_sbus_match, 403 .match = of_bus_sbus_match,
371 .count_cells = of_bus_sbus_count_cells, 404 .count_cells = of_bus_sbus_count_cells,
372 .map = of_bus_sbus_map, 405 .map = of_bus_sbus_map,
373 .translate = of_bus_sbus_translate,
374 .get_flags = of_bus_sbus_get_flags, 406 .get_flags = of_bus_sbus_get_flags,
375 }, 407 },
376 /* Default */ 408 /* Default */
@@ -380,7 +412,6 @@ static struct of_bus of_busses[] = {
380 .match = NULL, 412 .match = NULL,
381 .count_cells = of_bus_default_count_cells, 413 .count_cells = of_bus_default_count_cells,
382 .map = of_bus_default_map, 414 .map = of_bus_default_map,
383 .translate = of_bus_default_translate,
384 .get_flags = of_bus_default_get_flags, 415 .get_flags = of_bus_default_get_flags,
385 }, 416 },
386}; 417};
@@ -405,33 +436,34 @@ static int __init build_one_resource(struct device_node *parent,
405 u32 *ranges; 436 u32 *ranges;
406 unsigned int rlen; 437 unsigned int rlen;
407 int rone; 438 int rone;
408 u64 offset = OF_BAD_ADDR;
409 439
410 ranges = of_get_property(parent, "ranges", &rlen); 440 ranges = of_get_property(parent, "ranges", &rlen);
411 if (ranges == NULL || rlen == 0) { 441 if (ranges == NULL || rlen == 0) {
412 offset = of_read_addr(addr, na); 442 u32 result[OF_MAX_ADDR_CELLS];
413 memset(addr, 0, pna * 4); 443 int i;
414 goto finish; 444
445 memset(result, 0, pna * 4);
446 for (i = 0; i < na; i++)
447 result[pna - 1 - i] =
448 addr[na - 1 - i];
449
450 memcpy(addr, result, pna * 4);
451 return 0;
415 } 452 }
416 453
417 /* Now walk through the ranges */ 454 /* Now walk through the ranges */
418 rlen /= 4; 455 rlen /= 4;
419 rone = na + pna + ns; 456 rone = na + pna + ns;
420 for (; rlen >= rone; rlen -= rone, ranges += rone) { 457 for (; rlen >= rone; rlen -= rone, ranges += rone) {
421 offset = bus->map(addr, ranges, na, ns, pna); 458 if (!bus->map(addr, ranges, na, ns, pna))
422 if (offset != OF_BAD_ADDR) 459 return 0;
423 break;
424 } 460 }
425 if (offset == OF_BAD_ADDR)
426 return 1;
427
428 memcpy(addr, ranges + na, 4 * pna);
429 461
430finish: 462 return 1;
431 /* Translate it into parent bus space */
432 return pbus->translate(addr, offset, pna);
433} 463}
434 464
465static int of_resource_verbose;
466
435static void __init build_device_resources(struct of_device *op, 467static void __init build_device_resources(struct of_device *op,
436 struct device *parent) 468 struct device *parent)
437{ 469{
@@ -497,7 +529,8 @@ static void __init build_device_resources(struct of_device *op,
497 pbus = of_match_bus(pp); 529 pbus = of_match_bus(pp);
498 pbus->count_cells(dp, &pna, &pns); 530 pbus->count_cells(dp, &pna, &pns);
499 531
500 if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna)) 532 if (build_one_resource(dp, bus, pbus, addr,
533 dna, dns, pna))
501 break; 534 break;
502 535
503 dna = pna; 536 dna = pna;
@@ -507,6 +540,12 @@ static void __init build_device_resources(struct of_device *op,
507 540
508 build_res: 541 build_res:
509 memset(r, 0, sizeof(*r)); 542 memset(r, 0, sizeof(*r));
543
544 if (of_resource_verbose)
545 printk("%s reg[%d] -> %llx\n",
546 op->node->full_name, index,
547 result);
548
510 if (result != OF_BAD_ADDR) { 549 if (result != OF_BAD_ADDR) {
511 r->start = result & 0xffffffff; 550 r->start = result & 0xffffffff;
512 r->end = result + size - 1; 551 r->end = result + size - 1;
@@ -643,6 +682,18 @@ static int __init of_bus_driver_init(void)
643 682
644postcore_initcall(of_bus_driver_init); 683postcore_initcall(of_bus_driver_init);
645 684
685static int __init of_debug(char *str)
686{
687 int val = 0;
688
689 get_option(&str, &val);
690 if (val & 1)
691 of_resource_verbose = 1;
692 return 1;
693}
694
695__setup("of_debug=", of_debug);
696
646int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus) 697int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus)
647{ 698{
648 /* initialize common driver fields */ 699 /* initialize common driver fields */