|  | /* | 
|  | * arch/arm/mach-lpc32xx/irq.c | 
|  | * | 
|  | * Author: Kevin Wells <kevin.wells@nxp.com> | 
|  | * | 
|  | * Copyright (C) 2010 NXP Semiconductors | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or | 
|  | * (at your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/irq.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_address.h> | 
|  | #include <linux/of_irq.h> | 
|  | #include <linux/irqdomain.h> | 
|  | #include <linux/module.h> | 
|  |  | 
|  | #include <mach/irqs.h> | 
|  | #include <mach/hardware.h> | 
|  | #include <mach/platform.h> | 
|  | #include "common.h" | 
|  |  | 
|  | /* | 
|  | * Default value representing the Activation polarity of all internal | 
|  | * interrupt sources | 
|  | */ | 
|  | #define MIC_APR_DEFAULT		0x3FF0EFE0 | 
|  | #define SIC1_APR_DEFAULT	0xFBD27186 | 
|  | #define SIC2_APR_DEFAULT	0x801810C0 | 
|  |  | 
|  | /* | 
|  | * Default value representing the Activation Type of all internal | 
|  | * interrupt sources. All are level sensitive. | 
|  | */ | 
|  | #define MIC_ATR_DEFAULT		0x00000000 | 
|  | #define SIC1_ATR_DEFAULT	0x00026000 | 
|  | #define SIC2_ATR_DEFAULT	0x00000000 | 
|  |  | 
|  | static struct irq_domain *lpc32xx_mic_domain; | 
|  | static struct device_node *lpc32xx_mic_np; | 
|  |  | 
|  | struct lpc32xx_event_group_regs { | 
|  | void __iomem *enab_reg; | 
|  | void __iomem *edge_reg; | 
|  | void __iomem *maskstat_reg; | 
|  | void __iomem *rawstat_reg; | 
|  | }; | 
|  |  | 
|  | static const struct lpc32xx_event_group_regs lpc32xx_event_int_regs = { | 
|  | .enab_reg = LPC32XX_CLKPWR_INT_ER, | 
|  | .edge_reg = LPC32XX_CLKPWR_INT_AP, | 
|  | .maskstat_reg = LPC32XX_CLKPWR_INT_SR, | 
|  | .rawstat_reg = LPC32XX_CLKPWR_INT_RS, | 
|  | }; | 
|  |  | 
|  | static const struct lpc32xx_event_group_regs lpc32xx_event_pin_regs = { | 
|  | .enab_reg = LPC32XX_CLKPWR_PIN_ER, | 
|  | .edge_reg = LPC32XX_CLKPWR_PIN_AP, | 
|  | .maskstat_reg = LPC32XX_CLKPWR_PIN_SR, | 
|  | .rawstat_reg = LPC32XX_CLKPWR_PIN_RS, | 
|  | }; | 
|  |  | 
|  | struct lpc32xx_event_info { | 
|  | const struct lpc32xx_event_group_regs *event_group; | 
|  | u32 mask; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Maps an IRQ number to and event mask and register | 
|  | */ | 
|  | static const struct lpc32xx_event_info lpc32xx_events[NR_IRQS] = { | 
|  | [IRQ_LPC32XX_GPI_08] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_08_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_09] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_09_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_19] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_19_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_07] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_07_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_00] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_00_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_01] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_01_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_02] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_02_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_03] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_03_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_04] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_04_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_05] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_05_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_06] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_06_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPI_28] = { | 
|  | .event_group = &lpc32xx_event_pin_regs, | 
|  | .mask = LPC32XX_CLKPWR_EXTSRC_GPI_28_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_00] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_00_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_01] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_01_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_02] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_02_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_03] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_03_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_04] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_04_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_GPIO_05] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_GPIO_05_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_KEY] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_KEY_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_ETHERNET] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_MAC_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_USB_OTG_ATX] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_USBATXINT_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_USB_HOST] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_USB_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_RTC] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_RTC_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_MSTIMER] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_MSTIMER_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_TS_AUX] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_TS_AUX_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_TS_P] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_TS_P_BIT, | 
|  | }, | 
|  | [IRQ_LPC32XX_TS_IRQ] = { | 
|  | .event_group = &lpc32xx_event_int_regs, | 
|  | .mask = LPC32XX_CLKPWR_INTSRC_ADC_BIT, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void get_controller(unsigned int irq, unsigned int *base, | 
|  | unsigned int *irqbit) | 
|  | { | 
|  | if (irq < 32) { | 
|  | *base = LPC32XX_MIC_BASE; | 
|  | *irqbit = 1 << irq; | 
|  | } else if (irq < 64) { | 
|  | *base = LPC32XX_SIC1_BASE; | 
|  | *irqbit = 1 << (irq - 32); | 
|  | } else { | 
|  | *base = LPC32XX_SIC2_BASE; | 
|  | *irqbit = 1 << (irq - 64); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void lpc32xx_mask_irq(struct irq_data *d) | 
|  | { | 
|  | unsigned int reg, ctrl, mask; | 
|  |  | 
|  | get_controller(d->hwirq, &ctrl, &mask); | 
|  |  | 
|  | reg = __raw_readl(LPC32XX_INTC_MASK(ctrl)) & ~mask; | 
|  | __raw_writel(reg, LPC32XX_INTC_MASK(ctrl)); | 
|  | } | 
|  |  | 
|  | static void lpc32xx_unmask_irq(struct irq_data *d) | 
|  | { | 
|  | unsigned int reg, ctrl, mask; | 
|  |  | 
|  | get_controller(d->hwirq, &ctrl, &mask); | 
|  |  | 
|  | reg = __raw_readl(LPC32XX_INTC_MASK(ctrl)) | mask; | 
|  | __raw_writel(reg, LPC32XX_INTC_MASK(ctrl)); | 
|  | } | 
|  |  | 
|  | static void lpc32xx_ack_irq(struct irq_data *d) | 
|  | { | 
|  | unsigned int ctrl, mask; | 
|  |  | 
|  | get_controller(d->hwirq, &ctrl, &mask); | 
|  |  | 
|  | __raw_writel(mask, LPC32XX_INTC_RAW_STAT(ctrl)); | 
|  |  | 
|  | /* Also need to clear pending wake event */ | 
|  | if (lpc32xx_events[d->hwirq].mask != 0) | 
|  | __raw_writel(lpc32xx_events[d->hwirq].mask, | 
|  | lpc32xx_events[d->hwirq].event_group->rawstat_reg); | 
|  | } | 
|  |  | 
|  | static void __lpc32xx_set_irq_type(unsigned int irq, int use_high_level, | 
|  | int use_edge) | 
|  | { | 
|  | unsigned int reg, ctrl, mask; | 
|  |  | 
|  | get_controller(irq, &ctrl, &mask); | 
|  |  | 
|  | /* Activation level, high or low */ | 
|  | reg = __raw_readl(LPC32XX_INTC_POLAR(ctrl)); | 
|  | if (use_high_level) | 
|  | reg |= mask; | 
|  | else | 
|  | reg &= ~mask; | 
|  | __raw_writel(reg, LPC32XX_INTC_POLAR(ctrl)); | 
|  |  | 
|  | /* Activation type, edge or level */ | 
|  | reg = __raw_readl(LPC32XX_INTC_ACT_TYPE(ctrl)); | 
|  | if (use_edge) | 
|  | reg |= mask; | 
|  | else | 
|  | reg &= ~mask; | 
|  | __raw_writel(reg, LPC32XX_INTC_ACT_TYPE(ctrl)); | 
|  |  | 
|  | /* Use same polarity for the wake events */ | 
|  | if (lpc32xx_events[irq].mask != 0) { | 
|  | reg = __raw_readl(lpc32xx_events[irq].event_group->edge_reg); | 
|  |  | 
|  | if (use_high_level) | 
|  | reg |= lpc32xx_events[irq].mask; | 
|  | else | 
|  | reg &= ~lpc32xx_events[irq].mask; | 
|  |  | 
|  | __raw_writel(reg, lpc32xx_events[irq].event_group->edge_reg); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int lpc32xx_set_irq_type(struct irq_data *d, unsigned int type) | 
|  | { | 
|  | switch (type) { | 
|  | case IRQ_TYPE_EDGE_RISING: | 
|  | /* Rising edge sensitive */ | 
|  | __lpc32xx_set_irq_type(d->hwirq, 1, 1); | 
|  | __irq_set_handler_locked(d->hwirq, handle_edge_irq); | 
|  | break; | 
|  |  | 
|  | case IRQ_TYPE_EDGE_FALLING: | 
|  | /* Falling edge sensitive */ | 
|  | __lpc32xx_set_irq_type(d->hwirq, 0, 1); | 
|  | __irq_set_handler_locked(d->hwirq, handle_edge_irq); | 
|  | break; | 
|  |  | 
|  | case IRQ_TYPE_LEVEL_LOW: | 
|  | /* Low level sensitive */ | 
|  | __lpc32xx_set_irq_type(d->hwirq, 0, 0); | 
|  | __irq_set_handler_locked(d->hwirq, handle_level_irq); | 
|  | break; | 
|  |  | 
|  | case IRQ_TYPE_LEVEL_HIGH: | 
|  | /* High level sensitive */ | 
|  | __lpc32xx_set_irq_type(d->hwirq, 1, 0); | 
|  | __irq_set_handler_locked(d->hwirq, handle_level_irq); | 
|  | break; | 
|  |  | 
|  | /* Other modes are not supported */ | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int lpc32xx_irq_wake(struct irq_data *d, unsigned int state) | 
|  | { | 
|  | unsigned long eventreg; | 
|  |  | 
|  | if (lpc32xx_events[d->hwirq].mask != 0) { | 
|  | eventreg = __raw_readl(lpc32xx_events[d->hwirq]. | 
|  | event_group->enab_reg); | 
|  |  | 
|  | if (state) | 
|  | eventreg |= lpc32xx_events[d->hwirq].mask; | 
|  | else { | 
|  | eventreg &= ~lpc32xx_events[d->hwirq].mask; | 
|  |  | 
|  | /* | 
|  | * When disabling the wakeup, clear the latched | 
|  | * event | 
|  | */ | 
|  | __raw_writel(lpc32xx_events[d->hwirq].mask, | 
|  | lpc32xx_events[d->hwirq]. | 
|  | event_group->rawstat_reg); | 
|  | } | 
|  |  | 
|  | __raw_writel(eventreg, | 
|  | lpc32xx_events[d->hwirq].event_group->enab_reg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Clear event */ | 
|  | __raw_writel(lpc32xx_events[d->hwirq].mask, | 
|  | lpc32xx_events[d->hwirq].event_group->rawstat_reg); | 
|  |  | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | static void __init lpc32xx_set_default_mappings(unsigned int apr, | 
|  | unsigned int atr, unsigned int offset) | 
|  | { | 
|  | unsigned int i; | 
|  |  | 
|  | /* Set activation levels for each interrupt */ | 
|  | i = 0; | 
|  | while (i < 32) { | 
|  | __lpc32xx_set_irq_type(offset + i, ((apr >> i) & 0x1), | 
|  | ((atr >> i) & 0x1)); | 
|  | i++; | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct irq_chip lpc32xx_irq_chip = { | 
|  | .name = "MIC", | 
|  | .irq_ack = lpc32xx_ack_irq, | 
|  | .irq_mask = lpc32xx_mask_irq, | 
|  | .irq_unmask = lpc32xx_unmask_irq, | 
|  | .irq_set_type = lpc32xx_set_irq_type, | 
|  | .irq_set_wake = lpc32xx_irq_wake | 
|  | }; | 
|  |  | 
|  | static void lpc32xx_sic1_handler(unsigned int irq, struct irq_desc *desc) | 
|  | { | 
|  | unsigned long ints = __raw_readl(LPC32XX_INTC_STAT(LPC32XX_SIC1_BASE)); | 
|  |  | 
|  | while (ints != 0) { | 
|  | int irqno = fls(ints) - 1; | 
|  |  | 
|  | ints &= ~(1 << irqno); | 
|  |  | 
|  | generic_handle_irq(LPC32XX_SIC1_IRQ(irqno)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void lpc32xx_sic2_handler(unsigned int irq, struct irq_desc *desc) | 
|  | { | 
|  | unsigned long ints = __raw_readl(LPC32XX_INTC_STAT(LPC32XX_SIC2_BASE)); | 
|  |  | 
|  | while (ints != 0) { | 
|  | int irqno = fls(ints) - 1; | 
|  |  | 
|  | ints &= ~(1 << irqno); | 
|  |  | 
|  | generic_handle_irq(LPC32XX_SIC2_IRQ(irqno)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static int __init __lpc32xx_mic_of_init(struct device_node *node, | 
|  | struct device_node *parent) | 
|  | { | 
|  | lpc32xx_mic_np = node; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id mic_of_match[] __initconst = { | 
|  | { .compatible = "nxp,lpc3220-mic", .data = __lpc32xx_mic_of_init }, | 
|  | { } | 
|  | }; | 
|  |  | 
|  | void __init lpc32xx_init_irq(void) | 
|  | { | 
|  | unsigned int i; | 
|  |  | 
|  | /* Setup MIC */ | 
|  | __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_MIC_BASE)); | 
|  | __raw_writel(MIC_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_MIC_BASE)); | 
|  | __raw_writel(MIC_ATR_DEFAULT, LPC32XX_INTC_ACT_TYPE(LPC32XX_MIC_BASE)); | 
|  |  | 
|  | /* Setup SIC1 */ | 
|  | __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE)); | 
|  | __raw_writel(SIC1_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC1_BASE)); | 
|  | __raw_writel(SIC1_ATR_DEFAULT, | 
|  | LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC1_BASE)); | 
|  |  | 
|  | /* Setup SIC2 */ | 
|  | __raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE)); | 
|  | __raw_writel(SIC2_APR_DEFAULT, LPC32XX_INTC_POLAR(LPC32XX_SIC2_BASE)); | 
|  | __raw_writel(SIC2_ATR_DEFAULT, | 
|  | LPC32XX_INTC_ACT_TYPE(LPC32XX_SIC2_BASE)); | 
|  |  | 
|  | /* Configure supported IRQ's */ | 
|  | for (i = 0; i < NR_IRQS; i++) { | 
|  | irq_set_chip_and_handler(i, &lpc32xx_irq_chip, | 
|  | handle_level_irq); | 
|  | set_irq_flags(i, IRQF_VALID); | 
|  | } | 
|  |  | 
|  | /* Set default mappings */ | 
|  | lpc32xx_set_default_mappings(MIC_APR_DEFAULT, MIC_ATR_DEFAULT, 0); | 
|  | lpc32xx_set_default_mappings(SIC1_APR_DEFAULT, SIC1_ATR_DEFAULT, 32); | 
|  | lpc32xx_set_default_mappings(SIC2_APR_DEFAULT, SIC2_ATR_DEFAULT, 64); | 
|  |  | 
|  | /* Initially disable all wake events */ | 
|  | __raw_writel(0, LPC32XX_CLKPWR_P01_ER); | 
|  | __raw_writel(0, LPC32XX_CLKPWR_INT_ER); | 
|  | __raw_writel(0, LPC32XX_CLKPWR_PIN_ER); | 
|  |  | 
|  | /* | 
|  | * Default wake activation polarities, all pin sources are low edge | 
|  | * triggered | 
|  | */ | 
|  | __raw_writel(LPC32XX_CLKPWR_INTSRC_TS_P_BIT | | 
|  | LPC32XX_CLKPWR_INTSRC_MSTIMER_BIT | | 
|  | LPC32XX_CLKPWR_INTSRC_RTC_BIT, | 
|  | LPC32XX_CLKPWR_INT_AP); | 
|  | __raw_writel(0, LPC32XX_CLKPWR_PIN_AP); | 
|  |  | 
|  | /* Clear latched wake event states */ | 
|  | __raw_writel(__raw_readl(LPC32XX_CLKPWR_PIN_RS), | 
|  | LPC32XX_CLKPWR_PIN_RS); | 
|  | __raw_writel(__raw_readl(LPC32XX_CLKPWR_INT_RS), | 
|  | LPC32XX_CLKPWR_INT_RS); | 
|  |  | 
|  | of_irq_init(mic_of_match); | 
|  |  | 
|  | lpc32xx_mic_domain = irq_domain_add_legacy(lpc32xx_mic_np, NR_IRQS, | 
|  | 0, 0, &irq_domain_simple_ops, | 
|  | NULL); | 
|  | if (!lpc32xx_mic_domain) | 
|  | panic("Unable to add MIC irq domain\n"); | 
|  |  | 
|  | /* MIC SUBIRQx interrupts will route handling to the chain handlers */ | 
|  | irq_set_chained_handler(IRQ_LPC32XX_SUB1IRQ, lpc32xx_sic1_handler); | 
|  | irq_set_chained_handler(IRQ_LPC32XX_SUB2IRQ, lpc32xx_sic2_handler); | 
|  | } |