Commit 2b1e5978 authored by David S. Miller's avatar David S. Miller Committed by David S. Miller

[SPARC64]: of_device layer IRQ resolution

Do IRQ determination generically by parsing the PROM properties,
and using IRQ controller drivers for final resolution.

One immediate positive effect is that all of the IRQ frobbing
in the EBUS, ISA, and PCI controller layers has been eliminated.
We just look up the of_device and use the properly computed
value.

The PCI controller irq_build() routines are gone and no longer
used.  Unfortunately sbus_build_irq() has to remain as there is
a direct reference to this in the sunzilog driver.  That can be
killed off once the sparc32 side of this is written and the
sunzilog driver is transformed into an "of" bus driver.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c3a8b85f
......@@ -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));
}
}
......
......@@ -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_intmask *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)
......
......@@ -146,6 +146,26 @@ void of_iounmap(void __iomem *base, unsigned long 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",
......@@ -261,7 +281,6 @@ static unsigned int of_bus_default_get_flags(u32 *addr)
return IORESOURCE_MEM;
}
/*
* PCI bus specific translator
*/
......@@ -594,12 +613,171 @@ static void __init build_device_resources(struct of_device *op,
}
}
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;
int len, i;
if (!op)
return NULL;
......@@ -613,12 +791,16 @@ static struct of_device * __init scan_one_device(struct device_node *dp,
op->portid = of_getintprop_default(dp, "portid", -1);
irq = of_get_property(dp, "interrupts", &len);
if (irq)
op->irq = *irq;
else
op->irq = 0xffffffff;
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;
......
......@@ -404,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;
}
......
......@@ -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);
}
......
......@@ -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,
......@@ -1406,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;
......
......@@ -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;
......
This diff is collapsed.
......@@ -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;
......
This diff is collapsed.
......@@ -22,7 +22,8 @@ struct of_device
struct device_node *node;
struct device dev;
struct resource resource[PROMREG_MAX];
unsigned int irq;
unsigned int irqs[PROMINTR_MAX];
int num_irqs;
void *sysdata;
......@@ -35,6 +36,8 @@ struct of_device
extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name);
extern void of_iounmap(void __iomem *base, unsigned long size);
extern struct of_device *of_find_device_by_node(struct device_node *);
extern const struct of_device_id *of_match_device(
const struct of_device_id *matches, const struct of_device *dev);
......
......@@ -16,6 +16,7 @@
#include <asm/page.h>
#include <asm/oplib.h>
#include <asm/prom.h>
#include <asm/of_device.h>
#include <asm/iommu.h>
/* The abstraction used here is that there are PCI controllers,
......@@ -209,7 +210,6 @@ struct pci_controller_info {
/* Operations which are controller specific. */
void (*scan_bus)(struct pci_controller_info *);
unsigned int (*irq_build)(struct pci_pbm_info *, struct pci_dev *, unsigned int);
void (*base_address_update)(struct pci_dev *, int);
void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *);
......@@ -226,6 +226,7 @@ struct pci_controller_info {
struct pcidev_cookie {
struct pci_pbm_info *pbm;
struct device_node *prom_node;
struct of_device *op;
struct linux_prom_pci_registers prom_regs[PROMREG_MAX];
int num_prom_regs;
struct linux_prom_pci_registers prom_assignments[PROMREG_MAX];
......
......@@ -34,6 +34,7 @@ struct property {
unsigned int unique_id;
};
struct of_irq_controller;
struct device_node {
char *name;
char *type;
......@@ -53,6 +54,13 @@ struct device_node {
unsigned long _flags;
void *data;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
};
struct of_irq_controller {
unsigned int (*irq_build)(struct device_node *, unsigned int, void *);
void *data;
};
/* flag descriptions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment