summaryrefslogtreecommitdiff
path: root/arch/sparc64/kernel/pci.c
diff options
context:
space:
mode:
authorDavid S. Miller <davem@ultra5.davemloft.net>2007-03-04 12:53:19 -0800
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:55:10 -0700
commit01f94c4a6ced476ce69b895426fc29bfc48c69bd (patch)
tree68866108644a1e90ae7688d2e35cb8462ab6a07c /arch/sparc64/kernel/pci.c
parenta378fd0ee8ea6af5dafd0ab3d634f22b926b5ac4 (diff)
[SPARC64]: Fix sabre pci controllers with new probing scheme.
The SIMBA APB bridge is strange, it is a PCI bridge but it lacks some standard OF properties, in particular it lacks a 'ranges' property. What you have to do is read the IO and MEM range registers in the APB bridge to determine the ranges handled by each bridge. So fill in the bus resources by doing that. Since we now handle this quirk in the generic PCI and OF device probing layers, we can flat out eliminate all of that code from the sabre pci controller driver. In fact we can thus eliminate completely another quirk of the sabre driver. It tried to make the two APB bridges look like PBMs but that makes zero sense now (and it's questionable whether it ever made sense). So now just use pbm_A and probe the whole PCI hierarchy using that as the root. This simplification allows many future cleanups to occur. Also, I've found yet another quirk that needs to be worked around while testing this. You can't use the 'class-code' OF firmware property, especially for IDE controllers. We have to read the value out of PCI config space or else we'll see the value the device was showing before it was programmed into native mode. I'm starting to think it might be wise to just read all of the values out of PCI config space instead of using the OF properties. :-/ Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64/kernel/pci.c')
-rw-r--r--arch/sparc64/kernel/pci.c80
1 files changed, 74 insertions, 6 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 425e883e7e3b..b63341c2a334 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -26,6 +26,7 @@
#include <asm/ebus.h>
#include <asm/isa.h>
#include <asm/prom.h>
+#include <asm/apb.h>
#include "pci_impl.h"
@@ -372,6 +373,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
struct dev_archdata *sd;
struct pci_dev *dev;
const char *type;
+ u32 class;
dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
@@ -409,7 +411,15 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
- dev->class = of_getintprop_default(node, "class-code", 0);
+
+ /* dev->class = of_getintprop_default(node, "class-code", 0); */
+ /* We can't actually use the firmware value, we have to read what
+ * is in the register right now. One reason is that in the case
+ * of IDE interfaces the firmware can sample the value before the
+ * the IDE interface is programmed into native mode.
+ */
+ pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+ dev->class = class >> 8;
printk(" class: 0x%x\n", dev->class);
@@ -440,6 +450,53 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
return dev;
}
+static void __init apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p)
+{
+ u32 idx, first, last;
+
+ first = 8;
+ last = 0;
+ for (idx = 0; idx < 8; idx++) {
+ if ((map & (1 << idx)) != 0) {
+ if (first > idx)
+ first = idx;
+ if (last < idx)
+ last = idx;
+ }
+ }
+
+ *first_p = first;
+ *last_p = last;
+}
+
+/* Cook up fake bus resources for SUNW,simba PCI bridges which lack
+ * a proper 'ranges' property.
+ */
+static void __init apb_fake_ranges(struct pci_dev *dev,
+ struct pci_bus *bus,
+ struct pci_pbm_info *pbm)
+{
+ struct resource *res;
+ u32 first, last;
+ u8 map;
+
+ pci_read_config_byte(dev, APB_IO_ADDRESS_MAP, &map);
+ apb_calc_first_last(map, &first, &last);
+ res = bus->resource[0];
+ res->start = (first << 21);
+ res->end = (last << 21) + ((1 << 21) - 1);
+ res->flags = IORESOURCE_IO;
+ pbm->parent->resource_adjust(dev, res, &pbm->io_space);
+
+ pci_read_config_byte(dev, APB_MEM_ADDRESS_MAP, &map);
+ apb_calc_first_last(map, &first, &last);
+ res = bus->resource[1];
+ res->start = (first << 21);
+ res->end = (last << 21) + ((1 << 21) - 1);
+ res->flags = IORESOURCE_MEM;
+ pbm->parent->resource_adjust(dev, res, &pbm->mem_space);
+}
+
static void __init pci_of_scan_bus(struct pci_pbm_info *pbm,
struct device_node *node,
struct pci_bus *bus);
@@ -452,7 +509,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
{
struct pci_bus *bus;
const u32 *busrange, *ranges;
- int len, i;
+ int len, i, simba;
struct resource *res;
unsigned int flags;
u64 size;
@@ -467,10 +524,16 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
return;
}
ranges = of_get_property(node, "ranges", &len);
+ simba = 0;
if (ranges == NULL) {
- printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
- node->full_name);
- return;
+ char *model = of_get_property(node, "model", NULL);
+ if (model && !strcmp(model, "SUNW,simba")) {
+ simba = 1;
+ } else {
+ printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n",
+ node->full_name);
+ return;
+ }
}
bus = pci_add_new_bus(dev->bus, dev, busrange[0]);
@@ -484,7 +547,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
bus->subordinate = busrange[1];
bus->bridge_ctl = 0;
- /* parse ranges property */
+ /* parse ranges property, or cook one up by hand for Simba */
/* PCI #address-cells == 3 and #size-cells == 2 always */
res = &dev->resource[PCI_BRIDGE_RESOURCES];
for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) {
@@ -492,6 +555,10 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
bus->resource[i] = res;
++res;
}
+ if (simba) {
+ apb_fake_ranges(dev, bus, pbm);
+ goto simba_cont;
+ }
i = 1;
for (; len >= 32; len -= 32, ranges += 8) {
struct resource *root;
@@ -529,6 +596,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm,
*/
pbm->parent->resource_adjust(dev, res, root);
}
+simba_cont:
sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus),
bus->number);
printk(" bus name: %s\n", bus->name);