Skip to content

Commit

Permalink
[SPARC64]: of_device layer IRQ resolution
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller authored and David S. Miller committed Jun 29, 2006
1 parent c3a8b85 commit 2b1e597
Show file tree
Hide file tree
Showing 13 changed files with 1,105 additions and 1,050 deletions.
150 changes: 33 additions & 117 deletions arch/sparc64/kernel/ebus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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...
Expand All @@ -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];
}
}
}
}

Expand All @@ -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;
Expand All @@ -490,7 +406,7 @@ void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *d
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) {
Expand All @@ -500,7 +416,7 @@ void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *d
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));
}
}
Expand Down
101 changes: 6 additions & 95 deletions arch/sparc64/kernel/isa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 2b1e597

Please sign in to comment.