summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/sparc/kernel/ioport.c17
-rw-r--r--arch/sparc/kernel/of_device.c477
-rw-r--r--arch/sparc/kernel/prom.c30
-rw-r--r--arch/sparc/kernel/time.c109
-rw-r--r--arch/sparc64/kernel/auxio.c78
-rw-r--r--arch/sparc64/kernel/ebus.c150
-rw-r--r--arch/sparc64/kernel/irq.c4
-rw-r--r--arch/sparc64/kernel/isa.c101
-rw-r--r--arch/sparc64/kernel/of_device.c689
-rw-r--r--arch/sparc64/kernel/pci.c8
-rw-r--r--arch/sparc64/kernel/pci_common.c291
-rw-r--r--arch/sparc64/kernel/pci_psycho.c165
-rw-r--r--arch/sparc64/kernel/pci_sabre.c158
-rw-r--r--arch/sparc64/kernel/pci_schizo.c311
-rw-r--r--arch/sparc64/kernel/pci_sun4v.c10
-rw-r--r--arch/sparc64/kernel/power.c59
-rw-r--r--arch/sparc64/kernel/prom.c787
-rw-r--r--arch/sparc64/kernel/sbus.c6
-rw-r--r--arch/sparc64/kernel/starfire.c4
-rw-r--r--arch/sparc64/kernel/time.c258
-rw-r--r--arch/sparc64/kernel/unaligned.c4
21 files changed, 2299 insertions, 1417 deletions
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index 79d177149fdb..8654b446ac9e 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -26,6 +26,7 @@
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -40,6 +41,7 @@
#include <asm/vaddrs.h>
#include <asm/oplib.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/sbus.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
@@ -143,6 +145,21 @@ void __iomem *sbus_ioremap(struct resource *phyres, unsigned long offset,
phyres->start + offset, size, name);
}
+void __iomem *of_ioremap(struct resource *res, unsigned long offset,
+ unsigned long size, char *name)
+{
+ return _sparc_alloc_io(res->flags & 0xF,
+ res->start + offset,
+ size, name);
+}
+EXPORT_SYMBOL(of_ioremap);
+
+void of_iounmap(void __iomem *base, unsigned long size)
+{
+ iounmap(base);
+}
+EXPORT_SYMBOL(of_iounmap);
+
/*
*/
void sbus_iounmap(volatile void __iomem *addr, unsigned long size)
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c
index 80a809478781..bc956c530376 100644
--- a/arch/sparc/kernel/of_device.c
+++ b/arch/sparc/kernel/of_device.c
@@ -129,6 +129,26 @@ static int of_device_resume(struct device * dev)
return error;
}
+static int node_match(struct device *dev, void *data)
+{
+ struct of_device *op = to_of_device(dev);
+ struct device_node *dp = data;
+
+ return (op->node == dp);
+}
+
+struct of_device *of_find_device_by_node(struct device_node *dp)
+{
+ struct device *dev = bus_find_device(&of_bus_type, NULL,
+ dp, node_match);
+
+ if (dev)
+ return to_of_device(dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_node);
+
#ifdef CONFIG_PCI
struct bus_type ebus_bus_type = {
.name = "ebus",
@@ -153,10 +173,459 @@ struct bus_type sbus_bus_type = {
EXPORT_SYMBOL(sbus_bus_type);
#endif
+struct bus_type of_bus_type = {
+ .name = "of",
+ .match = of_platform_bus_match,
+ .probe = of_device_probe,
+ .remove = of_device_remove,
+ .suspend = of_device_suspend,
+ .resume = of_device_resume,
+};
+EXPORT_SYMBOL(of_bus_type);
+
+static inline u64 of_read_addr(u32 *cell, int size)
+{
+ u64 r = 0;
+ while (size--)
+ r = (r << 32) | *(cell++);
+ return r;
+}
+
+static void __init get_cells(struct device_node *dp,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = of_n_addr_cells(dp);
+ if (sizec)
+ *sizec = of_n_size_cells(dp);
+}
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+
+struct of_bus {
+ const char *name;
+ const char *addr_prop_name;
+ int (*match)(struct device_node *parent);
+ void (*count_cells)(struct device_node *child,
+ int *addrc, int *sizec);
+ u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna);
+ int (*translate)(u32 *addr, u64 offset, int na);
+ unsigned int (*get_flags)(u32 *addr);
+};
+
+/*
+ * Default translator (generic bus)
+ */
+
+static void of_bus_default_count_cells(struct device_node *dev,
+ int *addrc, int *sizec)
+{
+ get_cells(dev, addrc, sizec);
+}
+
+static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ cp = of_read_addr(range, na);
+ s = of_read_addr(range + na + pna, ns);
+ da = of_read_addr(addr, na);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_default_translate(u32 *addr, u64 offset, int na)
+{
+ u64 a = of_read_addr(addr, na);
+ memset(addr, 0, na * 4);
+ a += offset;
+ if (na > 1)
+ addr[na - 2] = a >> 32;
+ addr[na - 1] = a & 0xffffffffu;
+
+ return 0;
+}
+
+static unsigned int of_bus_default_get_flags(u32 *addr)
+{
+ return IORESOURCE_MEM;
+}
+
+
+/*
+ * PCI bus specific translator
+ */
+
+static int of_bus_pci_match(struct device_node *np)
+{
+ return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex");
+}
+
+static void of_bus_pci_count_cells(struct device_node *np,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 3;
+ if (sizec)
+ *sizec = 2;
+}
+
+static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ /* Check address type match */
+ if ((addr[0] ^ range[0]) & 0x03000000)
+ return OF_BAD_ADDR;
+
+ /* Read address values, skipping high cell */
+ cp = of_read_addr(range + 1, na - 1);
+ s = of_read_addr(range + na + pna, ns);
+ da = of_read_addr(addr + 1, na - 1);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+static unsigned int of_bus_pci_get_flags(u32 *addr)
+{
+ unsigned int flags = 0;
+ u32 w = addr[0];
+
+ switch((w >> 24) & 0x03) {
+ case 0x01:
+ flags |= IORESOURCE_IO;
+ case 0x02: /* 32 bits */
+ case 0x03: /* 64 bits */
+ flags |= IORESOURCE_MEM;
+ }
+ if (w & 0x40000000)
+ flags |= IORESOURCE_PREFETCH;
+ return flags;
+}
+
+/*
+ * SBUS bus specific translator
+ */
+
+static int of_bus_sbus_match(struct device_node *np)
+{
+ return !strcmp(np->name, "sbus") ||
+ !strcmp(np->name, "sbi");
+}
+
+static void of_bus_sbus_count_cells(struct device_node *child,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 2;
+ if (sizec)
+ *sizec = 1;
+}
+
+static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ return of_bus_default_map(addr, range, na, ns, pna);
+}
+
+static int of_bus_sbus_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr, offset, na);
+}
+
+static unsigned int of_bus_sbus_get_flags(u32 *addr)
+{
+ return IORESOURCE_MEM;
+}
+
+
+/*
+ * Array of bus specific translators
+ */
+
+static struct of_bus of_busses[] = {
+ /* PCI */
+ {
+ .name = "pci",
+ .addr_prop_name = "assigned-addresses",
+ .match = of_bus_pci_match,
+ .count_cells = of_bus_pci_count_cells,
+ .map = of_bus_pci_map,
+ .translate = of_bus_pci_translate,
+ .get_flags = of_bus_pci_get_flags,
+ },
+ /* SBUS */
+ {
+ .name = "sbus",
+ .addr_prop_name = "reg",
+ .match = of_bus_sbus_match,
+ .count_cells = of_bus_sbus_count_cells,
+ .map = of_bus_sbus_map,
+ .translate = of_bus_sbus_translate,
+ .get_flags = of_bus_sbus_get_flags,
+ },
+ /* Default */
+ {
+ .name = "default",
+ .addr_prop_name = "reg",
+ .match = NULL,
+ .count_cells = of_bus_default_count_cells,
+ .map = of_bus_default_map,
+ .translate = of_bus_default_translate,
+ .get_flags = of_bus_default_get_flags,
+ },
+};
+
+static struct of_bus *of_match_bus(struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
+ if (!of_busses[i].match || of_busses[i].match(np))
+ return &of_busses[i];
+ BUG();
+ return NULL;
+}
+
+static int __init build_one_resource(struct device_node *parent,
+ struct of_bus *bus,
+ struct of_bus *pbus,
+ u32 *addr,
+ int na, int ns, int pna)
+{
+ u32 *ranges;
+ unsigned int rlen;
+ int rone;
+ u64 offset = OF_BAD_ADDR;
+
+ ranges = of_get_property(parent, "ranges", &rlen);
+ if (ranges == NULL || rlen == 0) {
+ offset = of_read_addr(addr, na);
+ memset(addr, 0, pna * 4);
+ goto finish;
+ }
+
+ /* Now walk through the ranges */
+ rlen /= 4;
+ rone = na + pna + ns;
+ for (; rlen >= rone; rlen -= rone, ranges += rone) {
+ offset = bus->map(addr, ranges, na, ns, pna);
+ if (offset != OF_BAD_ADDR)
+ break;
+ }
+ if (offset == OF_BAD_ADDR)
+ return 1;
+
+ memcpy(addr, ranges + na, 4 * pna);
+
+finish:
+ /* Translate it into parent bus space */
+ return pbus->translate(addr, offset, pna);
+}
+
+static void __init build_device_resources(struct of_device *op,
+ struct device *parent)
+{
+ struct of_device *p_op;
+ struct of_bus *bus;
+ int na, ns;
+ int index, num_reg;
+ void *preg;
+
+ if (!parent)
+ return;
+
+ p_op = to_of_device(parent);
+ bus = of_match_bus(p_op->node);
+ bus->count_cells(op->node, &na, &ns);
+
+ preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);
+ if (!preg || num_reg == 0)
+ return;
+
+ /* Convert to num-cells. */
+ num_reg /= 4;
+
+ /* Conver to num-entries. */
+ num_reg /= na + ns;
+
+ for (index = 0; index < num_reg; index++) {
+ struct resource *r = &op->resource[index];
+ u32 addr[OF_MAX_ADDR_CELLS];
+ u32 *reg = (preg + (index * ((na + ns) * 4)));
+ struct device_node *dp = op->node;
+ struct device_node *pp = p_op->node;
+ struct of_bus *pbus;
+ u64 size, result = OF_BAD_ADDR;
+ unsigned long flags;
+ int dna, dns;
+ int pna, pns;
+
+ size = of_read_addr(reg + na, ns);
+ flags = bus->get_flags(reg);
+
+ memcpy(addr, reg, na * 4);
+
+ /* If the immediate parent has no ranges property to apply,
+ * just use a 1<->1 mapping.
+ */
+ if (of_find_property(pp, "ranges", NULL) == NULL) {
+ result = of_read_addr(addr, na);
+ goto build_res;
+ }
+
+ dna = na;
+ dns = ns;
+
+ while (1) {
+ dp = pp;
+ pp = dp->parent;
+ if (!pp) {
+ result = of_read_addr(addr, dna);
+ break;
+ }
+
+ pbus = of_match_bus(pp);
+ pbus->count_cells(dp, &pna, &pns);
+
+ if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna))
+ break;
+
+ dna = pna;
+ dns = pns;
+ bus = pbus;
+ }
+
+ build_res:
+ memset(r, 0, sizeof(*r));
+ if (result != OF_BAD_ADDR) {
+ r->start = result & 0xffffffff;
+ r->end = result + size - 1;
+ r->flags = flags | ((result >> 32ULL) & 0xffUL);
+ } else {
+ r->start = ~0UL;
+ r->end = ~0UL;
+ }
+ r->name = op->node->name;
+ }
+}
+
+static struct of_device * __init scan_one_device(struct device_node *dp,
+ struct device *parent)
+{
+ struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
+ struct linux_prom_irqs *intr;
+ int len, i;
+
+ if (!op)
+ return NULL;
+
+ op->node = dp;
+
+ op->clock_freq = of_getintprop_default(dp, "clock-frequency",
+ (25*1000*1000));
+ op->portid = of_getintprop_default(dp, "upa-portid", -1);
+ if (op->portid == -1)
+ op->portid = of_getintprop_default(dp, "portid", -1);
+
+ intr = of_get_property(dp, "intr", &len);
+ if (intr) {
+ op->num_irqs = len / sizeof(struct linux_prom_irqs);
+ for (i = 0; i < op->num_irqs; i++)
+ op->irqs[i] = intr[i].pri;
+ } else {
+ unsigned int *irq = of_get_property(dp, "interrupts", &len);
+
+ if (irq) {
+ op->num_irqs = len / sizeof(unsigned int);
+ for (i = 0; i < op->num_irqs; i++)
+ op->irqs[i] = irq[i];
+ } else {
+ op->num_irqs = 0;
+ }
+ }
+ if (sparc_cpu_model == sun4d) {
+ static int pil_to_sbus[] = {
+ 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
+ };
+ struct device_node *busp = dp->parent;
+ struct linux_prom_registers *regs;
+ int board = of_getintprop_default(busp, "board#", 0);
+ int slot;
+
+ regs = of_get_property(dp, "reg", NULL);
+ slot = regs->which_io;
+
+ for (i = 0; i < op->num_irqs; i++) {
+ int this_irq = op->irqs[i];
+ int sbusl = pil_to_sbus[this_irq];
+
+ if (sbusl)
+ this_irq = (((board + 1) << 5) +
+ (sbusl << 2) +
+ slot);
+
+ op->irqs[i] = this_irq;
+ }
+ }
+
+ build_device_resources(op, parent);
+
+ op->dev.parent = parent;
+ op->dev.bus = &of_bus_type;
+ if (!parent)
+ strcpy(op->dev.bus_id, "root");
+ else
+ strcpy(op->dev.bus_id, dp->path_component_name);
+
+ if (of_device_register(op)) {
+ printk("%s: Could not register of device.\n",
+ dp->full_name);
+ kfree(op);
+ op = NULL;
+ }
+
+ return op;
+}
+
+static void __init scan_tree(struct device_node *dp, struct device *parent)
+{
+ while (dp) {
+ struct of_device *op = scan_one_device(dp, parent);
+
+ if (op)
+ scan_tree(dp->child, &op->dev);
+
+ dp = dp->sibling;
+ }
+}
+
+static void __init scan_of_devices(void)
+{
+ struct device_node *root = of_find_node_by_path("/");
+ struct of_device *parent;
+
+ parent = scan_one_device(root, NULL);
+ if (!parent)
+ return;
+
+ scan_tree(root->child, &parent->dev);
+}
+
static int __init of_bus_driver_init(void)
{
- int err = 0;
+ int err;
+ err = bus_register(&of_bus_type);
#ifdef CONFIG_PCI
if (!err)
err = bus_register(&ebus_bus_type);
@@ -165,7 +634,11 @@ static int __init of_bus_driver_init(void)
if (!err)
err = bus_register(&sbus_bus_type);
#endif
- return 0;
+
+ if (!err)
+ scan_of_devices();
+
+ return err;
}
postcore_initcall(of_bus_driver_init);
diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c
index 946ce6d15819..4b06dcb00ebd 100644
--- a/arch/sparc/kernel/prom.c
+++ b/arch/sparc/kernel/prom.c
@@ -190,6 +190,36 @@ int of_getintprop_default(struct device_node *np, const char *name, int def)
}
EXPORT_SYMBOL(of_getintprop_default);
+int of_n_addr_cells(struct device_node *np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = of_get_property(np, "#address-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #address-cells property for the root node, default to 2 */
+ return 2;
+}
+EXPORT_SYMBOL(of_n_addr_cells);
+
+int of_n_size_cells(struct device_node *np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = of_get_property(np, "#size-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #size-cells property for the root node, default to 1 */
+ return 1;
+}
+EXPORT_SYMBOL(of_n_size_cells);
+
int of_set_property(struct device_node *dp, const char *name, void *val, int len)
{
struct property **prevp;
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c
index 7dadcdb4ca42..9631e8f4ae60 100644
--- a/arch/sparc/kernel/time.c
+++ b/arch/sparc/kernel/time.c
@@ -42,6 +42,7 @@
#include <asm/sun4paddr.h>
#include <asm/page.h>
#include <asm/pcic.h>
+#include <asm/of_device.h>
extern unsigned long wall_jiffies;
@@ -273,83 +274,31 @@ static __inline__ void sun4_clock_probe(void)
#endif
}
-/* Probe for the mostek real time clock chip. */
-static __inline__ void clock_probe(void)
+static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
{
- struct linux_prom_registers clk_reg[2];
- char model[128];
- register int node, cpuunit, bootbus;
- struct resource r;
-
- cpuunit = bootbus = 0;
- memset(&r, 0, sizeof(r));
-
- /* Determine the correct starting PROM node for the probe. */
- node = prom_getchild(prom_root_node);
- switch (sparc_cpu_model) {
- case sun4c:
- break;
- case sun4m:
- node = prom_getchild(prom_searchsiblings(node, "obio"));
- break;
- case sun4d:
- node = prom_getchild(bootbus = prom_searchsiblings(prom_getchild(cpuunit = prom_searchsiblings(node, "cpu-unit")), "bootbus"));
- break;
- default:
- prom_printf("CLOCK: Unsupported architecture!\n");
- prom_halt();
- }
+ struct device_node *dp = op->node;
+ char *model = of_get_property(dp, "model", NULL);
- /* Find the PROM node describing the real time clock. */
- sp_clock_typ = MSTK_INVALID;
- node = prom_searchsiblings(node,"eeprom");
- if (!node) {
- prom_printf("CLOCK: No clock found!\n");
- prom_halt();
- }
+ if (!model)
+ return -ENODEV;
- /* Get the model name and setup everything up. */
- model[0] = '\0';
- prom_getstring(node, "model", model, sizeof(model));
- if (strcmp(model, "mk48t02") == 0) {
+ if (!strcmp(model, "mk48t02")) {
sp_clock_typ = MSTK48T02;
- if (prom_getproperty(node, "reg", (char *) clk_reg, sizeof(clk_reg)) == -1) {
- prom_printf("clock_probe: FAILED!\n");
- prom_halt();
- }
- if (sparc_cpu_model == sun4d)
- prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
- else
- prom_apply_obio_ranges(clk_reg, 1);
+
/* Map the clock register io area read-only */
- r.flags = clk_reg[0].which_io;
- r.start = clk_reg[0].phys_addr;
- mstk48t02_regs = sbus_ioremap(&r, 0,
- sizeof(struct mostek48t02), "mk48t02");
+ mstk48t02_regs = of_ioremap(&op->resource[0], 0,
+ sizeof(struct mostek48t02),
+ "mk48t02");
mstk48t08_regs = NULL; /* To catch weirdness */
- } else if (strcmp(model, "mk48t08") == 0) {
+ } else if (!strcmp(model, "mk48t08")) {
sp_clock_typ = MSTK48T08;
- if(prom_getproperty(node, "reg", (char *) clk_reg,
- sizeof(clk_reg)) == -1) {
- prom_printf("clock_probe: FAILED!\n");
- prom_halt();
- }
- if (sparc_cpu_model == sun4d)
- prom_apply_generic_ranges (bootbus, cpuunit, clk_reg, 1);
- else
- prom_apply_obio_ranges(clk_reg, 1);
- /* Map the clock register io area read-only */
- /* XXX r/o attribute is somewhere in r.flags */
- r.flags = clk_reg[0].which_io;
- r.start = clk_reg[0].phys_addr;
- mstk48t08_regs = sbus_ioremap(&r, 0,
- sizeof(struct mostek48t08), "mk48t08");
+ mstk48t08_regs = of_ioremap(&op->resource[0], 0,
+ sizeof(struct mostek48t08),
+ "mk48t08");
mstk48t02_regs = &mstk48t08_regs->regs;
- } else {
- prom_printf("CLOCK: Unknown model name '%s'\n",model);
- prom_halt();
- }
+ } else
+ return -ENODEV;
/* Report a low battery voltage condition. */
if (has_low_battery())
@@ -358,6 +307,28 @@ static __inline__ void clock_probe(void)
/* Kick start the clock if it is completely stopped. */
if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
kick_start_clock();
+
+ return 0;
+}
+
+static struct of_device_id clock_match[] = {
+ {
+ .name = "eeprom",
+ },
+ {},
+};
+
+static struct of_platform_driver clock_driver = {
+ .name = "clock",
+ .match_table = clock_match,
+ .probe = clock_probe,
+};
+
+
+/* Probe for the mostek real time clock chip. */
+static void clock_init(void)
+{
+ of_register_driver(&clock_driver, &of_bus_type);
}
void __init sbus_time_init(void)
@@ -376,7 +347,7 @@ void __init sbus_time_init(void)
if (ARCH_SUN4)
sun4_clock_probe();
else
- clock_probe();
+ clock_init();
sparc_init_timers(timer_interrupt);
diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c
index c2c69c167d18..718350aba1ec 100644
--- a/arch/sparc64/kernel/auxio.c
+++ b/arch/sparc64/kernel/auxio.c
@@ -11,10 +11,9 @@
#include <linux/init.h>
#include <linux/ioport.h>
-#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/io.h>
-#include <asm/sbus.h>
-#include <asm/ebus.h>
#include <asm/auxio.h>
void __iomem *auxio_register = NULL;
@@ -111,12 +110,6 @@ void auxio_set_lte(int on)
}
}
-static void __devinit auxio_report_dev(struct device_node *dp)
-{
- printk(KERN_INFO "AUXIO: Found device at %s\n",
- dp->full_name);
-}
-
static struct of_device_id auxio_match[] = {
{
.name = "auxio",
@@ -126,67 +119,48 @@ static struct of_device_id auxio_match[] = {
MODULE_DEVICE_TABLE(of, auxio_match);
-#ifdef CONFIG_SBUS
-static int __devinit auxio_sbus_probe(struct of_device *dev, const struct of_device_id *match)
+static int __devinit auxio_probe(struct of_device *dev, const struct of_device_id *match)
{
- struct sbus_dev *sdev = to_sbus_device(&dev->dev);
-
- auxio_devtype = AUXIO_TYPE_SBUS;
- auxio_register = sbus_ioremap(&sdev->resource[0], 0,
- sdev->reg_addrs[0].reg_size,
- "auxiliaryIO");
- if (!auxio_register)
+ struct device_node *dp = dev->node;
+ unsigned long size;
+
+ if (!strcmp(dp->parent->name, "ebus")) {
+ auxio_devtype = AUXIO_TYPE_EBUS;
+ size = sizeof(u32);
+ } else if (!strcmp(dp->parent->name, "sbus")) {
+ auxio_devtype = AUXIO_TYPE_SBUS;
+ size = 1;
+ } else {
+ printk("auxio: Unknown parent bus type [%s]\n",
+ dp->parent->name);
return -ENODEV;
-
- auxio_report_dev(dev->node);
- return 0;
-}
-
-static struct of_platform_driver auxio_sbus_driver = {
- .name = "auxio",
- .match_table = auxio_match,
- .probe = auxio_sbus_probe,
-};
-#endif
-
-#ifdef CONFIG_PCI
-static int __devinit auxio_ebus_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct linux_ebus_device *edev = to_ebus_device(&dev->dev);
-
- auxio_devtype = AUXIO_TYPE_EBUS;
- auxio_register = ioremap(edev->resource[0].start, sizeof(u32));
+ }
+ auxio_register = of_ioremap(&dev->resource[0], 0, size, "auxio");
if (!auxio_register)
return -ENODEV;
- auxio_report_dev(dev->node);
+ printk(KERN_INFO "AUXIO: Found device at %s\n",
+ dp->full_name);
- auxio_set_led(AUXIO_LED_ON);
+ if (auxio_devtype == AUXIO_TYPE_EBUS)
+ auxio_set_led(AUXIO_LED_ON);
return 0;
}
-static struct of_platform_driver auxio_ebus_driver = {
+static struct of_platform_driver auxio_driver = {
.name = "auxio",
.match_table = auxio_match,
- .probe = auxio_ebus_probe,
+ .probe = auxio_probe,
};
-#endif
-static int __init auxio_probe(void)
+static int __init auxio_init(void)
{
-#ifdef CONFIG_SBUS
- of_register_driver(&auxio_sbus_driver, &sbus_bus_type);
-#endif
-#ifdef CONFIG_PCI
- of_register_driver(&auxio_ebus_driver, &ebus_bus_type);
-#endif
-
- return 0;
+ return of_register_driver(&auxio_driver, &of_bus_type);
}
/* Must be after subsys_initcall() so that busses are probed. Must
* be before device_initcall() because things like the floppy driver
* need to use the AUXIO register.
*/
-fs_initcall(auxio_probe);
+fs_initcall(auxio_init);
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index 98e0a8cbeecd..aac014d15ad3 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -20,6 +20,8 @@
#include <asm/pbm.h>
#include <asm/ebus.h>
#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/bpp.h>
#include <asm/irq.h>
@@ -279,45 +281,12 @@ static inline void *ebus_alloc(size_t size)
return mem;
}
-int __init ebus_intmap_match(struct linux_ebus *ebus,
- struct linux_prom_registers *reg,
- int *interrupt)
-{
- struct linux_prom_ebus_intmap *imap;
- struct linux_prom_ebus_intmask *imask;
- unsigned int hi, lo, irq;
- int i, len, n_imap;
-
- imap = of_get_property(ebus->prom_node, "interrupt-map", &len);
- if (!imap)
- return 0;
- n_imap = len / sizeof(imap[0]);
-
- imask = of_get_property(ebus->prom_node, "interrupt-map-mask", NULL);
- if (!imask)
- return 0;
-
- hi = reg->which_io & imask->phys_hi;
- lo = reg->phys_addr & imask->phys_lo;
- irq = *interrupt & imask->interrupt;
- for (i = 0; i < n_imap; i++) {
- if ((imap[i].phys_hi == hi) &&
- (imap[i].phys_lo == lo) &&
- (imap[i].interrupt == irq)) {
- *interrupt = imap[i].cinterrupt;
- return 0;
- }
- }
- return -1;
-}
-
-void __init fill_ebus_child(struct device_node *dp,
- struct linux_prom_registers *preg,
- struct linux_ebus_child *dev,
- int non_standard_regs)
+static void __init fill_ebus_child(struct device_node *dp,
+ struct linux_ebus_child *dev,
+ int non_standard_regs)
{
+ struct of_device *op;
int *regs;
- int *irqs;
int i, len;
dev->prom_node = dp;
@@ -354,12 +323,16 @@ void __init fill_ebus_child(struct device_node *dp,
}
}
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
+ op = of_find_device_by_node(dp);
+ if (!op) {
dev->num_irqs = 0;
+ } else {
+ dev->num_irqs = op->num_irqs;
+ for (i = 0; i < dev->num_irqs; i++)
+ dev->irqs[i] = op->irqs[i];
+ }
+
+ if (!dev->num_irqs) {
/*
* Oh, well, some PROMs don't export interrupts
* property to children of EBus devices...
@@ -375,23 +348,6 @@ void __init fill_ebus_child(struct device_node *dp,
dev->irqs[0] = dev->parent->irqs[1];
}
}
- } else {
- dev->num_irqs = len / sizeof(irqs[0]);
- for (i = 0; i < dev->num_irqs; i++) {
- struct pci_pbm_info *pbm = dev->bus->parent;
- struct pci_controller_info *p = pbm->parent;
-
- if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) {
- dev->irqs[i] = p->irq_build(pbm,
- dev->bus->self,
- irqs[i]);
- } else {
- /* If we get a bogus interrupt property, just
- * record the raw value instead of punting.
- */
- dev->irqs[i] = irqs[i];
- }
- }
}
}
@@ -403,72 +359,32 @@ static int __init child_regs_nonstandard(struct linux_ebus_device *dev)
return 0;
}
-void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
+static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
{
- struct linux_prom_registers *regs;
struct linux_ebus_child *child;
- int *irqs;
- int i, n, len;
+ struct of_device *op;
+ int i, len;
dev->prom_node = dp;
printk(" [%s", dp->name);
- regs = of_get_property(dp, "reg", &len);
- if (!regs) {
+ op = of_find_device_by_node(dp);
+ if (!op) {
dev->num_addrs = 0;
- goto probe_interrupts;
- }
-
- if (len % sizeof(struct linux_prom_registers)) {
- prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
- dev->prom_node->name, len,
- (int)sizeof(struct linux_prom_registers));
- prom_halt();
- }
- dev->num_addrs = len / sizeof(struct linux_prom_registers);
-
- for (i = 0; i < dev->num_addrs; i++) {
- /* XXX Learn how to interpret ebus ranges... -DaveM */
- if (regs[i].which_io >= 0x10)
- n = (regs[i].which_io - 0x10) >> 2;
- else
- n = regs[i].which_io;
-
- dev->resource[i].start = dev->bus->self->resource[n].start;
- dev->resource[i].start += (unsigned long)regs[i].phys_addr;
- dev->resource[i].end =
- (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL);
- dev->resource[i].flags = IORESOURCE_MEM;
- dev->resource[i].name = dev->prom_node->name;
- request_resource(&dev->bus->self->resource[n],
- &dev->resource[i]);
- }
-
-probe_interrupts:
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
dev->num_irqs = 0;
} else {
- dev->num_irqs = len / sizeof(irqs[0]);
- for (i = 0; i < dev->num_irqs; i++) {
- struct pci_pbm_info *pbm = dev->bus->parent;
- struct pci_controller_info *p = pbm->parent;
-
- if (ebus_intmap_match(dev->bus, &regs[0], &irqs[i]) != -1) {
- dev->irqs[i] = p->irq_build(pbm,
- dev->bus->self,
- irqs[i]);
- } else {
- /* If we get a bogus interrupt property, just
- * record the raw value instead of punting.
- */
- dev->irqs[i] = irqs[i];
- }
- }
+ (void) of_get_property(dp, "reg", &len);
+ dev->num_addrs = len / sizeof(struct linux_prom_registers);
+
+ for (i = 0; i < dev->num_addrs; i++)
+ memcpy(&dev->resource[i],
+ &op->resource[i],
+ sizeof(struct resource));
+
+ dev->num_irqs = op->num_irqs;
+ for (i = 0; i < dev->num_irqs; i++)
+ dev->irqs[i] = op->irqs[i];
}
dev->ofdev.node = dp;
@@ -490,7 +406,7 @@ probe_interrupts:
child->next = NULL;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(dp, regs, child,
+ fill_ebus_child(dp, child,
child_regs_nonstandard(dev));
while ((dp = dp->sibling) != NULL) {
@@ -500,7 +416,7 @@ probe_interrupts:
child->next = NULL;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(dp, regs, child,
+ fill_ebus_child(dp, child,
child_regs_nonstandard(dev));
}
}
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c
index ab9e640df228..eebe02f3f4cb 100644
--- a/arch/sparc64/kernel/irq.c
+++ b/arch/sparc64/kernel/irq.c
@@ -414,6 +414,10 @@ void irq_install_pre_handler(int virt_irq,
data->pre_handler_arg1 = arg1;
data->pre_handler_arg2 = arg2;
+ if (desc->chip == &sun4u_irq_ack ||
+ desc->chip == &sun4v_irq_ack)
+ return;
+
desc->chip = (desc->chip == &sun4u_irq ?
&sun4u_irq_ack : &sun4v_irq_ack);
}
diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c
index 6f16dee280a8..0f3aec72ef5f 100644
--- a/arch/sparc64/kernel/isa.c
+++ b/arch/sparc64/kernel/isa.c
@@ -3,6 +3,8 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/isa.h>
struct sparc_isa_bridge *isa_chain;
@@ -46,107 +48,16 @@ isa_dev_get_resource(struct sparc_isa_device *isa_dev)
return pregs;
}
-/* I can't believe they didn't put a real INO in the isa device
- * interrupts property. The whole point of the OBP properties
- * is to shield the kernel from IRQ routing details.
- *
- * The P1275 standard for ISA devices seems to also have been
- * totally ignored.
- *
- * On later systems, an interrupt-map and interrupt-map-mask scheme
- * akin to EBUS is used.
- */
-static struct {
- int obp_irq;
- int pci_ino;
-} grover_irq_table[] = {
- { 1, 0x00 }, /* dma, unknown ino at this point */
- { 2, 0x27 }, /* floppy */
- { 3, 0x22 }, /* parallel */
- { 4, 0x2b }, /* serial */
- { 5, 0x25 }, /* acpi power management */
-
- { 0, 0x00 } /* end of table */
-};
-
-static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev,
- struct sparc_isa_bridge *isa_br,
- int *interrupt,
- struct linux_prom_registers *reg)
-{
- struct linux_prom_ebus_intmap *imap;
- struct linux_prom_ebus_intmap *imask;
- unsigned int hi, lo, irq;
- int i, len, n_imap;
-
- imap = of_get_property(isa_br->prom_node, "interrupt-map", &len);
- if (!imap)
- return 0;
- n_imap = len / sizeof(imap[0]);
-
- imask = of_get_property(isa_br->prom_node, "interrupt-map-mask", NULL);
- if (!imask)
- return 0;
-
- hi = reg->which_io & imask->phys_hi;
- lo = reg->phys_addr & imask->phys_lo;
- irq = *interrupt & imask->interrupt;
- for (i = 0; i < n_imap; i++) {
- if ((imap[i].phys_hi == hi) &&
- (imap[i].phys_lo == lo) &&
- (imap[i].interrupt == irq)) {
- *interrupt = imap[i].cinterrupt;
- return 0;
- }
- }
- return -1;
-}
-
static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
struct linux_prom_registers *pregs)
{
- int irq_prop;
+ struct of_device *op = of_find_device_by_node(isa_dev->prom_node);
- irq_prop = of_getintprop_default(isa_dev->prom_node,
- "interrupts", -1);
- if (irq_prop <= 0) {
- goto no_irq;
+ if (!op || !op->num_irqs) {
+ isa_dev->irq = PCI_IRQ_NONE;
} else {
- struct pci_controller_info *pcic;
- struct pci_pbm_info *pbm;
- int i;
-
- if (of_find_property(isa_dev->bus->prom_node,
- "interrupt-map", NULL)) {
- if (!isa_dev_get_irq_using_imap(isa_dev,
- isa_dev->bus,
- &irq_prop,
- pregs))
- goto route_irq;
- }
-
- for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
- if (grover_irq_table[i].obp_irq == irq_prop) {
- int ino = grover_irq_table[i].pci_ino;
-
- if (ino == 0)
- goto no_irq;
-
- irq_prop = ino;
- goto route_irq;
- }
- }
- goto no_irq;
-
-route_irq:
- pbm = isa_dev->bus->parent;
- pcic = pbm->parent;
- isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
- return;
+ isa_dev->irq = op->irqs[0];
}
-
-no_irq:
- isa_dev->irq = PCI_IRQ_NONE;
}
static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
index 768475bbce82..3670dc8a7d5f 100644
--- a/arch/sparc64/kernel/of_device.c
+++ b/arch/sparc64/kernel/of_device.c
@@ -129,6 +129,43 @@ static int of_device_resume(struct device * dev)
return error;
}
+void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name)
+{
+ unsigned long ret = res->start + offset;
+
+ if (!request_region(ret, size, name))
+ ret = 0;
+
+ return (void __iomem *) ret;
+}
+EXPORT_SYMBOL(of_ioremap);
+
+void of_iounmap(void __iomem *base, unsigned long size)
+{
+ release_region((unsigned long) base, size);
+}
+EXPORT_SYMBOL(of_iounmap);
+
+static int node_match(struct device *dev, void *data)
+{
+ struct of_device *op = to_of_device(dev);
+ struct device_node *dp = data;
+
+ return (op->node == dp);
+}
+
+struct of_device *of_find_device_by_node(struct device_node *dp)
+{
+ struct device *dev = bus_find_device(&of_bus_type, NULL,
+ dp, node_match);
+
+ if (dev)
+ return to_of_device(dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_node);
+
#ifdef CONFIG_PCI
struct bus_type isa_bus_type = {
.name = "isa",
@@ -163,10 +200,654 @@ struct bus_type sbus_bus_type = {
EXPORT_SYMBOL(sbus_bus_type);
#endif
+struct bus_type of_bus_type = {
+ .name = "of",
+ .match = of_platform_bus_match,
+ .probe = of_device_probe,
+ .remove = of_device_remove,
+ .suspend = of_device_suspend,
+ .resume = of_device_resume,
+};
+EXPORT_SYMBOL(of_bus_type);
+
+static inline u64 of_read_addr(u32 *cell, int size)
+{
+ u64 r = 0;
+ while (size--)
+ r = (r << 32) | *(cell++);
+ return r;
+}
+
+static void __init get_cells(struct device_node *dp,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = of_n_addr_cells(dp);
+ if (sizec)
+ *sizec = of_n_size_cells(dp);
+}
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS 4
+
+struct of_bus {
+ const char *name;
+ const char *addr_prop_name;
+ int (*match)(struct device_node *parent);
+ void (*count_cells)(struct device_node *child,
+ int *addrc, int *sizec);
+ u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna);
+ int (*translate)(u32 *addr, u64 offset, int na);
+ unsigned int (*get_flags)(u32 *addr);
+};
+
+/*
+ * Default translator (generic bus)
+ */
+
+static void of_bus_default_count_cells(struct device_node *dev,
+ int *addrc, int *sizec)
+{
+ get_cells(dev, addrc, sizec);
+}
+
+static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ cp = of_read_addr(range, na);
+ s = of_read_addr(range + na + pna, ns);
+ da = of_read_addr(addr, na);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_default_translate(u32 *addr, u64 offset, int na)
+{
+ u64 a = of_read_addr(addr, na);
+ memset(addr, 0, na * 4);
+ a += offset;
+ if (na > 1)
+ addr[na - 2] = a >> 32;
+ addr[na - 1] = a & 0xffffffffu;
+
+ return 0;
+}
+
+static unsigned int of_bus_default_get_flags(u32 *addr)
+{
+ return IORESOURCE_MEM;
+}
+
+/*
+ * PCI bus specific translator
+ */
+
+static int of_bus_pci_match(struct device_node *np)
+{
+ return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex");
+}
+
+static void of_bus_pci_count_cells(struct device_node *np,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 3;
+ if (sizec)
+ *sizec = 2;
+}
+
+static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ /* Check address type match */
+ if ((addr[0] ^ range[0]) & 0x03000000)
+ return OF_BAD_ADDR;
+
+ /* Read address values, skipping high cell */
+ cp = of_read_addr(range + 1, na - 1);
+ s = of_read_addr(range + na + pna, ns);
+ da = of_read_addr(addr + 1, na - 1);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+static unsigned int of_bus_pci_get_flags(u32 *addr)
+{
+ unsigned int flags = 0;
+ u32 w = addr[0];
+
+ switch((w >> 24) & 0x03) {
+ case 0x01:
+ flags |= IORESOURCE_IO;
+ case 0x02: /* 32 bits */
+ case 0x03: /* 64 bits */
+ flags |= IORESOURCE_MEM;
+ }
+ if (w & 0x40000000)
+ flags |= IORESOURCE_PREFETCH;
+ return flags;
+}
+
+/*
+ * ISA bus specific translator
+ */
+
+static int of_bus_isa_match(struct device_node *np)
+{
+ return !strcmp(np->name, "isa");
+}
+
+static void of_bus_isa_count_cells(struct device_node *child,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 2;
+ if (sizec)
+ *sizec = 1;
+}
+
+static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ u64 cp, s, da;
+
+ /* Check address type match */
+ if ((addr[0] ^ range[0]) & 0x00000001)
+ return OF_BAD_ADDR;
+
+ /* Read address values, skipping high cell */
+ cp = of_read_addr(range + 1, na - 1);
+ s = of_read_addr(range + na + pna, ns);
+ da = of_read_addr(addr + 1, na - 1);
+
+ if (da < cp || da >= (cp + s))
+ return OF_BAD_ADDR;
+ return da - cp;
+}
+
+static int of_bus_isa_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr + 1, offset, na - 1);
+}
+
+static unsigned int of_bus_isa_get_flags(u32 *addr)
+{
+ unsigned int flags = 0;
+ u32 w = addr[0];
+
+ if (w & 1)
+ flags |= IORESOURCE_IO;
+ else
+ flags |= IORESOURCE_MEM;
+ return flags;
+}
+
+/*
+ * SBUS bus specific translator
+ */
+
+static int of_bus_sbus_match(struct device_node *np)
+{
+ return !strcmp(np->name, "sbus") ||
+ !strcmp(np->name, "sbi");
+}
+
+static void of_bus_sbus_count_cells(struct device_node *child,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = 2;
+ if (sizec)
+ *sizec = 1;
+}
+
+static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+ return of_bus_default_map(addr, range, na, ns, pna);
+}
+
+static int of_bus_sbus_translate(u32 *addr, u64 offset, int na)
+{
+ return of_bus_default_translate(addr, offset, na);
+}
+
+static unsigned int of_bus_sbus_get_flags(u32 *addr)
+{
+ return IORESOURCE_MEM;
+}
+
+
+/*
+ * Array of bus specific translators
+ */
+
+static struct of_bus of_busses[] = {
+ /* PCI */
+ {
+ .name = "pci",
+ .addr_prop_name = "assigned-addresses",
+ .match = of_bus_pci_match,
+ .count_cells = of_bus_pci_count_cells,
+ .map = of_bus_pci_map,
+ .translate = of_bus_pci_translate,
+ .get_flags = of_bus_pci_get_flags,
+ },
+ /* ISA */
+ {
+ .name = "isa",
+ .addr_prop_name = "reg",
+ .match = of_bus_isa_match,
+ .count_cells = of_bus_isa_count_cells,
+ .map = of_bus_isa_map,
+ .translate = of_bus_isa_translate,
+ .get_flags = of_bus_isa_get_flags,
+ },
+ /* SBUS */
+ {
+ .name = "sbus",
+ .addr_prop_name = "reg",
+ .match = of_bus_sbus_match,
+ .count_cells = of_bus_sbus_count_cells,
+ .map = of_bus_sbus_map,
+ .translate = of_bus_sbus_translate,
+ .get_flags = of_bus_sbus_get_flags,
+ },
+ /* Default */
+ {
+ .name = "default",
+ .addr_prop_name = "reg",
+ .match = NULL,
+ .count_cells = of_bus_default_count_cells,
+ .map = of_bus_default_map,
+ .translate = of_bus_default_translate,
+ .get_flags = of_bus_default_get_flags,
+ },
+};
+
+static struct of_bus *of_match_bus(struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
+ if (!of_busses[i].match || of_busses[i].match(np))
+ return &of_busses[i];
+ BUG();
+ return NULL;
+}
+
+static int __init build_one_resource(struct device_node *parent,
+ struct of_bus *bus,
+ struct of_bus *pbus,
+ u32 *addr,
+ int na, int ns, int pna)
+{
+ u32 *ranges;
+ unsigned int rlen;
+ int rone;
+ u64 offset = OF_BAD_ADDR;
+
+ ranges = of_get_property(parent, "ranges", &rlen);
+ if (ranges == NULL || rlen == 0) {
+ offset = of_read_addr(addr, na);
+ memset(addr, 0, pna * 4);
+ goto finish;
+ }
+
+ /* Now walk through the ranges */
+ rlen /= 4;
+ rone = na + pna + ns;
+ for (; rlen >= rone; rlen -= rone, ranges += rone) {
+ offset = bus->map(addr, ranges, na, ns, pna);
+ if (offset != OF_BAD_ADDR)
+ break;
+ }
+ if (offset == OF_BAD_ADDR)
+ return 1;
+
+ memcpy(addr, ranges + na, 4 * pna);
+
+finish:
+ /* Translate it into parent bus space */
+ return pbus->translate(addr, offset, pna);
+}
+
+static void __init build_device_resources(struct of_device *op,
+ struct device *parent)
+{
+ struct of_device *p_op;
+ struct of_bus *bus;
+ int na, ns;
+ int index, num_reg;
+ void *preg;
+
+ if (!parent)
+ return;
+
+ p_op = to_of_device(parent);
+ bus = of_match_bus(p_op->node);
+ bus->count_cells(op->node, &na, &ns);
+
+ preg = of_get_property(op->node, bus->addr_prop_name, &num_reg);
+ if (!preg || num_reg == 0)
+ return;
+
+ /* Convert to num-cells. */
+ num_reg /= 4;
+
+ /* Conver to num-entries. */
+ num_reg /= na + ns;
+
+ for (index = 0; index < num_reg; index++) {
+ struct resource *r = &op->resource[index];
+ u32 addr[OF_MAX_ADDR_CELLS];
+ u32 *reg = (preg + (index * ((na + ns) * 4)));
+ struct device_node *dp = op->node;
+ struct device_node *pp = p_op->node;
+ struct of_bus *pbus;
+ u64 size, result = OF_BAD_ADDR;
+ unsigned long flags;
+ int dna, dns;
+ int pna, pns;
+
+ size = of_read_addr(reg + na, ns);
+ flags = bus->get_flags(reg);
+
+ memcpy(addr, reg, na * 4);
+
+ /* If the immediate parent has no ranges property to apply,
+ * just use a 1<->1 mapping. Unless it is the 'dma' child
+ * of an isa bus, which must be passed up towards the root.
+ *
+ * Also, don't try to translate PMU bus device registers.
+ */
+ if ((of_find_property(pp, "ranges", NULL) == NULL &&
+ strcmp(pp->name, "dma") != 0) ||
+ !strcmp(pp->name, "pmu")) {
+ result = of_read_addr(addr, na);
+ goto build_res;
+ }
+
+ dna = na;
+ dns = ns;
+
+ while (1) {
+ dp = pp;
+ pp = dp->parent;
+ if (!pp) {
+ result = of_read_addr(addr, dna);
+ break;
+ }
+
+ pbus = of_match_bus(pp);
+ pbus->count_cells(dp, &pna, &pns);
+
+ if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna))
+ break;
+
+ dna = pna;
+ dns = pns;
+ bus = pbus;
+ }
+
+ build_res:
+ memset(r, 0, sizeof(*r));
+ if (result != OF_BAD_ADDR) {
+ r->start = result;
+ r->end = result + size - 1;
+ r->flags = flags;
+ } else {
+ r->start = ~0UL;
+ r->end = ~0UL;
+ }
+ r->name = op->node->name;
+ }
+}
+
+static struct device_node * __init
+apply_interrupt_map(struct device_node *dp, struct device_node *pp,
+ u32 *imap, int imlen, u32 *imask,
+ unsigned int *irq_p)
+{
+ struct device_node *cp;
+ unsigned int irq = *irq_p;
+ struct of_bus *bus;
+ phandle handle;
+ u32 *reg;
+ int na, num_reg, i;
+
+ bus = of_match_bus(pp);
+ bus->count_cells(dp, &na, NULL);
+
+ reg = of_get_property(dp, "reg", &num_reg);
+ if (!reg || !num_reg)
+ return NULL;
+
+ imlen /= ((na + 3) * 4);
+ handle = 0;
+ for (i = 0; i < imlen; i++) {
+ int j;
+
+ for (j = 0; j < na; j++) {
+ if ((reg[j] & imask[j]) != imap[j])
+ goto next;
+ }
+ if (imap[na] == irq) {
+ handle = imap[na + 1];
+ irq = imap[na + 2];
+ break;
+ }
+
+ next:
+ imap += (na + 3);
+ }
+ if (i == imlen)
+ return NULL;
+
+ *irq_p = irq;
+ cp = of_find_node_by_phandle(handle);
+
+ return cp;
+}
+
+static unsigned int __init pci_irq_swizzle(struct device_node *dp,
+ struct device_node *pp,
+ unsigned int irq)
+{
+ struct linux_prom_pci_registers *regs;
+ unsigned int devfn, slot, ret;
+
+ if (irq < 1 || irq > 4)
+ return irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (!regs)
+ return irq;
+
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ slot = (devfn >> 3) & 0x1f;
+
+ ret = ((irq - 1 + (slot & 3)) & 3) + 1;
+
+ return ret;
+}
+
+static unsigned int __init build_one_device_irq(struct of_device *op,
+ struct device *parent,
+ unsigned int irq)
+{
+ struct device_node *dp = op->node;
+ struct device_node *pp, *ip;
+ unsigned int orig_irq = irq;
+
+ if (irq == 0xffffffff)
+ return irq;
+
+ if (dp->irq_trans) {
+ irq = dp->irq_trans->irq_build(dp, irq,
+ dp->irq_trans->data);
+#if 1
+ printk("%s: direct translate %x --> %x\n",
+ dp->full_name, orig_irq, irq);
+#endif
+ return irq;
+ }
+
+ /* Something more complicated. Walk up to the root, applying
+ * interrupt-map or bus specific translations, until we hit
+ * an IRQ translator.
+ *
+ * If we hit a bus type or situation we cannot handle, we
+ * stop and assume that the original IRQ number was in a
+ * format which has special meaning to it's immediate parent.
+ */
+ pp = dp->parent;
+ ip = NULL;
+ while (pp) {
+ void *imap, *imsk;
+ int imlen;
+
+ imap = of_get_property(pp, "interrupt-map", &imlen);
+ imsk = of_get_property(pp, "interrupt-map-mask", NULL);
+ if (imap && imsk) {
+ struct device_node *iret;
+ int this_orig_irq = irq;
+
+ iret = apply_interrupt_map(dp, pp,
+ imap, imlen, imsk,
+ &irq);
+#if 1
+ printk("%s: Apply [%s:%x] imap --> [%s:%x]\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq,
+ (iret ? iret->full_name : "NULL"), irq);
+#endif
+ if (!iret)
+ break;
+
+ if (iret->irq_trans) {
+ ip = iret;
+ break;
+ }
+ } else {
+ if (!strcmp(pp->type, "pci") ||
+ !strcmp(pp->type, "pciex")) {
+ unsigned int this_orig_irq = irq;
+
+ irq = pci_irq_swizzle(dp, pp, irq);
+#if 1
+ printk("%s: PCI swizzle [%s] %x --> %x\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq, irq);
+#endif
+ }
+
+ if (pp->irq_trans) {
+ ip = pp;
+ break;
+ }
+ }
+ dp = pp;
+ pp = pp->parent;
+ }
+ if (!ip)
+ return orig_irq;
+
+ irq = ip->irq_trans->irq_build(op->node, irq,
+ ip->irq_trans->data);
+#if 1
+ printk("%s: Apply IRQ trans [%s] %x --> %x\n",
+ op->node->full_name, ip->full_name, orig_irq, irq);
+#endif
+
+ return irq;
+}
+
+static struct of_device * __init scan_one_device(struct device_node *dp,
+ struct device *parent)
+{
+ struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
+ unsigned int *irq;
+ int len, i;
+
+ if (!op)
+ return NULL;
+
+ op->node = dp;
+
+ op->clock_freq = of_getintprop_default(dp, "clock-frequency",
+ (25*1000*1000));
+ op->portid = of_getintprop_default(dp, "upa-portid", -1);
+ if (op->portid == -1)
+ op->portid = of_getintprop_default(dp, "portid", -1);
+
+ irq = of_get_property(dp, "interrupts", &len);
+ if (irq) {
+ memcpy(op->irqs, irq, len);
+ op->num_irqs = len / 4;
+ } else {
+ op->num_irqs = 0;
+ }
+
+ build_device_resources(op, parent);
+ for (i = 0; i < op->num_irqs; i++)
+ op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);
+
+ op->dev.parent = parent;
+ op->dev.bus = &of_bus_type;
+ if (!parent)
+ strcpy(op->dev.bus_id, "root");
+ else
+ strcpy(op->dev.bus_id, dp->path_component_name);
+
+ if (of_device_register(op)) {
+ printk("%s: Could not register of device.\n",
+ dp->full_name);
+ kfree(op);
+ op = NULL;
+ }
+
+ return op;
+}
+
+static void __init scan_tree(struct device_node *dp, struct device *parent)
+{
+ while (dp) {
+ struct of_device *op = scan_one_device(dp, parent);
+
+ if (op)
+ scan_tree(dp->child, &op->dev);
+
+ dp = dp->sibling;
+ }
+}
+
+static void __init scan_of_devices(void)
+{
+ struct device_node *root = of_find_node_by_path("/");
+ struct of_device *parent;
+
+ parent = scan_one_device(root, NULL);
+ if (!parent)
+ return;
+
+ scan_tree(root->child, &parent->dev);
+}
+
static int __init of_bus_driver_init(void)
{
- int err = 0;
+ int err;
+ err = bus_register(&of_bus_type);
#ifdef CONFIG_PCI
if (!err)
err = bus_register(&isa_bus_type);
@@ -177,7 +858,11 @@ static int __init of_bus_driver_init(void)
if (!err)
err = bus_register(&sbus_bus_type);
#endif
- return 0;
+
+ if (!err)
+ scan_of_devices();
+
+ return err;
}
postcore_initcall(of_bus_driver_init);
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 20ca9ec8fd3b..04ea6c2eb7a1 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -307,7 +307,6 @@ static void __init pci_scan_each_controller_bus(void)
p->scan_bus(p);
}
-extern void clock_probe(void);
extern void power_init(void);
static int __init pcibios_init(void)
@@ -320,7 +319,6 @@ static int __init pcibios_init(void)
isa_init();
ebus_init();
- clock_probe();
power_init();
return 0;
@@ -406,14 +404,8 @@ void pcibios_bus_to_resource(struct pci_dev *pdev, struct resource *res,
}
EXPORT_SYMBOL(pcibios_bus_to_resource);
-extern int pci_irq_verbose;
-
char * __init pcibios_setup(char *str)
{
- if (!strcmp(str, "irq_verbose")) {
- pci_irq_verbose = 1;
- return NULL;
- }
return str;
}
diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c
index b06a2955bf5f..7a59cc72c844 100644
--- a/arch/sparc64/kernel/pci_common.c
+++ b/arch/sparc64/kernel/pci_common.c
@@ -10,12 +10,10 @@
#include <asm/pbm.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include "pci_impl.h"
-/* Pass "pci=irq_verbose" on the kernel command line to enable this. */
-int pci_irq_verbose;
-
/* Fix self device of BUS and hook it into BUS->self.
* The pci_scan_bus does not do this for the host bridge.
*/
@@ -169,6 +167,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm,
}
pcp->pbm = pbm;
pcp->prom_node = dp;
+ pcp->op = of_find_device_by_node(dp);
memcpy(pcp->prom_regs, pregs,
nregs * sizeof(struct linux_prom_pci_registers));
pcp->num_prom_regs = nregs;
@@ -549,296 +548,18 @@ void __init pci_assign_unassigned(struct pci_pbm_info *pbm,
pci_assign_unassigned(pbm, bus);
}
-static inline unsigned int pci_slot_swivel(struct pci_pbm_info *pbm,
- struct pci_dev *toplevel_pdev,
- struct pci_dev *pdev,
- unsigned int interrupt)
-{
- unsigned int ret;
-
- if (unlikely(interrupt < 1 || interrupt > 4)) {
- printk("%s: Device %s interrupt value of %u is strange.\n",
- pbm->name, pci_name(pdev), interrupt);
- return interrupt;
- }
-
- ret = ((interrupt - 1 + (PCI_SLOT(pdev->devfn) & 3)) & 3) + 1;
-
- if (pci_irq_verbose)
- printk("%s: %s IRQ Swivel %s [%x:%x] -> [%x]\n",
- pbm->name, pci_name(toplevel_pdev), pci_name(pdev),
- interrupt, PCI_SLOT(pdev->devfn), ret);
-
- return ret;
-}
-
-static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm,
- struct pci_dev *toplevel_pdev,
- struct pci_dev *pbus,
- struct pci_dev *pdev,
- unsigned int interrupt,
- struct device_node **cnode)
-{
- struct linux_prom_pci_intmap *imap;
- struct linux_prom_pci_intmask *imask;
- struct pcidev_cookie *pbus_pcp = pbus->sysdata;
- struct pcidev_cookie *pdev_pcp = pdev->sysdata;
- struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs;
- struct property *prop;
- int plen, num_imap, i;
- unsigned int hi, mid, lo, irq, orig_interrupt;
-
- *cnode = pbus_pcp->prom_node;
-
- prop = of_find_property(pbus_pcp->prom_node, "interrupt-map", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_intmap)) != 0) {
- printk("%s: Device %s interrupt-map has bad len %d\n",
- pbm->name, pci_name(pbus), plen);
- goto no_intmap;
- }
- imap = prop->value;
- num_imap = plen / sizeof(struct linux_prom_pci_intmap);
-
- prop = of_find_property(pbus_pcp->prom_node, "interrupt-map-mask", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_intmask)) != 0) {
- printk("%s: Device %s interrupt-map-mask has bad len %d\n",
- pbm->name, pci_name(pbus), plen);
- goto no_intmap;
- }
- imask = prop->value;
-
- orig_interrupt = interrupt;
-
- hi = pregs->phys_hi & imask->phys_hi;
- mid = pregs->phys_mid & imask->phys_mid;
- lo = pregs->phys_lo & imask->phys_lo;
- irq = interrupt & imask->interrupt;
-
- for (i = 0; i < num_imap; i++) {
- if (imap[i].phys_hi == hi &&
- imap[i].phys_mid == mid &&
- imap[i].phys_lo == lo &&
- imap[i].interrupt == irq) {
- *cnode = of_find_node_by_phandle(imap[i].cnode);
- interrupt = imap[i].cinterrupt;
- }
- }
-
- if (pci_irq_verbose)
- printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n",
- pbm->name, pci_name(toplevel_pdev),
- pci_name(pbus), pci_name(pdev),
- orig_interrupt, interrupt);
-
-no_intmap:
- return interrupt;
-}
-
-/* For each PCI bus on the way to the root:
- * 1) If it has an interrupt-map property, apply it.
- * 2) Else, swivel the interrupt number based upon the PCI device number.
- *
- * Return the "IRQ controller" node. If this is the PBM's device node,
- * all interrupt translations are complete, else we should use that node's
- * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt.
- */
-static struct device_node * __init
-pci_intmap_match_to_root(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int *interrupt)
-{
- struct pci_dev *toplevel_pdev = pdev;
- struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata;
- struct device_node *cnode = toplevel_pcp->prom_node;
-
- while (pdev->bus->number != pbm->pci_first_busno) {
- struct pci_dev *pbus = pdev->bus->self;
- struct pcidev_cookie *pcp = pbus->sysdata;
- struct property *prop;
-
- prop = of_find_property(pcp->prom_node, "interrupt-map", NULL);
- if (!prop) {
- *interrupt = pci_slot_swivel(pbm, toplevel_pdev,
- pdev, *interrupt);
- cnode = pcp->prom_node;
- } else {
- *interrupt = pci_apply_intmap(pbm, toplevel_pdev,
- pbus, pdev,
- *interrupt, &cnode);
-
- while (pcp->prom_node != cnode &&
- pbus->bus->number != pbm->pci_first_busno) {
- pbus = pbus->bus->self;
- pcp = pbus->sysdata;
- }
- }
- pdev = pbus;
-
- if (cnode == pbm->prom_node)
- break;
- }
-
- return cnode;
-}
-
-static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
-{
- struct pcidev_cookie *dev_pcp = pdev->sysdata;
- struct pci_pbm_info *pbm = dev_pcp->pbm;
- struct linux_prom_pci_registers *reg;
- struct device_node *cnode;
- struct property *prop;
- unsigned int hi, mid, lo, irq;
- int i, plen;
-
- cnode = pci_intmap_match_to_root(pbm, pdev, interrupt);
- if (cnode == pbm->prom_node)
- goto success;
-
- prop = of_find_property(cnode, "reg", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_registers)) != 0) {
- printk("%s: OBP node %s reg property has bad len %d\n",
- pbm->name, cnode->full_name, plen);
- goto fail;
- }
- reg = prop->value;
-
- hi = reg[0].phys_hi & pbm->pbm_intmask->phys_hi;
- mid = reg[0].phys_mid & pbm->pbm_intmask->phys_mid;
- lo = reg[0].phys_lo & pbm->pbm_intmask->phys_lo;
- irq = *interrupt & pbm->pbm_intmask->interrupt;
-
- for (i = 0; i < pbm->num_pbm_intmap; i++) {
- struct linux_prom_pci_intmap *intmap;
-
- intmap = &pbm->pbm_intmap[i];
-
- if (intmap->phys_hi == hi &&
- intmap->phys_mid == mid &&
- intmap->phys_lo == lo &&
- intmap->interrupt == irq) {
- *interrupt = intmap->cinterrupt;
- goto success;
- }
- }
-
-fail:
- return 0;
-
-success:
- if (pci_irq_verbose)
- printk("%s: Routing bus[%2x] slot[%2x] to INO[%02x]\n",
- pbm->name,
- pdev->bus->number, PCI_SLOT(pdev->devfn),
- *interrupt);
- return 1;
-}
-
static void __init pdev_fixup_irq(struct pci_dev *pdev)
{
struct pcidev_cookie *pcp = pdev->sysdata;
- struct pci_pbm_info *pbm = pcp->pbm;
- struct pci_controller_info *p = pbm->parent;
- unsigned int portid = pbm->portid;
- unsigned int prom_irq;
- struct device_node *dp = pcp->prom_node;
- struct property *prop;
-
- /* If this is an empty EBUS device, sometimes OBP fails to
- * give it a valid fully specified interrupts property.
- * The EBUS hooked up to SunHME on PCI I/O boards of
- * Ex000 systems is one such case.
- *
- * The interrupt is not important so just ignore it.
- */
- if (pdev->vendor == PCI_VENDOR_ID_SUN &&
- pdev->device == PCI_DEVICE_ID_SUN_EBUS &&
- !dp->child) {
- pdev->irq = 0;
- return;
- }
+ struct of_device *op = pcp->op;
- prop = of_find_property(dp, "interrupts", NULL);
- if (!prop) {
- pdev->irq = 0;
+ if (op->irqs[0] == 0xffffffff) {
+ pdev->irq = PCI_IRQ_NONE;
return;
}
- prom_irq = *(unsigned int *) prop->value;
-
- if (tlb_type != hypervisor) {
- /* Fully specified already? */
- if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) {
- pdev->irq = p->irq_build(pbm, pdev, prom_irq);
- goto have_irq;
- }
-
- /* An onboard device? (bit 5 set) */
- if ((prom_irq & PCI_IRQ_INO) & 0x20) {
- pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq));
- goto have_irq;
- }
- }
-
- /* Can we find a matching entry in the interrupt-map? */
- if (pci_intmap_match(pdev, &prom_irq)) {
- pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq);
- goto have_irq;
- }
-
- /* Ok, we have to do it the hard way. */
- {
- unsigned int bus, slot, line;
-
- bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
-
- /* If we have a legal interrupt property, use it as
- * the IRQ line.
- */
- if (prom_irq > 0 && prom_irq < 5) {
- line = ((prom_irq - 1) & 3);
- } else {
- u8 pci_irq_line;
- /* Else just directly consult PCI config space. */
- pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line);
- line = ((pci_irq_line - 1) & 3);
- }
-
- /* Now figure out the slot.
- *
- * Basically, device number zero on the top-level bus is
- * always the PCI host controller. Slot 0 is then device 1.
- * PBM A supports two external slots (0 and 1), and PBM B
- * supports 4 external slots (0, 1, 2, and 3). On-board PCI
- * devices are wired to device numbers outside of these
- * ranges. -DaveM
- */
- if (pdev->bus->number == pbm->pci_first_busno) {
- slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot;
- } else {
- struct pci_dev *bus_dev;
-
- /* Underneath a bridge, use slot number of parent
- * bridge which is closest to the PBM.
- */
- bus_dev = pdev->bus->self;
- while (bus_dev->bus &&
- bus_dev->bus->number != pbm->pci_first_busno)
- bus_dev = bus_dev->bus->self;
-
- slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot;
- }
- slot = slot << 2;
-
- pdev->irq = p->irq_build(pbm, pdev,
- ((portid << 6) & PCI_IRQ_IGN) |
- (bus | slot | line));
- }
+ pdev->irq = op->irqs[0];
-have_irq:
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
pdev->irq & PCI_IRQ_INO);
}
diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c
index 5b2261ebda6f..bf7b32b36705 100644
--- a/arch/sparc64/kernel/pci_psycho.c
+++ b/arch/sparc64/kernel/pci_psycho.c
@@ -18,6 +18,7 @@
#include <asm/irq.h>
#include <asm/starfire.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include "pci_impl.h"
#include "iommu_common.h"
@@ -208,110 +209,6 @@ static struct pci_ops psycho_ops = {
.write = psycho_write_pci_cfg,
};
-/* PSYCHO interrupt mapping support. */
-#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
-#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
- else
- return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
-}
-
-#define PSYCHO_IMAP_SCSI 0x1000UL
-#define PSYCHO_IMAP_ETH 0x1008UL
-#define PSYCHO_IMAP_BPP 0x1010UL
-#define PSYCHO_IMAP_AU_REC 0x1018UL
-#define PSYCHO_IMAP_AU_PLAY 0x1020UL
-#define PSYCHO_IMAP_PFAIL 0x1028UL
-#define PSYCHO_IMAP_KMS 0x1030UL
-#define PSYCHO_IMAP_FLPY 0x1038UL
-#define PSYCHO_IMAP_SHW 0x1040UL
-#define PSYCHO_IMAP_KBD 0x1048UL
-#define PSYCHO_IMAP_MS 0x1050UL
-#define PSYCHO_IMAP_SER 0x1058UL
-#define PSYCHO_IMAP_TIM0 0x1060UL
-#define PSYCHO_IMAP_TIM1 0x1068UL
-#define PSYCHO_IMAP_UE 0x1070UL
-#define PSYCHO_IMAP_CE 0x1078UL
-#define PSYCHO_IMAP_A_ERR 0x1080UL
-#define PSYCHO_IMAP_B_ERR 0x1088UL
-#define PSYCHO_IMAP_PMGMT 0x1090UL
-#define PSYCHO_IMAP_GFX 0x1098UL
-#define PSYCHO_IMAP_EUPA 0x10a0UL
-
-static unsigned long __onboard_imap_off[] = {
-/*0x20*/ PSYCHO_IMAP_SCSI,
-/*0x21*/ PSYCHO_IMAP_ETH,
-/*0x22*/ PSYCHO_IMAP_BPP,
-/*0x23*/ PSYCHO_IMAP_AU_REC,
-/*0x24*/ PSYCHO_IMAP_AU_PLAY,
-/*0x25*/ PSYCHO_IMAP_PFAIL,
-/*0x26*/ PSYCHO_IMAP_KMS,
-/*0x27*/ PSYCHO_IMAP_FLPY,
-/*0x28*/ PSYCHO_IMAP_SHW,
-/*0x29*/ PSYCHO_IMAP_KBD,
-/*0x2a*/ PSYCHO_IMAP_MS,
-/*0x2b*/ PSYCHO_IMAP_SER,
-/*0x2c*/ PSYCHO_IMAP_TIM0,
-/*0x2d*/ PSYCHO_IMAP_TIM1,
-/*0x2e*/ PSYCHO_IMAP_UE,
-/*0x2f*/ PSYCHO_IMAP_CE,
-/*0x30*/ PSYCHO_IMAP_A_ERR,
-/*0x31*/ PSYCHO_IMAP_B_ERR,
-/*0x32*/ PSYCHO_IMAP_PMGMT
-};
-#define PSYCHO_ONBOARD_IRQ_BASE 0x20
-#define PSYCHO_ONBOARD_IRQ_LAST 0x32
-#define psycho_onboard_imap_offset(__ino) \
- __onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE]
-
-#define PSYCHO_ICLR_A_SLOT0 0x1400UL
-#define PSYCHO_ICLR_SCSI 0x1800UL
-
-#define psycho_iclr_offset(ino) \
- ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-static unsigned int psycho_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
-
- ino &= PCI_IRQ_INO;
- if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = psycho_pcislot_imap_offset(ino);
- } else {
- /* Onboard device */
- if (ino > PSYCHO_ONBOARD_IRQ_LAST) {
- prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
- prom_halt();
- }
- imap_off = psycho_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = pbm->controller_regs + imap_off;
- imap += 4;
-
- iclr_off = psycho_iclr_offset(ino);
- iclr = pbm->controller_regs + iclr_off;
- iclr += 4;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- return build_irq(inofixup, iclr, imap);
-}
-
/* PSYCHO error handling support. */
enum psycho_error_type {
UE_ERR, CE_ERR, PCI_ERR
@@ -944,51 +841,34 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id, struct pt_regs *reg
#define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */
#define PSYCHO_ECCCTRL_UE 0x4000000000000000UL /* Enable UE Interrupts */
#define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */
-#define PSYCHO_UE_INO 0x2e
-#define PSYCHO_CE_INO 0x2f
-#define PSYCHO_PCIERR_A_INO 0x30
-#define PSYCHO_PCIERR_B_INO 0x31
static void psycho_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ struct of_device *op = of_find_device_by_node(pbm->prom_node);
unsigned long base = p->pbm_A.controller_regs;
- unsigned int irq, portid = pbm->portid;
u64 tmp;
- /* Build IRQs and register handlers. */
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_UE_INO);
- if (request_irq(irq, psycho_ue_intr,
- SA_SHIRQ, "PSYCHO UE", p) < 0) {
- prom_printf("PSYCHO%d: Cannot register UE interrupt.\n",
- p->index);
- prom_halt();
- }
+ if (!op)
+ return;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_CE_INO);
- if (request_irq(irq, psycho_ce_intr,
- SA_SHIRQ, "PSYCHO CE", p) < 0) {
- prom_printf("PSYCHO%d: Cannot register CE interrupt.\n",
- p->index);
- prom_halt();
- }
+ /* Psycho interrupt property order is:
+ * 0: PCIERR PBM B INO
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: POWER FAIL
+ * 4: SPARE HARDWARE
+ * 5: PCIERR PBM A INO
+ */
- pbm = &p->pbm_A;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO);
- if (request_irq(irq, psycho_pcierr_intr,
- SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_A) < 0) {
- prom_printf("PSYCHO%d(PBMA): Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ if (op->num_irqs < 6)
+ return;
- pbm = &p->pbm_B;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO);
- if (request_irq(irq, psycho_pcierr_intr,
- SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_B) < 0) {
- prom_printf("PSYCHO%d(PBMB): Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ request_irq(op->irqs[1], psycho_ue_intr, SA_SHIRQ, "PSYCHO UE", p);
+ request_irq(op->irqs[2], psycho_ce_intr, SA_SHIRQ, "PSYCHO CE", p);
+ request_irq(op->irqs[5], psycho_pcierr_intr, SA_SHIRQ,
+ "PSYCHO PCIERR-A", &p->pbm_A);
+ request_irq(op->irqs[0], psycho_pcierr_intr, SA_SHIRQ,
+ "PSYCHO PCIERR-B", &p->pbm_B);
/* Enable UE and CE interrupts for controller. */
psycho_write(base + PSYCHO_ECC_CTRL,
@@ -1171,9 +1051,7 @@ static void psycho_iommu_init(struct pci_controller_info *p)
/* If necessary, hook us up for starfire IRQ translations. */
if (this_is_starfire)
- p->starfire_cookie = starfire_hookup(p->pbm_A.portid);
- else
- p->starfire_cookie = NULL;
+ starfire_hookup(p->pbm_A.portid);
}
#define PSYCHO_IRQ_RETRY 0x1a00UL
@@ -1408,7 +1286,6 @@ void psycho_init(struct device_node *dp, char *model_name)
p->index = pci_num_controllers++;
p->pbms_same_domain = 0;
p->scan_bus = psycho_scan_bus;
- p->irq_build = psycho_irq_build;
p->base_address_update = psycho_base_address_update;
p->resource_adjust = psycho_resource_adjust;
p->pci_ops = &psycho_ops;
diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c
index 26f194ce4400..5e087b0fb4c9 100644
--- a/arch/sparc64/kernel/pci_sabre.c
+++ b/arch/sparc64/kernel/pci_sabre.c
@@ -485,114 +485,6 @@ static struct pci_ops sabre_ops = {
.write = sabre_write_pci_cfg,
};
-static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return SABRE_IMAP_A_SLOT0 + (slot * 8);
- else
- return SABRE_IMAP_B_SLOT0 + (slot * 8);
-}
-
-static unsigned long __onboard_imap_off[] = {
-/*0x20*/ SABRE_IMAP_SCSI,
-/*0x21*/ SABRE_IMAP_ETH,
-/*0x22*/ SABRE_IMAP_BPP,
-/*0x23*/ SABRE_IMAP_AU_REC,
-/*0x24*/ SABRE_IMAP_AU_PLAY,
-/*0x25*/ SABRE_IMAP_PFAIL,
-/*0x26*/ SABRE_IMAP_KMS,
-/*0x27*/ SABRE_IMAP_FLPY,
-/*0x28*/ SABRE_IMAP_SHW,
-/*0x29*/ SABRE_IMAP_KBD,
-/*0x2a*/ SABRE_IMAP_MS,
-/*0x2b*/ SABRE_IMAP_SER,
-/*0x2c*/ 0 /* reserved */,
-/*0x2d*/ 0 /* reserved */,
-/*0x2e*/ SABRE_IMAP_UE,
-/*0x2f*/ SABRE_IMAP_CE,
-/*0x30*/ SABRE_IMAP_PCIERR,
-};
-#define SABRE_ONBOARD_IRQ_BASE 0x20
-#define SABRE_ONBOARD_IRQ_LAST 0x30
-#define sabre_onboard_imap_offset(__ino) \
- __onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE]
-
-#define sabre_iclr_offset(ino) \
- ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-/* When a device lives behind a bridge deeper in the PCI bus topology
- * than APB, a special sequence must run to make sure all pending DMA
- * transfers at the time of IRQ delivery are visible in the coherency
- * domain by the cpu. This sequence is to perform a read on the far
- * side of the non-APB bridge, then perform a read of Sabre's DMA
- * write-sync register.
- */
-static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- struct pci_dev *pdev = _arg1;
- unsigned long sync_reg = (unsigned long) _arg2;
- u16 _unused;
-
- pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused);
- sabre_read(sync_reg);
-}
-
-static unsigned int sabre_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
- int virt_irq;
-
- ino &= PCI_IRQ_INO;
- if (ino < SABRE_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = sabre_pcislot_imap_offset(ino);
- } else {
- /* onboard device */
- if (ino > SABRE_ONBOARD_IRQ_LAST) {
- prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino);
- prom_halt();
- }
- imap_off = sabre_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = pbm->controller_regs + imap_off;
- imap += 4;
-
- iclr_off = sabre_iclr_offset(ino);
- iclr = pbm->controller_regs + iclr_off;
- iclr += 4;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- virt_irq = build_irq(inofixup, iclr, imap);
-
- if (pdev) {
- struct pcidev_cookie *pcp = pdev->sysdata;
-
- if (pdev->bus->number != pcp->pbm->pci_first_busno) {
- struct pci_controller_info *p = pcp->pbm->parent;
-
- irq_install_pre_handler(virt_irq,
- sabre_wsync_handler,
- pdev,
- (void *)
- p->pbm_A.controller_regs +
- SABRE_WRSYNC);
- }
- }
- return virt_irq;
-}
-
/* SABRE error handling support. */
static void sabre_check_iommu_error(struct pci_controller_info *p,
unsigned long afsr,
@@ -929,17 +821,30 @@ static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id, struct pt_regs *regs
return IRQ_HANDLED;
}
-/* XXX What about PowerFail/PowerManagement??? -DaveM */
-#define SABRE_UE_INO 0x2e
-#define SABRE_CE_INO 0x2f
-#define SABRE_PCIERR_INO 0x30
static void sabre_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ struct device_node *dp = pbm->prom_node;
+ struct of_device *op;
unsigned long base = pbm->controller_regs;
- unsigned long irq, portid = pbm->portid;
u64 tmp;
+ if (pbm->chip_type == PBM_CHIP_TYPE_SABRE)
+ dp = dp->parent;
+
+ op = of_find_device_by_node(dp);
+ if (!op)
+ return;
+
+ /* Sabre/Hummingbird IRQ property layout is:
+ * 0: PCI ERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: POWER FAIL
+ */
+ if (op->num_irqs < 4)
+ return;
+
/* We clear the error bits in the appropriate AFSR before
* registering the handler so that we don't get spurious
* interrupts.
@@ -948,32 +853,16 @@ static void sabre_register_error_handlers(struct pci_controller_info *p)
(SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE));
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO);
- if (request_irq(irq, sabre_ue_intr,
- SA_SHIRQ, "SABRE UE", p) < 0) {
- prom_printf("SABRE%d: Cannot register UE interrupt.\n",
- p->index);
- prom_halt();
- }
+
+ request_irq(op->irqs[1], sabre_ue_intr, SA_SHIRQ, "SABRE UE", p);
sabre_write(base + SABRE_CE_AFSR,
(SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR));
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO);
- if (request_irq(irq, sabre_ce_intr,
- SA_SHIRQ, "SABRE CE", p) < 0) {
- prom_printf("SABRE%d: Cannot register CE interrupt.\n",
- p->index);
- prom_halt();
- }
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO);
- if (request_irq(irq, sabre_pcierr_intr,
- SA_SHIRQ, "SABRE PCIERR", p) < 0) {
- prom_printf("SABRE%d: Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ request_irq(op->irqs[2], sabre_ce_intr, SA_SHIRQ, "SABRE CE", p);
+ request_irq(op->irqs[0], sabre_pcierr_intr, SA_SHIRQ,
+ "SABRE PCIERR", p);
tmp = sabre_read(base + SABRE_PCICTRL);
tmp |= SABRE_PCICTRL_ERREN;
@@ -1492,7 +1381,6 @@ void sabre_init(struct device_node *dp, char *model_name)
p->index = pci_num_controllers++;
p->pbms_same_domain = 1;
p->scan_bus = sabre_scan_bus;
- p->irq_build = sabre_irq_build;
p->base_address_update = sabre_base_address_update;
p->resource_adjust = sabre_resource_adjust;
p->pci_ops = &sabre_ops;
diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c
index f16449ccd7bc..5c6e2a9b91f8 100644
--- a/arch/sparc64/kernel/pci_schizo.c
+++ b/arch/sparc64/kernel/pci_schizo.c
@@ -217,116 +217,6 @@ static struct pci_ops schizo_ops = {
.write = schizo_write_pci_cfg,
};
-/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
- * imap/iclr registers are per-PBM.
- */
-#define SCHIZO_IMAP_BASE 0x1000UL
-#define SCHIZO_ICLR_BASE 0x1400UL
-
-static unsigned long schizo_imap_offset(unsigned long ino)
-{
- return SCHIZO_IMAP_BASE + (ino * 8UL);
-}
-
-static unsigned long schizo_iclr_offset(unsigned long ino)
-{
- return SCHIZO_ICLR_BASE + (ino * 8UL);
-}
-
-static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- unsigned long sync_reg = (unsigned long) _arg2;
- u64 mask = 1UL << (ino & IMAP_INO);
- u64 val;
- int limit;
-
- schizo_write(sync_reg, mask);
-
- limit = 100000;
- val = 0;
- while (--limit) {
- val = schizo_read(sync_reg);
- if (!(val & mask))
- break;
- }
- if (limit <= 0) {
- printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
- val, mask);
- }
-
- if (_arg1) {
- static unsigned char cacheline[64]
- __attribute__ ((aligned (64)));
-
- __asm__ __volatile__("rd %%fprs, %0\n\t"
- "or %0, %4, %1\n\t"
- "wr %1, 0x0, %%fprs\n\t"
- "stda %%f0, [%5] %6\n\t"
- "wr %0, 0x0, %%fprs\n\t"
- "membar #Sync"
- : "=&r" (mask), "=&r" (val)
- : "0" (mask), "1" (val),
- "i" (FPRS_FEF), "r" (&cacheline[0]),
- "i" (ASI_BLK_COMMIT_P));
- }
-}
-
-static unsigned long schizo_ino_to_iclr(struct pci_pbm_info *pbm,
- unsigned int ino)
-{
- ino &= PCI_IRQ_INO;
- return pbm->pbm_regs + schizo_iclr_offset(ino) + 4;
-}
-
-static unsigned long schizo_ino_to_imap(struct pci_pbm_info *pbm,
- unsigned int ino)
-{
- ino &= PCI_IRQ_INO;
- return pbm->pbm_regs + schizo_imap_offset(ino) + 4;
-}
-
-static unsigned int schizo_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- int ign_fixup;
- int virt_irq;
-
- ino &= PCI_IRQ_INO;
-
- /* Now build the IRQ bucket. */
- imap = schizo_ino_to_imap(pbm, ino);
- iclr = schizo_ino_to_iclr(pbm, ino);
-
- /* On Schizo, no inofixup occurs. This is because each
- * INO has it's own IMAP register. On Psycho and Sabre
- * there is only one IMAP register for each PCI slot even
- * though four different INOs can be generated by each
- * PCI slot.
- *
- * But, for JBUS variants (essentially, Tomatillo), we have
- * to fixup the lowest bit of the interrupt group number.
- */
- ign_fixup = 0;
- if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
- if (pbm->portid & 1)
- ign_fixup = (1 << 6);
- }
-
- virt_irq = build_irq(ign_fixup, iclr, imap);
-
- if (pdev && pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
- irq_install_pre_handler(virt_irq,
- tomatillo_wsync_handler,
- ((pbm->chip_version <= 4) ?
- (void *) 1 : (void *) 0),
- (void *) pbm->sync_reg);
- }
-
- return virt_irq;
-}
-
/* SCHIZO error handling support. */
enum schizo_error_type {
UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR
@@ -362,34 +252,6 @@ struct pci_pbm_info *pbm_for_ino(struct pci_controller_info *p, u32 ino)
return &p->pbm_A;
}
-static void schizo_clear_other_err_intr(struct pci_controller_info *p, int irq)
-{
- struct pci_pbm_info *pbm;
- unsigned long iclr;
-
- /* Do not clear the interrupt for the other PCI bus.
- *
- * This "ACK both PBM IRQs" only needs to be performed
- * for chip-wide error interrupts.
- */
- if ((irq & IMAP_INO) == SCHIZO_PCIERR_A_INO ||
- (irq & IMAP_INO) == SCHIZO_PCIERR_B_INO)
- return;
-
- pbm = pbm_for_ino(p, irq);
- if (pbm == &p->pbm_A)
- pbm = &p->pbm_B;
- else
- pbm = &p->pbm_A;
-
- schizo_irq_build(pbm, NULL,
- (pbm->portid << 6) | (irq & IMAP_INO));
-
- iclr = schizo_ino_to_iclr(pbm,
- (pbm->portid << 6) | (irq & IMAP_INO));
- upa_writel(ICLR_IDLE, iclr);
-}
-
#define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */
#define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */
#define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */
@@ -720,8 +582,6 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id, struct pt_regs *regs)
/* Interrogate IOMMU for error status. */
schizo_check_iommu_error(p, UE_ERR);
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
@@ -811,8 +671,6 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id, struct pt_regs *regs)
printk("(none)");
printk("]\n");
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
@@ -1033,8 +891,6 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id, struct pt_regs *reg
if (error_bits & (SCHIZO_PCIAFSR_PPERR | SCHIZO_PCIAFSR_SPERR))
pci_scan_for_parity_error(p, pbm, pbm->pci_bus);
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
@@ -1090,7 +946,6 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs *
printk("PCI%d: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n",
p->index, errlog);
- schizo_clear_other_err_intr(p, irq);
return IRQ_HANDLED;
}
@@ -1098,7 +953,6 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs *
p->index);
schizo_check_iommu_error(p, SAFARI_ERR);
- schizo_clear_other_err_intr(p, irq);
return IRQ_HANDLED;
}
@@ -1130,74 +984,47 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id, struct pt_regs *
static void tomatillo_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm;
- unsigned int irq;
+ struct of_device *op;
u64 tmp, err_mask, err_no_mask;
- /* Build IRQs and register handlers. */
+ /* Tomatillo IRQ property layout is:
+ * 0: PCIERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: SERR
+ * 4: POWER FAIL?
+ */
+
pbm = pbm_for_ino(p, SCHIZO_UE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO);
- if (request_irq(irq, schizo_ue_intr,
- SA_SHIRQ, "TOMATILLO UE", p) < 0) {
- prom_printf("%s: Cannot register UE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_UE_INO));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_UE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[1], schizo_ue_intr, SA_SHIRQ,
+ "TOMATILLO_UE", p);
pbm = pbm_for_ino(p, SCHIZO_CE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO);
- if (request_irq(irq, schizo_ce_intr,
- SA_SHIRQ, "TOMATILLO CE", p) < 0) {
- prom_printf("%s: Cannot register CE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_CE_INO));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_CE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[2], schizo_ce_intr, SA_SHIRQ,
+ "TOMATILLO CE", p);
pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO);
- irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) |
- SCHIZO_PCIERR_A_INO));
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM A PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_PCIERR_A_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "TOMATILLO PCIERR-A", pbm);
+
pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO);
- irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) |
- SCHIZO_PCIERR_B_INO));
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM B PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_PCIERR_B_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "TOMATILLO PCIERR-B", pbm);
pbm = pbm_for_ino(p, SCHIZO_SERR_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO);
- if (request_irq(irq, schizo_safarierr_intr,
- SA_SHIRQ, "TOMATILLO SERR", p) < 0) {
- prom_printf("%s: Cannot register SafariERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_SERR_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_SERR_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[3], schizo_safarierr_intr, SA_SHIRQ,
+ "TOMATILLO SERR", p);
/* Enable UE and CE interrupts for controller. */
schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL,
@@ -1265,64 +1092,47 @@ static void tomatillo_register_error_handlers(struct pci_controller_info *p)
static void schizo_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm;
- unsigned int irq;
+ struct of_device *op;
u64 tmp, err_mask, err_no_mask;
- /* Build IRQs and register handlers. */
+ /* Schizo IRQ property layout is:
+ * 0: PCIERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: SERR
+ * 4: POWER FAIL?
+ */
+
pbm = pbm_for_ino(p, SCHIZO_UE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO);
- if (request_irq(irq, schizo_ue_intr,
- SA_SHIRQ, "SCHIZO UE", p) < 0) {
- prom_printf("%s: Cannot register UE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_UE_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_UE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[1], schizo_ue_intr, SA_SHIRQ,
+ "SCHIZO_UE", p);
pbm = pbm_for_ino(p, SCHIZO_CE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO);
- if (request_irq(irq, schizo_ce_intr,
- SA_SHIRQ, "SCHIZO CE", p) < 0) {
- prom_printf("%s: Cannot register CE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_CE_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_CE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[2], schizo_ce_intr, SA_SHIRQ,
+ "SCHIZO CE", p);
pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_A_INO);
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "SCHIZO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM A PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_PCIERR_A_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "SCHIZO PCIERR-A", pbm);
+
pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_B_INO);
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "SCHIZO PCIERR", &p->pbm_B) < 0) {
- prom_printf("%s: Cannot register PBM B PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_PCIERR_B_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "SCHIZO PCIERR-B", pbm);
pbm = pbm_for_ino(p, SCHIZO_SERR_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO);
- if (request_irq(irq, schizo_safarierr_intr,
- SA_SHIRQ, "SCHIZO SERR", p) < 0) {
- prom_printf("%s: Cannot register SafariERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_SERR_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_SERR_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[3], schizo_safarierr_intr, SA_SHIRQ,
+ "SCHIZO SERR", p);
/* Enable UE and CE interrupts for controller. */
schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL,
@@ -2022,7 +1832,6 @@ static void __schizo_init(struct device_node *dp, char *model_name, int chip_typ
p->scan_bus = (chip_type == PBM_CHIP_TYPE_TOMATILLO ?
tomatillo_scan_bus :
schizo_scan_bus);
- p->irq_build = schizo_irq_build;
p->base_address_update = schizo_base_address_update;
p->resource_adjust = schizo_resource_adjust;
p->pci_ops = &schizo_ops;
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c
index b69e2270a721..03ad4c06758e 100644
--- a/arch/sparc64/kernel/pci_sun4v.c
+++ b/arch/sparc64/kernel/pci_sun4v.c
@@ -843,15 +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;
-
- return sun4v_build_irq(devhandle, devino);
-}
-
static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource)
{
struct pcidev_cookie *pcp = pdev->sysdata;
@@ -1200,7 +1191,6 @@ void sun4v_pci_init(struct device_node *dp, 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;
diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c
index 9496c7734014..4febeda958a3 100644
--- a/arch/sparc64/kernel/power.c
+++ b/arch/sparc64/kernel/power.c
@@ -17,9 +17,10 @@
#include <linux/pm.h>
#include <asm/system.h>
-#include <asm/ebus.h>
-#include <asm/isa.h>
#include <asm/auxio.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
+#include <asm/io.h>
#include <linux/unistd.h>
@@ -30,6 +31,7 @@
int scons_pwroff = 1;
#ifdef CONFIG_PCI
+#include <linux/pci.h>
static void __iomem *power_reg;
static DECLARE_WAIT_QUEUE_HEAD(powerd_wait);
@@ -115,27 +117,33 @@ static int __init has_button_interrupt(unsigned int irq, struct device_node *dp)
return 1;
}
-static void __devinit power_probe_common(struct of_device *dev, struct resource *res, unsigned int irq)
+static int __devinit power_probe(struct of_device *op, const struct of_device_id *match)
{
- power_reg = ioremap(res->start, 0x4);
+ struct resource *res = &op->resource[0];
+ unsigned int irq= op->irqs[0];
- printk("power: Control reg at %p ... ", power_reg);
+ power_reg = of_ioremap(res, 0, 0x4, "power");
+
+ printk("%s: Control reg at %lx ... ",
+ op->node->name, res->start);
poweroff_method = machine_halt; /* able to use the standard halt */
- if (has_button_interrupt(irq, dev->node)) {
+ if (has_button_interrupt(irq, op->node)) {
if (kernel_thread(powerd, NULL, CLONE_FS) < 0) {
printk("Failed to start power daemon.\n");
- return;
+ return 0;
}
printk("powerd running.\n");
if (request_irq(irq,
- power_handler, SA_SHIRQ, "power", NULL) < 0)
+ power_handler, 0, "power", NULL) < 0)
printk("power: Error, cannot register IRQ handler.\n");
} else {
printk("not using powerd.\n");
}
+
+ return 0;
}
static struct of_device_id power_match[] = {
@@ -145,44 +153,15 @@ static struct of_device_id power_match[] = {
{},
};
-static int __devinit ebus_power_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct linux_ebus_device *edev = to_ebus_device(&dev->dev);
- struct resource *res = &edev->resource[0];
- unsigned int irq = edev->irqs[0];
-
- power_probe_common(dev, res,irq);
-
- return 0;
-}
-
-static struct of_platform_driver ebus_power_driver = {
- .name = "power",
- .match_table = power_match,
- .probe = ebus_power_probe,
-};
-
-static int __devinit isa_power_probe(struct of_device *dev, const struct of_device_id *match)
-{
- struct sparc_isa_device *idev = to_isa_device(&dev->dev);
- struct resource *res = &idev->resource;
- unsigned int irq = idev->irq;
-
- power_probe_common(dev, res,irq);
-
- return 0;
-}
-
-static struct of_platform_driver isa_power_driver = {
+static struct of_platform_driver power_driver = {
.name = "power",
.match_table = power_match,
- .probe = isa_power_probe,
+ .probe = power_probe,
};
void __init power_init(void)
{
- of_register_driver(&ebus_power_driver, &ebus_bus_type);
- of_register_driver(&isa_power_driver, &isa_bus_type);
+ of_register_driver(&power_driver, &of_bus_type);
return;
}
#endif /* CONFIG_PCI */
diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c
index 8e87e7ea0325..8a70c52c0447 100644
--- a/arch/sparc64/kernel/prom.c
+++ b/arch/sparc64/kernel/prom.c
@@ -15,6 +15,7 @@
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -23,7 +24,11 @@
#include <linux/module.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/asi.h>
+#include <asm/upa.h>
static struct device_node *allnodes;
@@ -190,6 +195,36 @@ int of_getintprop_default(struct device_node *np, const char *name, int def)
}
EXPORT_SYMBOL(of_getintprop_default);
+int of_n_addr_cells(struct device_node *np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = of_get_property(np, "#address-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #address-cells property for the root node, default to 2 */
+ return 2;
+}
+EXPORT_SYMBOL(of_n_addr_cells);
+
+int of_n_size_cells(struct device_node *np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = of_get_property(np, "#size-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #size-cells property for the root node, default to 1 */
+ return 1;
+}
+EXPORT_SYMBOL(of_n_size_cells);
+
int of_set_property(struct device_node *dp, const char *name, void *val, int len)
{
struct property **prevp;
@@ -253,6 +288,754 @@ static void * __init prom_early_alloc(unsigned long size)
return ret;
}
+#ifdef CONFIG_PCI
+/* PSYCHO interrupt mapping support. */
+#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
+#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
+}
+
+#define PSYCHO_IMAP_SCSI 0x1000UL
+#define PSYCHO_IMAP_ETH 0x1008UL
+#define PSYCHO_IMAP_BPP 0x1010UL
+#define PSYCHO_IMAP_AU_REC 0x1018UL
+#define PSYCHO_IMAP_AU_PLAY 0x1020UL
+#define PSYCHO_IMAP_PFAIL 0x1028UL
+#define PSYCHO_IMAP_KMS 0x1030UL
+#define PSYCHO_IMAP_FLPY 0x1038UL
+#define PSYCHO_IMAP_SHW 0x1040UL
+#define PSYCHO_IMAP_KBD 0x1048UL
+#define PSYCHO_IMAP_MS 0x1050UL
+#define PSYCHO_IMAP_SER 0x1058UL
+#define PSYCHO_IMAP_TIM0 0x1060UL
+#define PSYCHO_IMAP_TIM1 0x1068UL
+#define PSYCHO_IMAP_UE 0x1070UL
+#define PSYCHO_IMAP_CE 0x1078UL
+#define PSYCHO_IMAP_A_ERR 0x1080UL
+#define PSYCHO_IMAP_B_ERR 0x1088UL
+#define PSYCHO_IMAP_PMGMT 0x1090UL
+#define PSYCHO_IMAP_GFX 0x1098UL
+#define PSYCHO_IMAP_EUPA 0x10a0UL
+
+static unsigned long __psycho_onboard_imap_off[] = {
+/*0x20*/ PSYCHO_IMAP_SCSI,
+/*0x21*/ PSYCHO_IMAP_ETH,
+/*0x22*/ PSYCHO_IMAP_BPP,
+/*0x23*/ PSYCHO_IMAP_AU_REC,
+/*0x24*/ PSYCHO_IMAP_AU_PLAY,
+/*0x25*/ PSYCHO_IMAP_PFAIL,
+/*0x26*/ PSYCHO_IMAP_KMS,
+/*0x27*/ PSYCHO_IMAP_FLPY,
+/*0x28*/ PSYCHO_IMAP_SHW,
+/*0x29*/ PSYCHO_IMAP_KBD,
+/*0x2a*/ PSYCHO_IMAP_MS,
+/*0x2b*/ PSYCHO_IMAP_SER,
+/*0x2c*/ PSYCHO_IMAP_TIM0,
+/*0x2d*/ PSYCHO_IMAP_TIM1,
+/*0x2e*/ PSYCHO_IMAP_UE,
+/*0x2f*/ PSYCHO_IMAP_CE,
+/*0x30*/ PSYCHO_IMAP_A_ERR,
+/*0x31*/ PSYCHO_IMAP_B_ERR,
+/*0x32*/ PSYCHO_IMAP_PMGMT
+};
+#define PSYCHO_ONBOARD_IRQ_BASE 0x20
+#define PSYCHO_ONBOARD_IRQ_LAST 0x32
+#define psycho_onboard_imap_offset(__ino) \
+ __psycho_onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE]
+
+#define PSYCHO_ICLR_A_SLOT0 0x1400UL
+#define PSYCHO_ICLR_SCSI 0x1800UL
+
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static unsigned int psycho_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long controller_regs = (unsigned long) _data;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+
+ ino &= 0x3f;
+ if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = psycho_pcislot_imap_offset(ino);
+ } else {
+ /* Onboard device */
+ if (ino > PSYCHO_ONBOARD_IRQ_LAST) {
+ prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = psycho_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = psycho_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ return build_irq(inofixup, iclr, imap);
+}
+
+static void psycho_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = psycho_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) regs[2].phys_addr;
+}
+
+#define sabre_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+
+struct sabre_irq_data {
+ unsigned long controller_regs;
+ unsigned int pci_first_busno;
+};
+#define SABRE_CONFIGSPACE 0x001000000UL
+#define SABRE_WRSYNC 0x1c20UL
+
+#define SABRE_CONFIG_BASE(CONFIG_SPACE) \
+ (CONFIG_SPACE | (1UL << 24))
+#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+/* When a device lives behind a bridge deeper in the PCI bus topology
+ * than APB, a special sequence must run to make sure all pending DMA
+ * transfers at the time of IRQ delivery are visible in the coherency
+ * domain by the cpu. This sequence is to perform a read on the far
+ * side of the non-APB bridge, then perform a read of Sabre's DMA
+ * write-sync register.
+ */
+static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned int phys_hi = (unsigned int) (unsigned long) _arg1;
+ struct sabre_irq_data *irq_data = _arg2;
+ unsigned long controller_regs = irq_data->controller_regs;
+ unsigned long sync_reg = controller_regs + SABRE_WRSYNC;
+ unsigned long config_space = controller_regs + SABRE_CONFIGSPACE;
+ unsigned int bus, devfn;
+ u16 _unused;
+
+ config_space = SABRE_CONFIG_BASE(config_space);
+
+ bus = (phys_hi >> 16) & 0xff;
+ devfn = (phys_hi >> 8) & 0xff;
+
+ config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00);
+
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduha [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (_unused)
+ : "r" ((u16 *) config_space),
+ "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+
+ sabre_read(sync_reg);
+}
+
+#define SABRE_IMAP_A_SLOT0 0x0c00UL
+#define SABRE_IMAP_B_SLOT0 0x0c20UL
+#define SABRE_IMAP_SCSI 0x1000UL
+#define SABRE_IMAP_ETH 0x1008UL
+#define SABRE_IMAP_BPP 0x1010UL
+#define SABRE_IMAP_AU_REC 0x1018UL
+#define SABRE_IMAP_AU_PLAY 0x1020UL
+#define SABRE_IMAP_PFAIL 0x1028UL
+#define SABRE_IMAP_KMS 0x1030UL
+#define SABRE_IMAP_FLPY 0x1038UL
+#define SABRE_IMAP_SHW 0x1040UL
+#define SABRE_IMAP_KBD 0x1048UL
+#define SABRE_IMAP_MS 0x1050UL
+#define SABRE_IMAP_SER 0x1058UL
+#define SABRE_IMAP_UE 0x1070UL
+#define SABRE_IMAP_CE 0x1078UL
+#define SABRE_IMAP_PCIERR 0x1080UL
+#define SABRE_IMAP_GFX 0x1098UL
+#define SABRE_IMAP_EUPA 0x10a0UL
+#define SABRE_ICLR_A_SLOT0 0x1400UL
+#define SABRE_ICLR_B_SLOT0 0x1480UL
+#define SABRE_ICLR_SCSI 0x1800UL
+#define SABRE_ICLR_ETH 0x1808UL
+#define SABRE_ICLR_BPP 0x1810UL
+#define SABRE_ICLR_AU_REC 0x1818UL
+#define SABRE_ICLR_AU_PLAY 0x1820UL
+#define SABRE_ICLR_PFAIL 0x1828UL
+#define SABRE_ICLR_KMS 0x1830UL
+#define SABRE_ICLR_FLPY 0x1838UL
+#define SABRE_ICLR_SHW 0x1840UL
+#define SABRE_ICLR_KBD 0x1848UL
+#define SABRE_ICLR_MS 0x1850UL
+#define SABRE_ICLR_SER 0x1858UL
+#define SABRE_ICLR_UE 0x1870UL
+#define SABRE_ICLR_CE 0x1878UL
+#define SABRE_ICLR_PCIERR 0x1880UL
+
+static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return SABRE_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return SABRE_IMAP_B_SLOT0 + (slot * 8);
+}
+
+static unsigned long __sabre_onboard_imap_off[] = {
+/*0x20*/ SABRE_IMAP_SCSI,
+/*0x21*/ SABRE_IMAP_ETH,
+/*0x22*/ SABRE_IMAP_BPP,
+/*0x23*/ SABRE_IMAP_AU_REC,
+/*0x24*/ SABRE_IMAP_AU_PLAY,
+/*0x25*/ SABRE_IMAP_PFAIL,
+/*0x26*/ SABRE_IMAP_KMS,
+/*0x27*/ SABRE_IMAP_FLPY,
+/*0x28*/ SABRE_IMAP_SHW,
+/*0x29*/ SABRE_IMAP_KBD,
+/*0x2a*/ SABRE_IMAP_MS,
+/*0x2b*/ SABRE_IMAP_SER,
+/*0x2c*/ 0 /* reserved */,
+/*0x2d*/ 0 /* reserved */,
+/*0x2e*/ SABRE_IMAP_UE,
+/*0x2f*/ SABRE_IMAP_CE,
+/*0x30*/ SABRE_IMAP_PCIERR,
+};
+#define SABRE_ONBOARD_IRQ_BASE 0x20
+#define SABRE_ONBOARD_IRQ_LAST 0x30
+#define sabre_onboard_imap_offset(__ino) \
+ __sabre_onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE]
+
+#define sabre_iclr_offset(ino) \
+ ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static unsigned int sabre_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct sabre_irq_data *irq_data = _data;
+ unsigned long controller_regs = irq_data->controller_regs;
+ struct linux_prom_pci_registers *regs;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+ int virt_irq;
+
+ ino &= 0x3f;
+ if (ino < SABRE_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = sabre_pcislot_imap_offset(ino);
+ } else {
+ /* onboard device */
+ if (ino > SABRE_ONBOARD_IRQ_LAST) {
+ prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = sabre_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = sabre_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ virt_irq = build_irq(inofixup, iclr, imap);
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs &&
+ ((regs->phys_hi >> 16) & 0xff) != irq_data->pci_first_busno) {
+ irq_install_pre_handler(virt_irq,
+ sabre_wsync_handler,
+ (void *) (long) regs->phys_hi,
+ (void *)
+ controller_regs +
+ SABRE_WRSYNC);
+ }
+
+ return virt_irq;
+}
+
+static void sabre_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+ struct sabre_irq_data *irq_data;
+ u32 *busrange;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sabre_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct sabre_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ irq_data->controller_regs = regs[0].phys_addr;
+
+ busrange = of_get_property(dp, "bus-range", NULL);
+ irq_data->pci_first_busno = busrange[0];
+
+ dp->irq_trans->data = irq_data;
+}
+
+/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
+ * imap/iclr registers are per-PBM.
+ */
+#define SCHIZO_IMAP_BASE 0x1000UL
+#define SCHIZO_ICLR_BASE 0x1400UL
+
+static unsigned long schizo_imap_offset(unsigned long ino)
+{
+ return SCHIZO_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_iclr_offset(unsigned long ino)
+{
+ return SCHIZO_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + schizo_iclr_offset(ino) + 4;
+}
+
+static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + schizo_imap_offset(ino) + 4;
+}
+
+#define schizo_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define schizo_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned long sync_reg = (unsigned long) _arg2;
+ u64 mask = 1UL << (ino & IMAP_INO);
+ u64 val;
+ int limit;
+
+ schizo_write(sync_reg, mask);
+
+ limit = 100000;
+ val = 0;
+ while (--limit) {
+ val = schizo_read(sync_reg);
+ if (!(val & mask))
+ break;
+ }
+ if (limit <= 0) {
+ printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
+ val, mask);
+ }
+
+ if (_arg1) {
+ static unsigned char cacheline[64]
+ __attribute__ ((aligned (64)));
+
+ __asm__ __volatile__("rd %%fprs, %0\n\t"
+ "or %0, %4, %1\n\t"
+ "wr %1, 0x0, %%fprs\n\t"
+ "stda %%f0, [%5] %6\n\t"
+ "wr %0, 0x0, %%fprs\n\t"
+ "membar #Sync"
+ : "=&r" (mask), "=&r" (val)
+ : "0" (mask), "1" (val),
+ "i" (FPRS_FEF), "r" (&cacheline[0]),
+ "i" (ASI_BLK_COMMIT_P));
+ }
+}
+
+struct schizo_irq_data {
+ unsigned long pbm_regs;
+ unsigned long sync_reg;
+ u32 portid;
+ int chip_version;
+};
+
+static unsigned int schizo_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct schizo_irq_data *irq_data = _data;
+ unsigned long pbm_regs = irq_data->pbm_regs;
+ unsigned long imap, iclr;
+ int ign_fixup;
+ int virt_irq;
+ int is_tomatillo;
+
+ ino &= 0x3f;
+
+ /* Now build the IRQ bucket. */
+ imap = schizo_ino_to_imap(pbm_regs, ino);
+ iclr = schizo_ino_to_iclr(pbm_regs, ino);
+
+ /* On Schizo, no inofixup occurs. This is because each
+ * INO has it's own IMAP register. On Psycho and Sabre
+ * there is only one IMAP register for each PCI slot even
+ * though four different INOs can be generated by each
+ * PCI slot.
+ *
+ * But, for JBUS variants (essentially, Tomatillo), we have
+ * to fixup the lowest bit of the interrupt group number.
+ */
+ ign_fixup = 0;
+
+ is_tomatillo = (irq_data->sync_reg != 0UL);
+
+ if (is_tomatillo) {
+ if (irq_data->portid & 1)
+ ign_fixup = (1 << 6);
+ }
+
+ virt_irq = build_irq(ign_fixup, iclr, imap);
+
+ if (is_tomatillo) {
+ irq_install_pre_handler(virt_irq,
+ tomatillo_wsync_handler,
+ ((irq_data->chip_version <= 4) ?
+ (void *) 1 : (void *) 0),
+ (void *) irq_data->sync_reg);
+ }
+
+ return virt_irq;
+}
+
+static void schizo_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+ struct schizo_irq_data *irq_data;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = schizo_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct schizo_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = irq_data;
+
+ irq_data->pbm_regs = regs[0].phys_addr;
+ irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL;
+ irq_data->portid = of_getintprop_default(dp, "portid", 0);
+ irq_data->chip_version = of_getintprop_default(dp, "version#", 0);
+}
+
+static unsigned int pci_sun4v_irq_build(struct device_node *dp,
+ unsigned int devino,
+ void *_data)
+{
+ u32 devhandle = (u32) (unsigned long) _data;
+
+ return sun4v_build_irq(devhandle, devino);
+}
+
+static void pci_sun4v_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = pci_sun4v_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long)
+ ((regs->phys_addr >> 32UL) & 0x0fffffff);
+}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_SBUS
+/* INO number to IMAP register offset for SYSIO external IRQ's.
+ * This should conform to both Sunfire/Wildfire server and Fusion
+ * desktop designs.
+ */
+#define SYSIO_IMAP_SLOT0 0x2c04UL
+#define SYSIO_IMAP_SLOT1 0x2c0cUL
+#define SYSIO_IMAP_SLOT2 0x2c14UL
+#define SYSIO_IMAP_SLOT3 0x2c1cUL
+#define SYSIO_IMAP_SCSI 0x3004UL
+#define SYSIO_IMAP_ETH 0x300cUL
+#define SYSIO_IMAP_BPP 0x3014UL
+#define SYSIO_IMAP_AUDIO 0x301cUL
+#define SYSIO_IMAP_PFAIL 0x3024UL
+#define SYSIO_IMAP_KMS 0x302cUL
+#define SYSIO_IMAP_FLPY 0x3034UL
+#define SYSIO_IMAP_SHW 0x303cUL
+#define SYSIO_IMAP_KBD 0x3044UL
+#define SYSIO_IMAP_MS 0x304cUL
+#define SYSIO_IMAP_SER 0x3054UL
+#define SYSIO_IMAP_TIM0 0x3064UL
+#define SYSIO_IMAP_TIM1 0x306cUL
+#define SYSIO_IMAP_UE 0x3074UL
+#define SYSIO_IMAP_CE 0x307cUL
+#define SYSIO_IMAP_SBERR 0x3084UL
+#define SYSIO_IMAP_PMGMT 0x308cUL
+#define SYSIO_IMAP_GFX 0x3094UL
+#define SYSIO_IMAP_EUPA 0x309cUL
+
+#define bogon ((unsigned long) -1)
+static unsigned long sysio_irq_offsets[] = {
+ /* SBUS Slot 0 --> 3, level 1 --> 7 */
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+
+ /* Onboard devices (not relevant/used on SunFire). */
+ SYSIO_IMAP_SCSI,
+ SYSIO_IMAP_ETH,
+ SYSIO_IMAP_BPP,
+ bogon,
+ SYSIO_IMAP_AUDIO,
+ SYSIO_IMAP_PFAIL,
+ bogon,
+ bogon,
+ SYSIO_IMAP_KMS,
+ SYSIO_IMAP_FLPY,
+ SYSIO_IMAP_SHW,
+ SYSIO_IMAP_KBD,
+ SYSIO_IMAP_MS,
+ SYSIO_IMAP_SER,
+ bogon,
+ bogon,
+ SYSIO_IMAP_TIM0,
+ SYSIO_IMAP_TIM1,
+ bogon,
+ bogon,
+ SYSIO_IMAP_UE,
+ SYSIO_IMAP_CE,
+ SYSIO_IMAP_SBERR,
+ SYSIO_IMAP_PMGMT,
+};
+
+#undef bogon
+
+#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets)
+
+/* Convert Interrupt Mapping register pointer to associated
+ * Interrupt Clear register pointer, SYSIO specific version.
+ */
+#define SYSIO_ICLR_UNUSED0 0x3400UL
+#define SYSIO_ICLR_SLOT0 0x340cUL
+#define SYSIO_ICLR_SLOT1 0x344cUL
+#define SYSIO_ICLR_SLOT2 0x348cUL
+#define SYSIO_ICLR_SLOT3 0x34ccUL
+static unsigned long sysio_imap_to_iclr(unsigned long imap)
+{
+ unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0;
+ return imap + diff;
+}
+
+static unsigned int sbus_of_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long reg_base = (unsigned long) _data;
+ struct linux_prom_registers *regs;
+ unsigned long imap, iclr;
+ int sbus_slot = 0;
+ int sbus_level = 0;
+
+ ino &= 0x3f;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs)
+ sbus_slot = regs->which_io;
+
+ if (ino < 0x20)
+ ino += (sbus_slot * 8);
+
+ imap = sysio_irq_offsets[ino];
+ if (imap == ((unsigned long)-1)) {
+ prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n",
+ ino);
+ prom_halt();
+ }
+ imap += reg_base;
+
+ /* SYSIO inconsistency. For external SLOTS, we have to select
+ * the right ICLR register based upon the lower SBUS irq level
+ * bits.
+ */
+ if (ino >= 0x20) {
+ iclr = sysio_imap_to_iclr(imap);
+ } else {
+ sbus_level = ino & 0x7;
+
+ switch(sbus_slot) {
+ case 0:
+ iclr = reg_base + SYSIO_ICLR_SLOT0;
+ break;
+ case 1:
+ iclr = reg_base + SYSIO_ICLR_SLOT1;
+ break;
+ case 2:
+ iclr = reg_base + SYSIO_ICLR_SLOT2;
+ break;
+ default:
+ case 3:
+ iclr = reg_base + SYSIO_ICLR_SLOT3;
+ break;
+ };
+
+ iclr += ((unsigned long)sbus_level - 1UL) * 8UL;
+ }
+ return build_irq(sbus_level, iclr, imap);
+}
+
+static void sbus_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sbus_of_build_irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr;
+}
+#endif /* CONFIG_SBUS */
+
+
+static unsigned int central_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct device_node *central_dp = _data;
+ struct of_device *central_op = of_find_device_by_node(central_dp);
+ struct resource *res;
+ unsigned long imap, iclr;
+ u32 tmp;
+
+ if (!strcmp(dp->name, "eeprom")) {
+ res = &central_op->resource[5];
+ } else if (!strcmp(dp->name, "zs")) {
+ res = &central_op->resource[4];
+ } else if (!strcmp(dp->name, "clock-board")) {
+ res = &central_op->resource[3];
+ } else {
+ return ino;
+ }
+
+ imap = res->start + 0x00UL;
+ iclr = res->start + 0x10UL;
+
+ /* Set the INO state to idle, and disable. */
+ upa_writel(0, iclr);
+ upa_readl(iclr);
+
+ tmp = upa_readl(imap);
+ tmp &= ~0x80000000;
+ upa_writel(tmp, imap);
+
+ return build_irq(0, iclr, imap);
+}
+
+static void central_irq_trans_init(struct device_node *dp)
+{
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = central_build_irq;
+
+ dp->irq_trans->data = dp;
+}
+
+struct irq_trans {
+ const char *name;
+ void (*init)(struct device_node *);
+};
+
+#ifdef CONFIG_PCI
+static struct irq_trans pci_irq_trans_table[] = {
+ { "SUNW,sabre", sabre_irq_trans_init },
+ { "pci108e,a000", sabre_irq_trans_init },
+ { "pci108e,a001", sabre_irq_trans_init },
+ { "SUNW,psycho", psycho_irq_trans_init },
+ { "pci108e,8000", psycho_irq_trans_init },
+ { "SUNW,schizo", schizo_irq_trans_init },
+ { "pci108e,8001", schizo_irq_trans_init },
+ { "SUNW,schizo+", schizo_irq_trans_init },
+ { "pci108e,8002", schizo_irq_trans_init },
+ { "SUNW,tomatillo", schizo_irq_trans_init },
+ { "pci108e,a801", schizo_irq_trans_init },
+ { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
+};
+#endif
+
+static void irq_trans_init(struct device_node *dp)
+{
+ const char *model;
+ int i;
+
+ model = of_get_property(dp, "model", NULL);
+ if (!model)
+ model = of_get_property(dp, "compatible", NULL);
+ if (!model)
+ return;
+
+#ifdef CONFIG_PCI
+ for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) {
+ struct irq_trans *t = &pci_irq_trans_table[i];
+
+ if (!strcmp(model, t->name))
+ return t->init(dp);
+ }
+#endif
+#ifdef CONFIG_SBUS
+ if (!strcmp(dp->name, "sbus") ||
+ !strcmp(dp->name, "sbi"))
+ return sbus_irq_trans_init(dp);
+#endif
+ if (!strcmp(dp->name, "central"))
+ return central_irq_trans_init(dp->child);
+}
+
static int is_root_node(const struct device_node *dp)
{
if (!dp)
@@ -676,10 +1459,10 @@ static struct device_node * __init create_node(phandle node)
dp->type = get_one_property(node, "device_type");
dp->node = node;
- /* Build interrupts later... */
-
dp->properties = build_prop_list(node);
+ irq_trans_init(dp);
+
return dp;
}
diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c
index ac05e0f692ef..ef68aa4fec65 100644
--- a/arch/sparc64/kernel/sbus.c
+++ b/arch/sparc64/kernel/sbus.c
@@ -1221,9 +1221,7 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus)
/* Now some Xfire specific grot... */
if (this_is_starfire)
- sbus->starfire_cookie = starfire_hookup(sbus->portid);
- else
- sbus->starfire_cookie = NULL;
+ starfire_hookup(sbus->portid);
sysio_register_error_handlers(sbus);
}
@@ -1269,8 +1267,6 @@ int __init sbus_arch_preinit(void)
void __init sbus_arch_postinit(void)
{
extern void firetruck_init(void);
- extern void clock_probe(void);
firetruck_init();
- clock_probe();
}
diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c
index ae859d40771e..b930fee7708a 100644
--- a/arch/sparc64/kernel/starfire.c
+++ b/arch/sparc64/kernel/starfire.c
@@ -54,7 +54,7 @@ struct starfire_irqinfo {
static struct starfire_irqinfo *sflist = NULL;
/* Beam me up Scott(McNeil)y... */
-void *starfire_hookup(int upaid)
+void starfire_hookup(int upaid)
{
struct starfire_irqinfo *p;
unsigned long treg_base, hwmid, i;
@@ -81,8 +81,6 @@ void *starfire_hookup(int upaid)
p->upaid = upaid;
p->next = sflist;
sflist = p;
-
- return (void *) p;
}
unsigned int starfire_translate(unsigned long imap,
diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c
index 348b82035561..5f3dd4d800cd 100644
--- a/arch/sparc64/kernel/time.c
+++ b/arch/sparc64/kernel/time.c
@@ -38,11 +38,8 @@
#include <asm/timer.h>
#include <asm/irq.h>
#include <asm/io.h>
-#include <asm/sbus.h>
-#include <asm/fhc.h>
-#include <asm/pbm.h>
-#include <asm/ebus.h>
-#include <asm/isa.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/starfire.h>
#include <asm/smp.h>
#include <asm/sections.h>
@@ -770,237 +767,106 @@ static int __init clock_model_matches(char *model)
return 1;
}
-static void __init __clock_assign_common(void __iomem *addr, char *model)
+static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match)
{
- if (model[5] == '0' && model[6] == '2') {
- mstk48t02_regs = addr;
- } else if(model[5] == '0' && model[6] == '8') {
- mstk48t08_regs = addr;
- mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02;
- } else {
- mstk48t59_regs = addr;
- mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02;
- }
-}
-
-static void __init clock_assign_clk_reg(struct linux_prom_registers *clk_reg,
- char *model)
-{
- unsigned long addr;
-
- addr = ((unsigned long) clk_reg[0].phys_addr |
- (((unsigned long) clk_reg[0].which_io) << 32UL));
-
- __clock_assign_common((void __iomem *) addr, model);
-}
-
-static int __init clock_probe_central(void)
-{
- struct linux_prom_registers clk_reg[2], *pr;
- struct device_node *dp;
- char *model;
+ struct device_node *dp = op->node;
+ char *model = of_get_property(dp, "model", NULL);
+ unsigned long size, flags;
+ void __iomem *regs;
- if (!central_bus)
- return 0;
-
- /* Get Central FHC's prom node. */
- dp = central_bus->child->prom_node;
-
- /* Then get the first child device below it. */
- dp = dp->child;
-
- while (dp) {
- model = of_get_property(dp, "model", NULL);
- if (!model || !clock_model_matches(model))
- goto next_sibling;
-
- pr = of_get_property(dp, "reg", NULL);
- memcpy(clk_reg, pr, sizeof(clk_reg));
-
- apply_fhc_ranges(central_bus->child, clk_reg, 1);
- apply_central_ranges(central_bus, clk_reg, 1);
-
- clock_assign_clk_reg(clk_reg, model);
- return 1;
+ if (!model || !clock_model_matches(model))
+ return -ENODEV;
- next_sibling:
- dp = dp->sibling;
- }
+ /* On an Enterprise system there can be multiple mostek clocks.
+ * We should only match the one that is on the central FHC bus.
+ */
+ if (!strcmp(dp->parent->name, "fhc") &&
+ strcmp(dp->parent->parent->name, "central") != 0)
+ return -ENODEV;
- return 0;
-}
+ size = (op->resource[0].end - op->resource[0].start) + 1;
+ regs = of_ioremap(&op->resource[0], 0, size, "clock");
+ if (!regs)
+ return -ENOMEM;
-#ifdef CONFIG_PCI
-static void __init clock_isa_ebus_assign_regs(struct resource *res, char *model)
-{
if (!strcmp(model, "ds1287") ||
!strcmp(model, "m5819") ||
!strcmp(model, "m5819p") ||
!strcmp(model, "m5823")) {
- ds1287_regs = res->start;
+ ds1287_regs = (unsigned long) regs;
+ } else if (model[5] == '0' && model[6] == '2') {
+ mstk48t02_regs = regs;
+ } else if(model[5] == '0' && model[6] == '8') {
+ mstk48t08_regs = regs;
+ mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02;
} else {
- mstk48t59_regs = (void __iomem *) res->start;
+ mstk48t59_regs = regs;
mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02;
}
-}
-
-static int __init clock_probe_one_ebus_dev(struct linux_ebus_device *edev)
-{
- struct device_node *dp = edev->prom_node;
- char *model;
-
- model = of_get_property(dp, "model", NULL);
- if (!clock_model_matches(model))
- return 0;
- clock_isa_ebus_assign_regs(&edev->resource[0], model);
+ printk(KERN_INFO "%s: Clock regs at %p\n", dp->full_name, regs);
- return 1;
-}
-
-static int __init clock_probe_ebus(void)
-{
- struct linux_ebus *ebus;
+ local_irq_save(flags);
- for_each_ebus(ebus) {
- struct linux_ebus_device *edev;
+ if (mstk48t02_regs != NULL) {
+ /* Report a low battery voltage condition. */
+ if (has_low_battery())
+ prom_printf("NVRAM: Low battery voltage!\n");
- for_each_ebusdev(edev, ebus) {
- if (clock_probe_one_ebus_dev(edev))
- return 1;
- }
+ /* Kick start the clock if it is completely stopped. */
+ if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
+ kick_start_clock();
}
- return 0;
-}
-
-static int __init clock_probe_one_isa_dev(struct sparc_isa_device *idev)
-{
- struct device_node *dp = idev->prom_node;
- char *model;
-
- model = of_get_property(dp, "model", NULL);
- if (!clock_model_matches(model))
- return 0;
-
- clock_isa_ebus_assign_regs(&idev->resource, model);
-
- return 1;
-}
-
-static int __init clock_probe_isa(void)
-{
- struct sparc_isa_bridge *isa_br;
-
- for_each_isa(isa_br) {
- struct sparc_isa_device *isa_dev;
-
- for_each_isadev(isa_dev, isa_br) {
- if (clock_probe_one_isa_dev(isa_dev))
- return 1;
- }
- }
+ set_system_time();
+
+ local_irq_restore(flags);
return 0;
}
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_SBUS
-static int __init clock_probe_one_sbus_dev(struct sbus_bus *sbus, struct sbus_dev *sdev)
-{
- struct resource *res;
- char model[64];
- void __iomem *addr;
-
- prom_getstring(sdev->prom_node, "model", model, sizeof(model));
- if (!clock_model_matches(model))
- return 0;
-
- res = &sdev->resource[0];
- addr = sbus_ioremap(res, 0, 0x800UL, "eeprom");
- __clock_assign_common(addr, model);
-
- return 1;
-}
-
-static int __init clock_probe_sbus(void)
-{
- struct sbus_bus *sbus;
-
- for_each_sbus(sbus) {
- struct sbus_dev *sdev;
-
- for_each_sbusdev(sdev, sbus) {
- if (clock_probe_one_sbus_dev(sbus, sdev))
- return 1;
- }
- }
+static struct of_device_id clock_match[] = {
+ {
+ .name = "eeprom",
+ },
+ {
+ .name = "rtc",
+ },
+ {},
+};
- return 0;
-}
-#endif
+static struct of_platform_driver clock_driver = {
+ .name = "clock",
+ .match_table = clock_match,
+ .probe = clock_probe,
+};
-void __init clock_probe(void)
+static int __init clock_init(void)
{
- static int invoked;
- unsigned long flags;
-
- if (invoked)
- return;
- invoked = 1;
-
if (this_is_starfire) {
xtime.tv_sec = starfire_get_time();
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
- return;
+ return 0;
}
if (tlb_type == hypervisor) {
xtime.tv_sec = hypervisor_get_time();
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
set_normalized_timespec(&wall_to_monotonic,
-xtime.tv_sec, -xtime.tv_nsec);
- return;
- }
-
- /* Check FHC Central then EBUSs then ISA bridges then SBUSs.
- * That way we handle the presence of multiple properly.
- *
- * As a special case, machines with Central must provide the
- * timer chip there.
- */
- if (!clock_probe_central() &&
-#ifdef CONFIG_PCI
- !clock_probe_ebus() &&
- !clock_probe_isa() &&
-#endif
-#ifdef CONFIG_SBUS
- !clock_probe_sbus()
-#endif
- ) {
- printk(KERN_WARNING "No clock chip found.\n");
- return;
- }
-
- local_irq_save(flags);
-
- if (mstk48t02_regs != NULL) {
- /* Report a low battery voltage condition. */
- if (has_low_battery())
- prom_printf("NVRAM: Low battery voltage!\n");
-
- /* Kick start the clock if it is completely stopped. */
- if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP)
- kick_start_clock();
+ return 0;
}
- set_system_time();
-
- local_irq_restore(flags);
+ return of_register_driver(&clock_driver, &of_bus_type);
}
+/* Must be after subsys_initcall() so that busses are probed. Must
+ * be before device_initcall() because things like the RTC driver
+ * need to see the clock registers.
+ */
+fs_initcall(clock_init);
+
/* This is gets the master TICK_INT timer going. */
static unsigned long sparc64_init_timers(void)
{
diff --git a/arch/sparc64/kernel/unaligned.c b/arch/sparc64/kernel/unaligned.c
index bb2d68577855..a9b765271b85 100644
--- a/arch/sparc64/kernel/unaligned.c
+++ b/arch/sparc64/kernel/unaligned.c
@@ -20,6 +20,7 @@
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/bitops.h>
+#include <linux/kallsyms.h>
#include <asm/fpumacro.h>
/* #define DEBUG_MNA */
@@ -291,7 +292,8 @@ asmlinkage void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn)
if (count < 5) {
last_time = jiffies;
count++;
- printk("Kernel unaligned access at TPC[%lx]\n", regs->tpc);
+ printk("Kernel unaligned access at TPC[%lx] ", regs->tpc);
+ print_symbol("%s\n", regs->tpc);
}
if (!ok_for_kernel(insn) || dir == both) {