diff options
Diffstat (limited to 'arch/powerpc/sysdev/ipic.c')
| -rw-r--r-- | arch/powerpc/sysdev/ipic.c | 637 |
1 files changed, 472 insertions, 165 deletions
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index 8f01e0f1d84..b50f97811c2 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -1,5 +1,5 @@ /* - * include/asm-ppc/ipic.c + * arch/powerpc/sysdev/ipic.c * * IPIC routines implementations. * @@ -18,20 +18,80 @@ #include <linux/stddef.h> #include <linux/sched.h> #include <linux/signal.h> -#include <linux/sysdev.h> +#include <linux/syscore_ops.h> +#include <linux/device.h> +#include <linux/bootmem.h> +#include <linux/spinlock.h> +#include <linux/fsl_devices.h> #include <asm/irq.h> #include <asm/io.h> +#include <asm/prom.h> #include <asm/ipic.h> -#include <asm/mpc83xx.h> #include "ipic.h" -static struct ipic p_ipic; static struct ipic * primary_ipic; +static struct irq_chip ipic_level_irq_chip, ipic_edge_irq_chip; +static DEFINE_RAW_SPINLOCK(ipic_lock); static struct ipic_info ipic_info[] = { + [1] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 16, + .prio_mask = 0, + }, + [2] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 17, + .prio_mask = 1, + }, + [3] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 18, + .prio_mask = 2, + }, + [4] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 19, + .prio_mask = 3, + }, + [5] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 20, + .prio_mask = 4, + }, + [6] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 21, + .prio_mask = 5, + }, + [7] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 22, + .prio_mask = 6, + }, + [8] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_C, + .force = IPIC_SIFCR_H, + .bit = 23, + .prio_mask = 7, + }, [9] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -39,7 +99,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [10] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -47,15 +106,27 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [11] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, .bit = 26, .prio_mask = 2, }, + [12] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 27, + .prio_mask = 3, + }, + [13] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_D, + .force = IPIC_SIFCR_H, + .bit = 28, + .prio_mask = 4, + }, [14] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -63,7 +134,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [15] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -71,7 +141,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [16] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_D, .force = IPIC_SIFCR_H, @@ -79,7 +148,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [17] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -87,7 +156,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [18] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -95,7 +164,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [19] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -103,7 +172,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [20] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -111,7 +180,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [21] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -119,7 +188,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [22] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -127,7 +196,7 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [23] = { - .pend = IPIC_SEPNR, + .ack = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_B, .force = IPIC_SEFCR, @@ -135,7 +204,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 7, }, [32] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -143,7 +211,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [33] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -151,7 +218,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [34] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -159,7 +225,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [35] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -167,7 +232,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [36] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -175,7 +239,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [37] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -183,7 +246,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 5, }, [38] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, @@ -191,15 +253,69 @@ static struct ipic_info ipic_info[] = { .prio_mask = 6, }, [39] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_H, .prio = IPIC_SIPRR_A, .force = IPIC_SIFCR_H, .bit = 7, .prio_mask = 7, }, + [40] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 8, + .prio_mask = 0, + }, + [41] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 9, + .prio_mask = 1, + }, + [42] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 10, + .prio_mask = 2, + }, + [43] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 11, + .prio_mask = 3, + }, + [44] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 12, + .prio_mask = 4, + }, + [45] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 13, + .prio_mask = 5, + }, + [46] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 14, + .prio_mask = 6, + }, + [47] = { + .mask = IPIC_SIMSR_H, + .prio = IPIC_SIPRR_B, + .force = IPIC_SIFCR_H, + .bit = 15, + .prio_mask = 7, + }, [48] = { - .pend = IPIC_SEPNR, .mask = IPIC_SEMSR, .prio = IPIC_SMPRR_A, .force = IPIC_SEFCR, @@ -207,7 +323,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 4, }, [64] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -215,7 +330,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [65] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -223,7 +337,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [66] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -231,7 +344,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [67] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_A, .force = IPIC_SIFCR_L, @@ -239,7 +351,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [68] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -247,7 +358,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 0, }, [69] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -255,7 +365,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 1, }, [70] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -263,7 +372,6 @@ static struct ipic_info ipic_info[] = { .prio_mask = 2, }, [71] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = IPIC_SMPRR_B, .force = IPIC_SIFCR_L, @@ -271,96 +379,131 @@ static struct ipic_info ipic_info[] = { .prio_mask = 3, }, [72] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 8, }, [73] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 9, }, [74] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 10, }, [75] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 11, }, [76] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 12, }, [77] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 13, }, [78] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 14, }, [79] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 15, }, [80] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 16, }, + [81] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 17, + }, + [82] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 18, + }, + [83] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 19, + }, [84] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 20, }, [85] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 21, }, + [86] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 22, + }, + [87] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 23, + }, + [88] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 24, + }, + [89] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 25, + }, [90] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 26, }, [91] = { - .pend = IPIC_SIPNR_H, .mask = IPIC_SIMSR_L, .prio = 0, .force = IPIC_SIFCR_L, .bit = 27, }, + [94] = { + .mask = IPIC_SIMSR_L, + .prio = 0, + .force = IPIC_SIFCR_L, + .bit = 30, + }, }; static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) @@ -373,79 +516,222 @@ static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 out_be32(base + (reg >> 2), value); } -static inline struct ipic * ipic_from_irq(unsigned int irq) +static inline struct ipic * ipic_from_irq(unsigned int virq) { return primary_ipic; } -static void ipic_enable_irq(unsigned int irq) +static void ipic_unmask_irq(struct irq_data *d) { - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; + struct ipic *ipic = ipic_from_irq(d->irq); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; u32 temp; + raw_spin_lock_irqsave(&ipic_lock, flags); + temp = ipic_read(ipic->regs, ipic_info[src].mask); temp |= (1 << (31 - ipic_info[src].bit)); ipic_write(ipic->regs, ipic_info[src].mask, temp); + + raw_spin_unlock_irqrestore(&ipic_lock, flags); } -static void ipic_disable_irq(unsigned int irq) +static void ipic_mask_irq(struct irq_data *d) { - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; + struct ipic *ipic = ipic_from_irq(d->irq); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; u32 temp; + raw_spin_lock_irqsave(&ipic_lock, flags); + temp = ipic_read(ipic->regs, ipic_info[src].mask); temp &= ~(1 << (31 - ipic_info[src].bit)); ipic_write(ipic->regs, ipic_info[src].mask, temp); + + /* mb() can't guarantee that masking is finished. But it does finish + * for nearly all cases. */ + mb(); + + raw_spin_unlock_irqrestore(&ipic_lock, flags); } -static void ipic_disable_irq_and_ack(unsigned int irq) +static void ipic_ack_irq(struct irq_data *d) { - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; + struct ipic *ipic = ipic_from_irq(d->irq); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; u32 temp; - ipic_disable_irq(irq); + raw_spin_lock_irqsave(&ipic_lock, flags); - temp = ipic_read(ipic->regs, ipic_info[src].pend); - temp |= (1 << (31 - ipic_info[src].bit)); - ipic_write(ipic->regs, ipic_info[src].pend, temp); + temp = 1 << (31 - ipic_info[src].bit); + ipic_write(ipic->regs, ipic_info[src].ack, temp); + + /* mb() can't guarantee that ack is finished. But it does finish + * for nearly all cases. */ + mb(); + + raw_spin_unlock_irqrestore(&ipic_lock, flags); } -static void ipic_end_irq(unsigned int irq) +static void ipic_mask_irq_and_ack(struct irq_data *d) { - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - ipic_enable_irq(irq); + struct ipic *ipic = ipic_from_irq(d->irq); + unsigned int src = irqd_to_hwirq(d); + unsigned long flags; + u32 temp; + + raw_spin_lock_irqsave(&ipic_lock, flags); + + temp = ipic_read(ipic->regs, ipic_info[src].mask); + temp &= ~(1 << (31 - ipic_info[src].bit)); + ipic_write(ipic->regs, ipic_info[src].mask, temp); + + temp = 1 << (31 - ipic_info[src].bit); + ipic_write(ipic->regs, ipic_info[src].ack, temp); + + /* mb() can't guarantee that ack is finished. But it does finish + * for nearly all cases. */ + mb(); + + raw_spin_unlock_irqrestore(&ipic_lock, flags); } -struct hw_interrupt_type ipic = { - .typename = " IPIC ", - .enable = ipic_enable_irq, - .disable = ipic_disable_irq, - .ack = ipic_disable_irq_and_ack, - .end = ipic_end_irq, +static int ipic_set_irq_type(struct irq_data *d, unsigned int flow_type) +{ + struct ipic *ipic = ipic_from_irq(d->irq); + unsigned int src = irqd_to_hwirq(d); + unsigned int vold, vnew, edibit; + + if (flow_type == IRQ_TYPE_NONE) + flow_type = IRQ_TYPE_LEVEL_LOW; + + /* ipic supports only low assertion and high-to-low change senses + */ + if (!(flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))) { + printk(KERN_ERR "ipic: sense type 0x%x not supported\n", + flow_type); + return -EINVAL; + } + /* ipic supports only edge mode on external interrupts */ + if ((flow_type & IRQ_TYPE_EDGE_FALLING) && !ipic_info[src].ack) { + printk(KERN_ERR "ipic: edge sense not supported on internal " + "interrupts\n"); + return -EINVAL; + + } + + irqd_set_trigger_type(d, flow_type); + if (flow_type & IRQ_TYPE_LEVEL_LOW) { + __irq_set_handler_locked(d->irq, handle_level_irq); + d->chip = &ipic_level_irq_chip; + } else { + __irq_set_handler_locked(d->irq, handle_edge_irq); + d->chip = &ipic_edge_irq_chip; + } + + /* only EXT IRQ senses are programmable on ipic + * internal IRQ senses are LEVEL_LOW + */ + if (src == IPIC_IRQ_EXT0) + edibit = 15; + else + if (src >= IPIC_IRQ_EXT1 && src <= IPIC_IRQ_EXT7) + edibit = (14 - (src - IPIC_IRQ_EXT1)); + else + return (flow_type & IRQ_TYPE_LEVEL_LOW) ? 0 : -EINVAL; + + vold = ipic_read(ipic->regs, IPIC_SECNR); + if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING) { + vnew = vold | (1 << edibit); + } else { + vnew = vold & ~(1 << edibit); + } + if (vold != vnew) + ipic_write(ipic->regs, IPIC_SECNR, vnew); + return IRQ_SET_MASK_OK_NOCOPY; +} + +/* level interrupts and edge interrupts have different ack operations */ +static struct irq_chip ipic_level_irq_chip = { + .name = "IPIC", + .irq_unmask = ipic_unmask_irq, + .irq_mask = ipic_mask_irq, + .irq_mask_ack = ipic_mask_irq, + .irq_set_type = ipic_set_irq_type, }; -void __init ipic_init(phys_addr_t phys_addr, - unsigned int flags, - unsigned int irq_offset, - unsigned char *senses, - unsigned int senses_count) +static struct irq_chip ipic_edge_irq_chip = { + .name = "IPIC", + .irq_unmask = ipic_unmask_irq, + .irq_mask = ipic_mask_irq, + .irq_mask_ack = ipic_mask_irq_and_ack, + .irq_ack = ipic_ack_irq, + .irq_set_type = ipic_set_irq_type, +}; + +static int ipic_host_match(struct irq_domain *h, struct device_node *node) +{ + /* Exact match, unless ipic node is NULL */ + return h->of_node == NULL || h->of_node == node; +} + +static int ipic_host_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) { - u32 i, temp = 0; + struct ipic *ipic = h->host_data; + + irq_set_chip_data(virq, ipic); + irq_set_chip_and_handler(virq, &ipic_level_irq_chip, handle_level_irq); + + /* Set default irq type */ + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static struct irq_domain_ops ipic_host_ops = { + .match = ipic_host_match, + .map = ipic_host_map, + .xlate = irq_domain_xlate_onetwocell, +}; - primary_ipic = &p_ipic; - primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE); +struct ipic * __init ipic_init(struct device_node *node, unsigned int flags) +{ + struct ipic *ipic; + struct resource res; + u32 temp = 0, ret; + + ret = of_address_to_resource(node, 0, &res); + if (ret) + return NULL; + + ipic = kzalloc(sizeof(*ipic), GFP_KERNEL); + if (ipic == NULL) + return NULL; + + ipic->irqhost = irq_domain_add_linear(node, NR_IPIC_INTS, + &ipic_host_ops, ipic); + if (ipic->irqhost == NULL) { + kfree(ipic); + return NULL; + } - primary_ipic->irq_offset = irq_offset; + ipic->regs = ioremap(res.start, resource_size(&res)); - ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0); + /* init hw */ + ipic_write(ipic->regs, IPIC_SICNR, 0x0); /* default priority scheme is grouped. If spread mode is required * configure SICFR accordingly */ if (flags & IPIC_SPREADMODE_GRP_A) temp |= SICFR_IPSA; + if (flags & IPIC_SPREADMODE_GRP_B) + temp |= SICFR_IPSB; + if (flags & IPIC_SPREADMODE_GRP_C) + temp |= SICFR_IPSC; if (flags & IPIC_SPREADMODE_GRP_D) temp |= SICFR_IPSD; if (flags & IPIC_SPREADMODE_MIX_A) @@ -453,49 +739,40 @@ void __init ipic_init(phys_addr_t phys_addr, if (flags & IPIC_SPREADMODE_MIX_B) temp |= SICFR_MPSB; - ipic_write(primary_ipic->regs, IPIC_SICNR, temp); + ipic_write(ipic->regs, IPIC_SICFR, temp); /* handle MCP route */ temp = 0; if (flags & IPIC_DISABLE_MCP_OUT) temp = SERCR_MCPR; - ipic_write(primary_ipic->regs, IPIC_SERCR, temp); + ipic_write(ipic->regs, IPIC_SERCR, temp); /* handle routing of IRQ0 to MCP */ - temp = ipic_read(primary_ipic->regs, IPIC_SEMSR); + temp = ipic_read(ipic->regs, IPIC_SEMSR); if (flags & IPIC_IRQ0_MCP) temp |= SEMSR_SIRQ0; else temp &= ~SEMSR_SIRQ0; - ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); + ipic_write(ipic->regs, IPIC_SEMSR, temp); - for (i = 0 ; i < NR_IPIC_INTS ; i++) { - irq_desc[i+irq_offset].handler = &ipic; - irq_desc[i+irq_offset].status = IRQ_LEVEL; - } + primary_ipic = ipic; + irq_set_default_host(primary_ipic->irqhost); - temp = 0; - for (i = 0 ; i < senses_count ; i++) { - if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) { - temp |= 1 << (15 - i); - if (i != 0) - irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0; - else - irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0; - } - } - ipic_write(primary_ipic->regs, IPIC_SECNR, temp); + ipic_write(ipic->regs, IPIC_SIMSR_H, 0); + ipic_write(ipic->regs, IPIC_SIMSR_L, 0); - printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS, - senses_count, primary_ipic->regs); + printk ("IPIC (%d IRQ sources) at %p\n", NR_IPIC_INTS, + primary_ipic->regs); + + return ipic; } -int ipic_set_priority(unsigned int irq, unsigned int priority) +int ipic_set_priority(unsigned int virq, unsigned int priority) { - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; + struct ipic *ipic = ipic_from_irq(virq); + unsigned int src = virq_to_hw(virq); u32 temp; if (priority > 7) @@ -520,10 +797,10 @@ int ipic_set_priority(unsigned int irq, unsigned int priority) return 0; } -void ipic_set_highest_priority(unsigned int irq) +void ipic_set_highest_priority(unsigned int virq) { - struct ipic *ipic = ipic_from_irq(irq); - unsigned int src = irq - ipic->irq_offset; + struct ipic *ipic = ipic_from_irq(virq); + unsigned int src = virq_to_hw(virq); u32 temp; temp = ipic_read(ipic->regs, IPIC_SICFR); @@ -537,37 +814,12 @@ void ipic_set_highest_priority(unsigned int irq) void ipic_set_default_priority(void) { - ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0); - ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1); - ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2); - ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3); - ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4); - ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5); - ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6); - ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7); - - ipic_set_priority(MPC83xx_IRQ_UART1, 0); - ipic_set_priority(MPC83xx_IRQ_UART2, 1); - ipic_set_priority(MPC83xx_IRQ_SEC2, 2); - ipic_set_priority(MPC83xx_IRQ_IIC1, 5); - ipic_set_priority(MPC83xx_IRQ_IIC2, 6); - ipic_set_priority(MPC83xx_IRQ_SPI, 7); - ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0); - ipic_set_priority(MPC83xx_IRQ_PIT, 1); - ipic_set_priority(MPC83xx_IRQ_PCI1, 2); - ipic_set_priority(MPC83xx_IRQ_PCI2, 3); - ipic_set_priority(MPC83xx_IRQ_EXT0, 4); - ipic_set_priority(MPC83xx_IRQ_EXT1, 5); - ipic_set_priority(MPC83xx_IRQ_EXT2, 6); - ipic_set_priority(MPC83xx_IRQ_EXT3, 7); - ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0); - ipic_set_priority(MPC83xx_IRQ_MU, 1); - ipic_set_priority(MPC83xx_IRQ_SBA, 2); - ipic_set_priority(MPC83xx_IRQ_DMA, 3); - ipic_set_priority(MPC83xx_IRQ_EXT4, 4); - ipic_set_priority(MPC83xx_IRQ_EXT5, 5); - ipic_set_priority(MPC83xx_IRQ_EXT6, 6); - ipic_set_priority(MPC83xx_IRQ_EXT7, 7); + ipic_write(primary_ipic->regs, IPIC_SIPRR_A, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_B, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_C, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SIPRR_D, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SMPRR_A, IPIC_PRIORITY_DEFAULT); + ipic_write(primary_ipic->regs, IPIC_SMPRR_B, IPIC_PRIORITY_DEFAULT); } void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) @@ -600,47 +852,102 @@ void ipic_clear_mcp_status(u32 mask) ipic_write(primary_ipic->regs, IPIC_SERMR, mask); } -/* Return an interrupt vector or -1 if no interrupt is pending. */ -int ipic_get_irq(struct pt_regs *regs) +/* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ +unsigned int ipic_get_irq(void) { int irq; - irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f; + BUG_ON(primary_ipic == NULL); + +#define IPIC_SIVCR_VECTOR_MASK 0x7f + irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & IPIC_SIVCR_VECTOR_MASK; if (irq == 0) /* 0 --> no irq is pending */ - irq = -1; + return NO_IRQ; - return irq; + return irq_linear_revmap(primary_ipic->irqhost, irq); } -static struct sysdev_class ipic_sysclass = { - set_kset_name("ipic"), -}; +#ifdef CONFIG_SUSPEND +static struct { + u32 sicfr; + u32 siprr[2]; + u32 simsr[2]; + u32 sicnr; + u32 smprr[2]; + u32 semsr; + u32 secnr; + u32 sermr; + u32 sercr; +} ipic_saved_state; + +static int ipic_suspend(void) +{ + struct ipic *ipic = primary_ipic; -static struct sys_device device_ipic = { - .id = 0, - .cls = &ipic_sysclass, -}; + ipic_saved_state.sicfr = ipic_read(ipic->regs, IPIC_SICFR); + ipic_saved_state.siprr[0] = ipic_read(ipic->regs, IPIC_SIPRR_A); + ipic_saved_state.siprr[1] = ipic_read(ipic->regs, IPIC_SIPRR_D); + ipic_saved_state.simsr[0] = ipic_read(ipic->regs, IPIC_SIMSR_H); + ipic_saved_state.simsr[1] = ipic_read(ipic->regs, IPIC_SIMSR_L); + ipic_saved_state.sicnr = ipic_read(ipic->regs, IPIC_SICNR); + ipic_saved_state.smprr[0] = ipic_read(ipic->regs, IPIC_SMPRR_A); + ipic_saved_state.smprr[1] = ipic_read(ipic->regs, IPIC_SMPRR_B); + ipic_saved_state.semsr = ipic_read(ipic->regs, IPIC_SEMSR); + ipic_saved_state.secnr = ipic_read(ipic->regs, IPIC_SECNR); + ipic_saved_state.sermr = ipic_read(ipic->regs, IPIC_SERMR); + ipic_saved_state.sercr = ipic_read(ipic->regs, IPIC_SERCR); + + if (fsl_deep_sleep()) { + /* In deep sleep, make sure there can be no + * pending interrupts, as this can cause + * problems on 831x. + */ + ipic_write(ipic->regs, IPIC_SIMSR_H, 0); + ipic_write(ipic->regs, IPIC_SIMSR_L, 0); + ipic_write(ipic->regs, IPIC_SEMSR, 0); + ipic_write(ipic->regs, IPIC_SERMR, 0); + } -static int __init init_ipic_sysfs(void) + return 0; +} + +static void ipic_resume(void) { - int rc; + struct ipic *ipic = primary_ipic; - if (!primary_ipic->regs) - return -ENODEV; - printk(KERN_DEBUG "Registering ipic with sysfs...\n"); + ipic_write(ipic->regs, IPIC_SICFR, ipic_saved_state.sicfr); + ipic_write(ipic->regs, IPIC_SIPRR_A, ipic_saved_state.siprr[0]); + ipic_write(ipic->regs, IPIC_SIPRR_D, ipic_saved_state.siprr[1]); + ipic_write(ipic->regs, IPIC_SIMSR_H, ipic_saved_state.simsr[0]); + ipic_write(ipic->regs, IPIC_SIMSR_L, ipic_saved_state.simsr[1]); + ipic_write(ipic->regs, IPIC_SICNR, ipic_saved_state.sicnr); + ipic_write(ipic->regs, IPIC_SMPRR_A, ipic_saved_state.smprr[0]); + ipic_write(ipic->regs, IPIC_SMPRR_B, ipic_saved_state.smprr[1]); + ipic_write(ipic->regs, IPIC_SEMSR, ipic_saved_state.semsr); + ipic_write(ipic->regs, IPIC_SECNR, ipic_saved_state.secnr); + ipic_write(ipic->regs, IPIC_SERMR, ipic_saved_state.sermr); + ipic_write(ipic->regs, IPIC_SERCR, ipic_saved_state.sercr); +} +#else +#define ipic_suspend NULL +#define ipic_resume NULL +#endif + +static struct syscore_ops ipic_syscore_ops = { + .suspend = ipic_suspend, + .resume = ipic_resume, +}; - rc = sysdev_class_register(&ipic_sysclass); - if (rc) { - printk(KERN_ERR "Failed registering ipic sys class\n"); - return -ENODEV; - } - rc = sysdev_register(&device_ipic); - if (rc) { - printk(KERN_ERR "Failed registering ipic sys device\n"); +static int __init init_ipic_syscore(void) +{ + if (!primary_ipic || !primary_ipic->regs) return -ENODEV; - } + + printk(KERN_DEBUG "Registering ipic system core operations\n"); + register_syscore_ops(&ipic_syscore_ops); + return 0; } -subsys_initcall(init_ipic_sysfs); +subsys_initcall(init_ipic_syscore); |
