diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2006-07-03 21:36:01 +1000 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-07-03 21:36:01 +1000 |
commit | 0ebfff1491ef85d41ddf9c633834838be144f69f (patch) | |
tree | 5b469a6d61a9fcfbf94e7b6d411e544dbdec8dec /arch/powerpc/platforms/cell/spider-pic.c | |
parent | f63e115fb50db39706b955b81e3375ef6bab2268 (diff) | |
download | op-kernel-dev-0ebfff1491ef85d41ddf9c633834838be144f69f.zip op-kernel-dev-0ebfff1491ef85d41ddf9c633834838be144f69f.tar.gz |
[POWERPC] Add new interrupt mapping core and change platforms to use it
This adds the new irq remapper core and removes the old one. Because
there are some fundamental conflicts with the old code, like the value
of NO_IRQ which I'm now setting to 0 (as per discussions with Linus),
etc..., this commit also changes the relevant platform and driver code
over to use the new remapper (so as not to cause difficulties later
in bisecting).
This patch removes the old pre-parsing of the open firmware interrupt
tree along with all the bogus assumptions it made to try to renumber
interrupts according to the platform. This is all to be handled by the
new code now.
For the pSeries XICS interrupt controller, a single remapper host is
created for the whole machine regardless of how many interrupt
presentation and source controllers are found, and it's set to match
any device node that isn't a 8259. That works fine on pSeries and
avoids having to deal with some of the complexities of split source
controllers vs. presentation controllers in the pSeries device trees.
The powerpc i8259 PIC driver now always requests the legacy interrupt
range. It also has the feature of being able to match any device node
(including NULL) if passed no device node as an input. That will help
porting over platforms with broken device-trees like Pegasos who don't
have a proper interrupt tree.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/cell/spider-pic.c')
-rw-r--r-- | arch/powerpc/platforms/cell/spider-pic.c | 341 |
1 files changed, 246 insertions, 95 deletions
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index 98425ac..ae7ef88 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/ioport.h> #include <asm/pgtable.h> #include <asm/prom.h> @@ -56,58 +57,67 @@ enum { REISWAITEN = 0x508, /* Reissue Wait Control*/ }; -static void __iomem *spider_pics[4]; +#define SPIDER_CHIP_COUNT 4 +#define SPIDER_SRC_COUNT 64 +#define SPIDER_IRQ_INVALID 63 -static void __iomem *spider_get_pic(int irq) -{ - int node = irq / IIC_NODE_STRIDE; - irq %= IIC_NODE_STRIDE; - - if (irq >= IIC_EXT_OFFSET && - irq < IIC_EXT_OFFSET + IIC_NUM_EXT && - spider_pics) - return spider_pics[node]; - return NULL; -} +struct spider_pic { + struct irq_host *host; + struct device_node *of_node; + void __iomem *regs; + unsigned int node_id; +}; +static struct spider_pic spider_pics[SPIDER_CHIP_COUNT]; -static int spider_get_nr(unsigned int irq) +static struct spider_pic *spider_virq_to_pic(unsigned int virq) { - return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; + return irq_map[virq].host->host_data; } -static void __iomem *spider_get_irq_config(int irq) +static void __iomem *spider_get_irq_config(struct spider_pic *pic, + unsigned int src) { - void __iomem *pic; - pic = spider_get_pic(irq); - return pic + TIR_CFGA + 8 * spider_get_nr(irq); + return pic->regs + TIR_CFGA + 8 * src; } -static void spider_unmask_irq(unsigned int irq) +static void spider_unmask_irq(unsigned int virq) { - int nodeid = (irq / IIC_NODE_STRIDE) * 0x10; - void __iomem *cfg = spider_get_irq_config(irq); - irq = spider_get_nr(irq); + struct spider_pic *pic = spider_virq_to_pic(virq); + void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq); - /* FIXME: Most of that is configuration and has nothing to do with enabling/disable, - * besides, it's also partially bogus. + /* We use no locking as we should be covered by the descriptor lock + * for access to invidual source configuration registers */ - out_be32(cfg, (in_be32(cfg) & ~0xf0)| 0x3107000eu | nodeid); - out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); + out_be32(cfg, in_be32(cfg) | 0x30000000u); } -static void spider_mask_irq(unsigned int irq) +static void spider_mask_irq(unsigned int virq) { - void __iomem *cfg = spider_get_irq_config(irq); - irq = spider_get_nr(irq); + struct spider_pic *pic = spider_virq_to_pic(virq); + void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq); + /* We use no locking as we should be covered by the descriptor lock + * for access to invidual source configuration registers + */ out_be32(cfg, in_be32(cfg) & ~0x30000000u); } -static void spider_ack_irq(unsigned int irq) +static void spider_ack_irq(unsigned int virq) { - /* Should reset edge detection logic but we don't configure any edge interrupt - * at the moment. + struct spider_pic *pic = spider_virq_to_pic(virq); + unsigned int src = irq_map[virq].hwirq; + + /* Reset edge detection logic if necessary */ + if (get_irq_desc(virq)->status & IRQ_LEVEL) + return; + + /* Only interrupts 47 to 50 can be set to edge */ + if (src < 47 || src > 50) + return; + + /* Perform the clear of the edge logic */ + out_be32(pic->regs + TIR_EDC, 0x100 | (src & 0xf)); } static struct irq_chip spider_pic = { @@ -117,102 +127,243 @@ static struct irq_chip spider_pic = { .ack = spider_ack_irq, }; -static int spider_get_irq(int node) +static int spider_host_match(struct irq_host *h, struct device_node *node) +{ + struct spider_pic *pic = h->host_data; + return node == pic->of_node; +} + +static int spider_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) { - unsigned long cs; - void __iomem *regs = spider_pics[node]; + unsigned int sense = flags & IRQ_TYPE_SENSE_MASK; + struct spider_pic *pic = h->host_data; + void __iomem *cfg = spider_get_irq_config(pic, hw); + int level = 0; + u32 ic; + + /* Note that only level high is supported for most interrupts */ + if (sense != IRQ_TYPE_NONE && sense != IRQ_TYPE_LEVEL_HIGH && + (hw < 47 || hw > 50)) + return -EINVAL; + + /* Decode sense type */ + switch(sense) { + case IRQ_TYPE_EDGE_RISING: + ic = 0x3; + break; + case IRQ_TYPE_EDGE_FALLING: + ic = 0x2; + break; + case IRQ_TYPE_LEVEL_LOW: + ic = 0x0; + level = 1; + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_NONE: + ic = 0x1; + level = 1; + break; + default: + return -EINVAL; + } - cs = in_be32(regs + TIR_CS) >> 24; + /* Configure the source. One gross hack that was there before and + * that I've kept around is the priority to the BE which I set to + * be the same as the interrupt source number. I don't know wether + * that's supposed to make any kind of sense however, we'll have to + * decide that, but for now, I'm not changing the behaviour. + */ + out_be32(cfg, (ic << 24) | (0x7 << 16) | (pic->node_id << 4) | 0xe); + out_be32(cfg + 4, (0x2 << 16) | (hw & 0xff)); - if (cs == 63) - return -1; - else - return cs; + if (level) + get_irq_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &spider_pic, handle_level_irq); + return 0; +} + +static int spider_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) + +{ + /* Spider interrupts have 2 cells, first is the interrupt source, + * second, well, I don't know for sure yet ... We mask the top bits + * because old device-trees encode a node number in there + */ + *out_hwirq = intspec[0] & 0x3f; + *out_flags = IRQ_TYPE_LEVEL_HIGH; + return 0; } +static struct irq_host_ops spider_host_ops = { + .match = spider_host_match, + .map = spider_host_map, + .xlate = spider_host_xlate, +}; + static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) { - int node = (int)(long)desc->handler_data; - int cascade_irq; + struct spider_pic *pic = desc->handler_data; + unsigned int cs, virq; - cascade_irq = spider_get_irq(node); - generic_handle_irq(cascade_irq, regs); + cs = in_be32(pic->regs + TIR_CS) >> 24; + if (cs == SPIDER_IRQ_INVALID) + virq = NO_IRQ; + else + virq = irq_linear_revmap(pic->host, cs); + if (virq != NO_IRQ) + generic_handle_irq(virq, regs); desc->chip->eoi(irq); } -/* hardcoded part to be compatible with older firmware */ +/* For hooking up the cascace we have a problem. Our device-tree is + * crap and we don't know on which BE iic interrupt we are hooked on at + * least not the "standard" way. We can reconstitute it based on two + * informations though: which BE node we are connected to and wether + * we are connected to IOIF0 or IOIF1. Right now, we really only care + * about the IBM cell blade and we know that its firmware gives us an + * interrupt-map property which is pretty strange. + */ +static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) +{ + unsigned int virq; + u32 *imap, *tmp; + int imaplen, intsize, unit; + struct device_node *iic; + struct irq_host *iic_host; + +#if 0 /* Enable that when we have a way to retreive the node as well */ + /* First, we check wether we have a real "interrupts" in the device + * tree in case the device-tree is ever fixed + */ + struct of_irq oirq; + if (of_irq_map_one(pic->of_node, 0, &oirq) == 0) { + virq = irq_create_of_mapping(oirq.controller, oirq.specifier, + oirq.size); + goto bail; + } +#endif + + /* Now do the horrible hacks */ + tmp = (u32 *)get_property(pic->of_node, "#interrupt-cells", NULL); + if (tmp == NULL) + return NO_IRQ; + intsize = *tmp; + imap = (u32 *)get_property(pic->of_node, "interrupt-map", &imaplen); + if (imap == NULL || imaplen < (intsize + 1)) + return NO_IRQ; + iic = of_find_node_by_phandle(imap[intsize]); + if (iic == NULL) + return NO_IRQ; + imap += intsize + 1; + tmp = (u32 *)get_property(iic, "#interrupt-cells", NULL); + if (tmp == NULL) + return NO_IRQ; + intsize = *tmp; + /* Assume unit is last entry of interrupt specifier */ + unit = imap[intsize - 1]; + /* Ok, we have a unit, now let's try to get the node */ + tmp = (u32 *)get_property(iic, "ibm,interrupt-server-ranges", NULL); + if (tmp == NULL) { + of_node_put(iic); + return NO_IRQ; + } + /* ugly as hell but works for now */ + pic->node_id = (*tmp) >> 1; + of_node_put(iic); + + /* Ok, now let's get cracking. You may ask me why I just didn't match + * the iic host from the iic OF node, but that way I'm still compatible + * with really really old old firmwares for which we don't have a node + */ + iic_host = iic_get_irq_host(pic->node_id); + if (iic_host == NULL) + return NO_IRQ; + /* Manufacture an IIC interrupt number of class 2 */ + virq = irq_create_mapping(iic_host, 0x20 | unit, 0); + if (virq == NO_IRQ) + printk(KERN_ERR "spider_pic: failed to map cascade !"); + return virq; +} + -static void __init spider_init_one(int node, unsigned long addr) +static void __init spider_init_one(struct device_node *of_node, int chip, + unsigned long addr) { - int n, irq; + struct spider_pic *pic = &spider_pics[chip]; + int i, virq; - spider_pics[node] = ioremap(addr, 0x800); - if (spider_pics[node] == NULL) + /* Map registers */ + pic->regs = ioremap(addr, 0x1000); + if (pic->regs == NULL) panic("spider_pic: can't map registers !"); - printk(KERN_INFO "spider_pic: mapped for node %d, addr: 0x%lx mapped to %p\n", - node, addr, spider_pics[node]); + /* Allocate a host */ + pic->host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, SPIDER_SRC_COUNT, + &spider_host_ops, SPIDER_IRQ_INVALID); + if (pic->host == NULL) + panic("spider_pic: can't allocate irq host !"); + pic->host->host_data = pic; - for (n = 0; n < IIC_NUM_EXT; n++) { - if (n == IIC_EXT_CASCADE) - continue; - irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; - set_irq_chip_and_handler(irq, &spider_pic, handle_level_irq); - get_irq_desc(irq)->status |= IRQ_LEVEL; + /* Fill out other bits */ + pic->of_node = of_node_get(of_node); + + /* Go through all sources and disable them */ + for (i = 0; i < SPIDER_SRC_COUNT; i++) { + void __iomem *cfg = pic->regs + TIR_CFGA + 8 * i; + out_be32(cfg, in_be32(cfg) & ~0x30000000u); } /* do not mask any interrupts because of level */ - out_be32(spider_pics[node] + TIR_MSK, 0x0); - - /* disable edge detection clear */ - /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ + out_be32(pic->regs + TIR_MSK, 0x0); /* enable interrupt packets to be output */ - out_be32(spider_pics[node] + TIR_PIEN, - in_be32(spider_pics[node] + TIR_PIEN) | 0x1); + out_be32(pic->regs + TIR_PIEN, in_be32(pic->regs + TIR_PIEN) | 0x1); - /* Hook up cascade */ - irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE; - set_irq_data(irq, (void *)(long)node); - set_irq_chained_handler(irq, spider_irq_cascade); + /* Hook up the cascade interrupt to the iic and nodeid */ + virq = spider_find_cascade_and_node(pic); + if (virq == NO_IRQ) + return; + set_irq_data(virq, pic); + set_irq_chained_handler(virq, spider_irq_cascade); + + printk(KERN_INFO "spider_pic: node %d, addr: 0x%lx %s\n", + pic->node_id, addr, of_node->full_name); /* Enable the interrupt detection enable bit. Do this last! */ - out_be32(spider_pics[node] + TIR_DEN, - in_be32(spider_pics[node] + TIR_DEN) | 0x1); + out_be32(pic->regs + TIR_DEN, in_be32(pic->regs + TIR_DEN) | 0x1); } void __init spider_init_IRQ(void) { - unsigned long *spider_reg; + struct resource r; struct device_node *dn; - char *compatible; - int node = 0; - - /* XXX node numbers are totally bogus. We _hope_ we get the device nodes in the right - * order here but that's definitely not guaranteed, we need to get the node from the - * device tree instead. There is currently no proper property for it (but our whole - * device-tree is bogus anyway) so all we can do is pray or maybe test the address - * and deduce the node-id + int chip = 0; + + /* XXX node numbers are totally bogus. We _hope_ we get the device + * nodes in the right order here but that's definitely not guaranteed, + * we need to get the node from the device tree instead. + * There is currently no proper property for it (but our whole + * device-tree is bogus anyway) so all we can do is pray or maybe test + * the address and deduce the node-id */ - for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { - compatible = (char *)get_property(dn, "compatible", NULL); - - if (!compatible) - continue; - - if (strstr(compatible, "CBEA,platform-spider-pic")) - spider_reg = (unsigned long *)get_property(dn, "reg", NULL); - else if (strstr(compatible, "sti,platform-spider-pic") && (node < 2)) { - static long hard_coded_pics[] = { 0x24000008000, 0x34000008000 }; - spider_reg = &hard_coded_pics[node]; + for (dn = NULL; + (dn = of_find_node_by_name(dn, "interrupt-controller"));) { + if (device_is_compatible(dn, "CBEA,platform-spider-pic")) { + if (of_address_to_resource(dn, 0, &r)) { + printk(KERN_WARNING "spider-pic: Failed\n"); + continue; + } + } else if (device_is_compatible(dn, "sti,platform-spider-pic") + && (chip < 2)) { + static long hard_coded_pics[] = + { 0x24000008000, 0x34000008000 }; + r.start = hard_coded_pics[chip]; } else continue; - - if (spider_reg == NULL) - printk(KERN_ERR "spider_pic: No address for node %d\n", node); - - spider_init_one(node, *spider_reg); - node++; + spider_init_one(dn, chip++, r.start); } } |