Merge branch 'master' into upstream
[powerpc.git] / arch / sparc64 / kernel / pci_sun4v.c
index 2b7a1f3..03ad4c0 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/pstate.h>
 #include <asm/oplib.h>
 #include <asm/hypervisor.h>
+#include <asm/prom.h>
 
 #include "pci_impl.h"
 #include "iommu_common.h"
@@ -599,18 +600,130 @@ struct pci_iommu_ops pci_sun4v_iommu_ops = {
 
 /* SUN4V PCI configuration space accessors. */
 
-static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func)
+struct pdev_entry {
+       struct pdev_entry       *next;
+       u32                     devhandle;
+       unsigned int            bus;
+       unsigned int            device;
+       unsigned int            func;
+};
+
+#define PDEV_HTAB_SIZE 16
+#define PDEV_HTAB_MASK (PDEV_HTAB_SIZE - 1)
+static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE];
+
+static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
 {
-       if (bus == pbm->pci_first_busno) {
-               if (device == 0 && func == 0)
-                       return 0;
-               return 1;
+       unsigned int val;
+
+       val = (devhandle ^ (devhandle >> 4));
+       val ^= bus;
+       val ^= device;
+       val ^= func;
+
+       return val & PDEV_HTAB_MASK;
+}
+
+static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
+{
+       struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL);
+       struct pdev_entry **slot;
+
+       if (!p)
+               return -ENOMEM;
+
+       slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
+       p->next = *slot;
+       *slot = p;
+
+       p->devhandle = devhandle;
+       p->bus = bus;
+       p->device = device;
+       p->func = func;
+
+       return 0;
+}
+
+/* Recursively descend into the OBP device tree, rooted at toplevel_node,
+ * looking for a PCI device matching bus and devfn.
+ */
+static int obp_find(struct device_node *toplevel_node, unsigned int bus, unsigned int devfn)
+{
+       toplevel_node = toplevel_node->child;
+
+       while (toplevel_node != NULL) {
+               struct linux_prom_pci_registers *regs;
+               struct property *prop;
+               int ret;
+
+               ret = obp_find(toplevel_node, bus, devfn);
+               if (ret != 0)
+                       return ret;
+
+               prop = of_find_property(toplevel_node, "reg", NULL);
+               if (!prop)
+                       goto next_sibling;
+
+               regs = prop->value;
+               if (((regs->phys_hi >> 16) & 0xff) == bus &&
+                   ((regs->phys_hi >> 8) & 0xff) == devfn)
+                       break;
+
+       next_sibling:
+               toplevel_node = toplevel_node->sibling;
+       }
+
+       return toplevel_node != NULL;
+}
+
+static int pdev_htab_populate(struct pci_pbm_info *pbm)
+{
+       u32 devhandle = pbm->devhandle;
+       unsigned int bus;
+
+       for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) {
+               unsigned int devfn;
+
+               for (devfn = 0; devfn < 256; devfn++) {
+                       unsigned int device = PCI_SLOT(devfn);
+                       unsigned int func = PCI_FUNC(devfn);
+
+                       if (obp_find(pbm->prom_node, bus, devfn)) {
+                               int err = pdev_htab_add(devhandle, bus,
+                                                       device, func);
+                               if (err)
+                                       return err;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func)
+{
+       struct pdev_entry *p;
+
+       p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)];
+       while (p) {
+               if (p->devhandle == devhandle &&
+                   p->bus == bus &&
+                   p->device == device &&
+                   p->func == func)
+                       break;
+
+               p = p->next;
        }
 
+       return p;
+}
+
+static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func)
+{
        if (bus < pbm->pci_first_busno ||
            bus > pbm->pci_last_busno)
                return 1;
-       return 0;
+       return pdev_find(pbm->devhandle, bus, device, func) == NULL;
 }
 
 static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
@@ -701,8 +814,7 @@ static void pbm_scan_bus(struct pci_controller_info *p,
        pci_fixup_host_bridge_self(pbm->pci_bus);
        pbm->pci_bus->self->sysdata = cookie;
 #endif
-       pci_fill_in_pbm_cookies(pbm->pci_bus, pbm,
-                               pbm->prom_node);
+       pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node);
        pci_record_assignments(pbm, pbm->pci_bus);
        pci_assign_unassigned(pbm, pbm->pci_bus);
        pci_fixup_irq(pbm, pbm->pci_bus);
