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,
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_irqs = 0;
} else {
(void) of_get_property(dp, "reg", &len);
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;
for (i = 0; i < dev->num_addrs; i++)
memcpy(&dev->resource[i],
&op->resource[i],
sizeof(struct resource));
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];
}
}
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;
isa_dev->irq = op->irqs[0];
}
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;
}
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;
struct of_device *op = pcp->op;
/* 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;
if (op->irqs[0] == 0xffffffff) {
pdev->irq = PCI_IRQ_NONE;
return;
}
prop = of_find_property(dp, "interrupts", NULL);
if (!prop) {
pdev->irq = 0;
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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......@@ -283,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)
......@@ -706,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;
}
......
......@@ -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