@@ -712,15 +824,18 @@ static void pbm_scan_bus(struct pci_controller_info *p,
 
 static void pci_sun4v_scan_bus(struct pci_controller_info *p)
 {
-       if (p->pbm_A.prom_node) {
-               p->pbm_A.is_66mhz_capable =
-                       prom_getbool(p->pbm_A.prom_node, "66mhz-capable");
+       struct property *prop;
+       struct device_node *dp;
+
+       if ((dp = p->pbm_A.prom_node) != NULL) {
+               prop = of_find_property(dp, "66mhz-capable", NULL);
+               p->pbm_A.is_66mhz_capable = (prop != NULL);
 
                pbm_scan_bus(p, &p->pbm_A);
        }
-       if (p->pbm_B.prom_node) {
-               p->pbm_B.is_66mhz_capable =
-                       prom_getbool(p->pbm_B.prom_node, "66mhz-capable");
+       if ((dp = p->pbm_B.prom_node) != NULL) {
+               prop = of_find_property(dp, "66mhz-capable", NULL);
+               p->pbm_B.is_66mhz_capable = (prop != NULL);
 
                pbm_scan_bus(p, &p->pbm_B);
        }
@@ -728,45 +843,6 @@ static void pci_sun4v_scan_bus(struct pci_controller_info *p)
        /* XXX register error interrupt handlers XXX */
 }
 
-static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm,
-                                       struct pci_dev *pdev,
-                                       unsigned int devino)
-{
-       u32 devhandle = pbm->devhandle;
-       int pil;
-
-       pil = 5;
-       if (pdev) {
-               switch ((pdev->class >> 16) & 0xff) {
-               case PCI_BASE_CLASS_STORAGE:
-                       pil = 5;
-                       break;
-
-               case PCI_BASE_CLASS_NETWORK:
-                       pil = 6;
-                       break;
-
-               case PCI_BASE_CLASS_DISPLAY:
-                       pil = 9;
-                       break;
-
-               case PCI_BASE_CLASS_MULTIMEDIA:
-               case PCI_BASE_CLASS_MEMORY:
-               case PCI_BASE_CLASS_BRIDGE:
-               case PCI_BASE_CLASS_SERIAL:
-                       pil = 10;
-                       break;
-
-               default:
-                       pil = 5;
-                       break;
-               };
-       }
-       BUG_ON(PIL_RESERVED(pil));
-
-       return sun4v_build_irq(devhandle, devino, pil, IBF_PCI);
-}
-
 static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource)
 {
        struct pcidev_cookie *pcp = pdev->sysdata;
@@ -902,8 +978,13 @@ static unsigned long probe_existing_entries(struct pci_pbm_info *pbm,
                                             HV_PCI_TSBID(0, i),
                                             &io_attrs, &ra);
                if (ret == HV_EOK) {
-                       cnt++;
-                       __set_bit(i, arena->map);
+                       if (page_in_phys_avail(ra)) {
+                               pci_sun4v_iommu_demap(devhandle,
+                                                     HV_PCI_TSBID(0, i), 1);
+                       } else {
+                               cnt++;
+                               __set_bit(i, arena->map);
+                       }
                }
        }
 
@@ -913,13 +994,18 @@ static unsigned long probe_existing_entries(struct pci_pbm_info *pbm,
 static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
 {
        struct pci_iommu *iommu = pbm->iommu;
+       struct property *prop;
        unsigned long num_tsb_entries, sz;
        u32 vdma[2], dma_mask, dma_offset;
-       int err, tsbsize;
+       int tsbsize;
+
+       prop = of_find_property(pbm->prom_node, "virtual-dma", NULL);
+       if (prop) {
+               u32 *val = prop->value;
 
-       err = prom_getproperty(pbm->prom_node, "virtual-dma",
-                              (char *)&vdma[0], sizeof(vdma));
-       if (err == 0 || err == -1) {
+               vdma[0] = val[0];
+               vdma[1] = val[1];
+       } else {
                /* No property, use default values. */
                vdma[0] = 0x80000000;
                vdma[1] = 0x80000000;
@@ -971,34 +1057,30 @@ static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm)
        iommu->arena.limit = num_tsb_entries;
 
        sz = probe_existing_entries(pbm, iommu);
-
-       printk("%s: TSB entries [%lu], existing mapings [%lu]\n",
-              pbm->name, num_tsb_entries, sz);
+       if (sz)
+               printk("%s: Imported %lu TSB entries from OBP\n",
+                      pbm->name, sz);
 }
 
 static void pci_sun4v_get_bus_range(struct pci_pbm_info *pbm)
 {
-       unsigned int busrange[2];
-       int prom_node = pbm->prom_node;
-       int err;
-
-       err = prom_getproperty(prom_node, "bus-range",
-                              (char *)&busrange[0],
-                              sizeof(busrange));
-       if (err == 0 || err == -1) {
-               prom_printf("%s: Fatal error, no bus-range.\n", pbm->name);
-               prom_halt();
-       }
+       struct property *prop;
+       unsigned int *busrange;
+
+       prop = of_find_property(pbm->prom_node, "bus-range", NULL);
+
+       busrange = prop->value;
 
        pbm->pci_first_busno = busrange[0];
        pbm->pci_last_busno = busrange[1];
 
 }
 
-static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32 devhandle)
+static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle)
 {
        struct pci_pbm_info *pbm;
-       int err, i;
+       struct property *prop;
+       int len, i;
 
        if (devhandle & 0x40)
                pbm = &p->pbm_B;
@@ -1006,32 +1088,19 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32
                pbm = &p->pbm_A;
 
        pbm->parent = p;
-       pbm->prom_node = prom_node;
+       pbm->prom_node = dp;
        pbm->pci_first_slot = 1;
 
        pbm->devhandle = devhandle;
 
-       sprintf(pbm->name, "SUN4V-PCI%d PBM%c",
-               p->index, (pbm == &p->pbm_A ? 'A' : 'B'));
-
-       printk("%s: devhandle[%x] prom_node[%x:%x]\n",
-              pbm->name, pbm->devhandle,
-              pbm->prom_node, prom_getchild(pbm->prom_node));
-
-       prom_getstring(prom_node, "name",
-                      pbm->prom_name, sizeof(pbm->prom_name));
+       pbm->name = dp->full_name;
 
-       err = prom_getproperty(prom_node, "ranges",
-                              (char *) pbm->pbm_ranges,
-                              sizeof(pbm->pbm_ranges));
-       if (err == 0 || err == -1) {
-               prom_printf("%s: Fatal error, no ranges property.\n",
-                           pbm->name);
-               prom_halt();
-       }
+       printk("%s: SUN4V PCI Bus Module\n", pbm->name);
 
+       prop = of_find_property(dp, "ranges", &len);
+       pbm->pbm_ranges = prop->value;
        pbm->num_pbm_ranges =
-               (err / sizeof(struct linux_prom_pci_ranges));
+               (len / sizeof(struct linux_prom_pci_ranges));
 
        /* Mask out the top 8 bits of the ranges, leaving the real
         * physical address.
@@ -1042,39 +1111,33 @@ static void pci_sun4v_pbm_init(struct pci_controller_info *p, int prom_node, u32
        pci_sun4v_determine_mem_io_space(pbm);
        pbm_register_toplevel_resources(p, pbm);
 
-       err = prom_getproperty(prom_node, "interrupt-map",
-                              (char *)pbm->pbm_intmap,
-                              sizeof(pbm->pbm_intmap));
-       if (err == 0 || err == -1) {
-               prom_printf("%s: Fatal error, no interrupt-map property.\n",
-                           pbm->name);
-               prom_halt();
-       }
+       prop = of_find_property(dp, "interrupt-map", &len);
+       pbm->pbm_intmap = prop->value;
+       pbm->num_pbm_intmap =
+               (len / sizeof(struct linux_prom_pci_intmap));
 
-       pbm->num_pbm_intmap = (err / sizeof(struct linux_prom_pci_intmap));
-       err = prom_getproperty(prom_node, "interrupt-map-mask",
-                              (char *)&pbm->pbm_intmask,
-                              sizeof(pbm->pbm_intmask));
-       if (err == 0 || err == -1) {
-               prom_printf("%s: Fatal error, no interrupt-map-mask.\n",
-                           pbm->name);
-               prom_halt();
-       }
+       prop = of_find_property(dp, "interrupt-map-mask", NULL);
+       pbm->pbm_intmask = prop->value;
 
        pci_sun4v_get_bus_range(pbm);
        pci_sun4v_iommu_init(pbm);
+
+       pdev_htab_populate(pbm);
 }
 
-void sun4v_pci_init(int node, char *model_name)
+void sun4v_pci_init(struct device_node *dp, char *model_name)
 {
        struct pci_controller_info *p;
        struct pci_iommu *iommu;
-       struct linux_prom64_registers regs;
+       struct property *prop;
+       struct linux_prom64_registers *regs;
        u32 devhandle;
        int i;
 
-       prom_getproperty(node, "reg", (char *)&regs, sizeof(regs));
-       devhandle = (regs.phys_addr >> 32UL) & 0x0fffffff;
+       prop = of_find_property(dp, "reg", NULL);
+       regs = prop->value;
+
+       devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff;
 
        for (p = pci_controller_root; p; p = p->next) {
                struct pci_pbm_info *pbm;
@@ -1087,7 +1150,7 @@ void sun4v_pci_init(int node, char *model_name)
                       &p->pbm_B);
 
                if (pbm->devhandle == (devhandle ^ 0x40)) {
-                       pci_sun4v_pbm_init(p, node, devhandle);
+                       pci_sun4v_pbm_init(p, dp, devhandle);
                        return;
                }
        }
@@ -1128,7 +1191,6 @@ void sun4v_pci_init(int node, char *model_name)
        p->pbms_same_domain = 0;
 
        p->scan_bus = pci_sun4v_scan_bus;
-       p->irq_build = pci_sun4v_irq_build;
        p->base_address_update = pci_sun4v_base_address_update;
        p->resource_adjust = pci_sun4v_resource_adjust;
        p->pci_ops = &pci_sun4v_ops;
@@ -1138,7 +1200,7 @@ void sun4v_pci_init(int node, char *model_name)
         */
        pci_memspace_mask = 0x7fffffffUL;
 
-       pci_sun4v_pbm_init(p, node, devhandle);
+       pci_sun4v_pbm_init(p, dp, devhandle);
        return;
 
 fatal_memory_error: