diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/clocksource/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clocksource/metag_generic.c | 198 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
| -rw-r--r-- | drivers/irqchip/irq-metag-ext.c | 868 | ||||
| -rw-r--r-- | drivers/irqchip/irq-metag.c | 343 |
6 files changed, 1417 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e920cbe519fa..e507ab7df60b 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig | |||
| @@ -62,3 +62,8 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK | |||
| 62 | 62 | ||
| 63 | config ARM_ARCH_TIMER | 63 | config ARM_ARCH_TIMER |
| 64 | bool | 64 | bool |
| 65 | |||
| 66 | config CLKSRC_METAG_GENERIC | ||
| 67 | def_bool y if METAG | ||
| 68 | help | ||
| 69 | This option enables support for the Meta per-thread timers. | ||
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 7d671b85a98e..4d8283aec5b5 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
| @@ -21,3 +21,4 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o | |||
| 21 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o | 21 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o |
| 22 | 22 | ||
| 23 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 23 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
| 24 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | ||
diff --git a/drivers/clocksource/metag_generic.c b/drivers/clocksource/metag_generic.c new file mode 100644 index 000000000000..ade7513a11d1 --- /dev/null +++ b/drivers/clocksource/metag_generic.c | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2013 Imagination Technologies Ltd. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | * | ||
| 16 | * | ||
| 17 | * Support for Meta per-thread timers. | ||
| 18 | * | ||
| 19 | * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used | ||
| 20 | * as a free-running time base (hz clocksource), and the interrupt timer | ||
| 21 | * (TXTIMERI) is used for the timer interrupt (clock event). Both counters | ||
| 22 | * traditionally count at approximately 1MHz. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <clocksource/metag_generic.h> | ||
| 26 | #include <linux/cpu.h> | ||
| 27 | #include <linux/errno.h> | ||
| 28 | #include <linux/sched.h> | ||
| 29 | #include <linux/kernel.h> | ||
| 30 | #include <linux/param.h> | ||
| 31 | #include <linux/time.h> | ||
| 32 | #include <linux/init.h> | ||
| 33 | #include <linux/proc_fs.h> | ||
| 34 | #include <linux/clocksource.h> | ||
| 35 | #include <linux/clockchips.h> | ||
| 36 | #include <linux/interrupt.h> | ||
| 37 | |||
| 38 | #include <asm/clock.h> | ||
| 39 | #include <asm/hwthread.h> | ||
| 40 | #include <asm/core_reg.h> | ||
| 41 | #include <asm/metag_mem.h> | ||
| 42 | #include <asm/tbx.h> | ||
| 43 | |||
| 44 | #define HARDWARE_FREQ 1000000 /* 1MHz */ | ||
| 45 | #define HARDWARE_DIV 1 /* divide by 1 = 1MHz clock */ | ||
| 46 | #define HARDWARE_TO_NS_SHIFT 10 /* convert ticks to ns */ | ||
| 47 | |||
| 48 | static unsigned int hwtimer_freq = HARDWARE_FREQ; | ||
| 49 | static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); | ||
| 50 | static DEFINE_PER_CPU(char [11], local_clockevent_name); | ||
| 51 | |||
| 52 | static int metag_timer_set_next_event(unsigned long delta, | ||
| 53 | struct clock_event_device *dev) | ||
| 54 | { | ||
| 55 | __core_reg_set(TXTIMERI, -delta); | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | static void metag_timer_set_mode(enum clock_event_mode mode, | ||
| 60 | struct clock_event_device *evt) | ||
| 61 | { | ||
| 62 | switch (mode) { | ||
| 63 | case CLOCK_EVT_MODE_ONESHOT: | ||
| 64 | case CLOCK_EVT_MODE_RESUME: | ||
| 65 | break; | ||
| 66 | |||
| 67 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
| 68 | /* We should disable the IRQ here */ | ||
| 69 | break; | ||
| 70 | |||
| 71 | case CLOCK_EVT_MODE_PERIODIC: | ||
| 72 | case CLOCK_EVT_MODE_UNUSED: | ||
| 73 | WARN_ON(1); | ||
| 74 | break; | ||
| 75 | }; | ||
| 76 | } | ||
| 77 | |||
| 78 | static cycle_t metag_clocksource_read(struct clocksource *cs) | ||
| 79 | { | ||
| 80 | return __core_reg_get(TXTIMER); | ||
| 81 | } | ||
| 82 | |||
| 83 | static struct clocksource clocksource_metag = { | ||
| 84 | .name = "META", | ||
| 85 | .rating = 200, | ||
| 86 | .mask = CLOCKSOURCE_MASK(32), | ||
| 87 | .read = metag_clocksource_read, | ||
| 88 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
| 89 | }; | ||
| 90 | |||
| 91 | static irqreturn_t metag_timer_interrupt(int irq, void *dummy) | ||
| 92 | { | ||
| 93 | struct clock_event_device *evt = &__get_cpu_var(local_clockevent); | ||
| 94 | |||
| 95 | evt->event_handler(evt); | ||
| 96 | |||
| 97 | return IRQ_HANDLED; | ||
| 98 | } | ||
| 99 | |||
| 100 | static struct irqaction metag_timer_irq = { | ||
| 101 | .name = "META core timer", | ||
| 102 | .handler = metag_timer_interrupt, | ||
| 103 | .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU, | ||
| 104 | }; | ||
| 105 | |||
| 106 | unsigned long long sched_clock(void) | ||
| 107 | { | ||
| 108 | unsigned long long ticks = __core_reg_get(TXTIMER); | ||
| 109 | return ticks << HARDWARE_TO_NS_SHIFT; | ||
| 110 | } | ||
| 111 | |||
| 112 | static void __cpuinit arch_timer_setup(unsigned int cpu) | ||
| 113 | { | ||
| 114 | unsigned int txdivtime; | ||
| 115 | struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); | ||
| 116 | char *name = per_cpu(local_clockevent_name, cpu); | ||
| 117 | |||
| 118 | txdivtime = __core_reg_get(TXDIVTIME); | ||
| 119 | |||
| 120 | txdivtime &= ~TXDIVTIME_DIV_BITS; | ||
| 121 | txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS); | ||
| 122 | |||
| 123 | __core_reg_set(TXDIVTIME, txdivtime); | ||
| 124 | |||
| 125 | sprintf(name, "META %d", cpu); | ||
| 126 | clk->name = name; | ||
| 127 | clk->features = CLOCK_EVT_FEAT_ONESHOT, | ||
| 128 | |||
| 129 | clk->rating = 200, | ||
| 130 | clk->shift = 12, | ||
| 131 | clk->irq = tbisig_map(TBID_SIGNUM_TRT), | ||
| 132 | clk->set_mode = metag_timer_set_mode, | ||
| 133 | clk->set_next_event = metag_timer_set_next_event, | ||
| 134 | |||
| 135 | clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift); | ||
| 136 | clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk); | ||
| 137 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
| 138 | clk->cpumask = cpumask_of(cpu); | ||
| 139 | |||
| 140 | clockevents_register_device(clk); | ||
| 141 | |||
| 142 | /* | ||
| 143 | * For all non-boot CPUs we need to synchronize our free | ||
| 144 | * running clock (TXTIMER) with the boot CPU's clock. | ||
| 145 | * | ||
| 146 | * While this won't be accurate, it should be close enough. | ||
| 147 | */ | ||
| 148 | if (cpu) { | ||
| 149 | unsigned int thread0 = cpu_2_hwthread_id[0]; | ||
| 150 | unsigned long val; | ||
| 151 | |||
| 152 | val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0); | ||
| 153 | __core_reg_set(TXTIMER, val); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, | ||
| 158 | unsigned long action, void *hcpu) | ||
| 159 | { | ||
| 160 | int cpu = (long)hcpu; | ||
| 161 | |||
| 162 | switch (action) { | ||
| 163 | case CPU_STARTING: | ||
| 164 | case CPU_STARTING_FROZEN: | ||
| 165 | arch_timer_setup(cpu); | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | |||
| 169 | return NOTIFY_OK; | ||
| 170 | } | ||
| 171 | |||
| 172 | static struct notifier_block __cpuinitdata arch_timer_cpu_nb = { | ||
| 173 | .notifier_call = arch_timer_cpu_notify, | ||
| 174 | }; | ||
| 175 | |||
| 176 | int __init metag_generic_timer_init(void) | ||
| 177 | { | ||
| 178 | /* | ||
| 179 | * On Meta 2 SoCs, the actual frequency of the timer is based on the | ||
| 180 | * Meta core clock speed divided by an integer, so it is only | ||
| 181 | * approximately 1MHz. Calculating the real frequency here drastically | ||
| 182 | * reduces clock skew on these SoCs. | ||
| 183 | */ | ||
| 184 | #ifdef CONFIG_METAG_META21 | ||
| 185 | hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1); | ||
| 186 | #endif | ||
| 187 | clocksource_register_hz(&clocksource_metag, hwtimer_freq); | ||
| 188 | |||
| 189 | setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); | ||
| 190 | |||
| 191 | /* Configure timer on boot CPU */ | ||
| 192 | arch_timer_setup(smp_processor_id()); | ||
| 193 | |||
| 194 | /* Hook cpu boot to configure other CPU's timers */ | ||
| 195 | register_cpu_notifier(&arch_timer_cpu_nb); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index e65fbf2cdf71..98e3b87bdf1b 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
| @@ -2,6 +2,8 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o | |||
| 2 | 2 | ||
| 3 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o | 3 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o |
| 4 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o | 4 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o |
| 5 | obj-$(CONFIG_METAG) += irq-metag-ext.o | ||
| 6 | obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o | ||
| 5 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o | 7 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o |
| 6 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 8 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
| 7 | obj-$(CONFIG_ARM_GIC) += irq-gic.o | 9 | obj-$(CONFIG_ARM_GIC) += irq-gic.o |
diff --git a/drivers/irqchip/irq-metag-ext.c b/drivers/irqchip/irq-metag-ext.c new file mode 100644 index 000000000000..92c41ab4dbfd --- /dev/null +++ b/drivers/irqchip/irq-metag-ext.c | |||
| @@ -0,0 +1,868 @@ | |||
| 1 | /* | ||
| 2 | * Meta External interrupt code. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2012 Imagination Technologies Ltd. | ||
| 5 | * | ||
| 6 | * External interrupts on Meta are configured at two-levels, in the CPU core and | ||
| 7 | * in the external trigger block. Interrupts from SoC peripherals are | ||
| 8 | * multiplexed onto a single Meta CPU "trigger" - traditionally it has always | ||
| 9 | * been trigger 2 (TR2). For info on how de-multiplexing happens check out | ||
| 10 | * meta_intc_irq_demux(). | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/irqchip/metag-ext.h> | ||
| 15 | #include <linux/irqdomain.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <linux/of.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/syscore_ops.h> | ||
| 20 | |||
| 21 | #include <asm/irq.h> | ||
| 22 | #include <asm/hwthread.h> | ||
| 23 | |||
| 24 | #define HWSTAT_STRIDE 8 | ||
| 25 | #define HWVEC_BLK_STRIDE 0x1000 | ||
| 26 | |||
| 27 | /** | ||
| 28 | * struct meta_intc_priv - private meta external interrupt data | ||
| 29 | * @nr_banks: Number of interrupt banks | ||
| 30 | * @domain: IRQ domain for all banks of external IRQs | ||
| 31 | * @unmasked: Record of unmasked IRQs | ||
| 32 | * @levels_altered: Record of altered level bits | ||
| 33 | */ | ||
| 34 | struct meta_intc_priv { | ||
| 35 | unsigned int nr_banks; | ||
| 36 | struct irq_domain *domain; | ||
| 37 | |||
| 38 | unsigned long unmasked[4]; | ||
| 39 | |||
| 40 | #ifdef CONFIG_METAG_SUSPEND_MEM | ||
| 41 | unsigned long levels_altered[4]; | ||
| 42 | #endif | ||
| 43 | }; | ||
| 44 | |||
| 45 | /* Private data for the one and only external interrupt controller */ | ||
| 46 | static struct meta_intc_priv meta_intc_priv; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * meta_intc_offset() - Get the offset into the bank of a hardware IRQ number | ||
| 50 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 51 | * | ||
| 52 | * Returns: Bit offset into the IRQ's bank registers | ||
| 53 | */ | ||
| 54 | static unsigned int meta_intc_offset(irq_hw_number_t hw) | ||
| 55 | { | ||
| 56 | return hw & 0x1f; | ||
| 57 | } | ||
| 58 | |||
| 59 | /** | ||
| 60 | * meta_intc_bank() - Get the bank number of a hardware IRQ number | ||
| 61 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 62 | * | ||
| 63 | * Returns: Bank number indicating which register the IRQ's bits are | ||
| 64 | */ | ||
| 65 | static unsigned int meta_intc_bank(irq_hw_number_t hw) | ||
| 66 | { | ||
| 67 | return hw >> 5; | ||
| 68 | } | ||
| 69 | |||
| 70 | /** | ||
| 71 | * meta_intc_stat_addr() - Get the address of a HWSTATEXT register | ||
| 72 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 73 | * | ||
| 74 | * Returns: Address of a HWSTATEXT register containing the status bit for | ||
| 75 | * the specified hardware IRQ number | ||
| 76 | */ | ||
| 77 | static void __iomem *meta_intc_stat_addr(irq_hw_number_t hw) | ||
| 78 | { | ||
| 79 | return (void __iomem *)(HWSTATEXT + | ||
| 80 | HWSTAT_STRIDE * meta_intc_bank(hw)); | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * meta_intc_level_addr() - Get the address of a HWLEVELEXT register | ||
| 85 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 86 | * | ||
| 87 | * Returns: Address of a HWLEVELEXT register containing the sense bit for | ||
| 88 | * the specified hardware IRQ number | ||
| 89 | */ | ||
| 90 | static void __iomem *meta_intc_level_addr(irq_hw_number_t hw) | ||
| 91 | { | ||
| 92 | return (void __iomem *)(HWLEVELEXT + | ||
| 93 | HWSTAT_STRIDE * meta_intc_bank(hw)); | ||
| 94 | } | ||
| 95 | |||
| 96 | /** | ||
| 97 | * meta_intc_mask_addr() - Get the address of a HWMASKEXT register | ||
| 98 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 99 | * | ||
| 100 | * Returns: Address of a HWMASKEXT register containing the mask bit for the | ||
| 101 | * specified hardware IRQ number | ||
| 102 | */ | ||
| 103 | static void __iomem *meta_intc_mask_addr(irq_hw_number_t hw) | ||
| 104 | { | ||
| 105 | return (void __iomem *)(HWMASKEXT + | ||
| 106 | HWSTAT_STRIDE * meta_intc_bank(hw)); | ||
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * meta_intc_vec_addr() - Get the vector address of a hardware interrupt | ||
| 111 | * @hw: Hardware IRQ number (within external trigger block) | ||
| 112 | * | ||
| 113 | * Returns: Address of a HWVECEXT register controlling the core trigger to | ||
| 114 | * vector the IRQ onto | ||
| 115 | */ | ||
| 116 | static inline void __iomem *meta_intc_vec_addr(irq_hw_number_t hw) | ||
| 117 | { | ||
| 118 | return (void __iomem *)(HWVEC0EXT + | ||
| 119 | HWVEC_BLK_STRIDE * meta_intc_bank(hw) + | ||
| 120 | HWVECnEXT_STRIDE * meta_intc_offset(hw)); | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | ||
| 124 | * meta_intc_startup_irq() - set up an external irq | ||
| 125 | * @data: data for the external irq to start up | ||
| 126 | * | ||
| 127 | * Multiplex interrupts for irq onto TR2. Clear any pending interrupts and | ||
| 128 | * unmask irq, both using the appropriate callbacks. | ||
| 129 | */ | ||
| 130 | static unsigned int meta_intc_startup_irq(struct irq_data *data) | ||
| 131 | { | ||
| 132 | irq_hw_number_t hw = data->hwirq; | ||
| 133 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 134 | int thread = hard_processor_id(); | ||
| 135 | |||
| 136 | /* Perform any necessary acking. */ | ||
| 137 | if (data->chip->irq_ack) | ||
| 138 | data->chip->irq_ack(data); | ||
| 139 | |||
| 140 | /* Wire up this interrupt to the core with HWVECxEXT. */ | ||
| 141 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | ||
| 142 | |||
| 143 | /* Perform any necessary unmasking. */ | ||
| 144 | data->chip->irq_unmask(data); | ||
| 145 | |||
| 146 | return 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * meta_intc_shutdown_irq() - turn off an external irq | ||
| 151 | * @data: data for the external irq to turn off | ||
| 152 | * | ||
| 153 | * Mask irq using the appropriate callback and stop muxing it onto TR2. | ||
| 154 | */ | ||
| 155 | static void meta_intc_shutdown_irq(struct irq_data *data) | ||
| 156 | { | ||
| 157 | irq_hw_number_t hw = data->hwirq; | ||
| 158 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 159 | |||
| 160 | /* Mask the IRQ */ | ||
| 161 | data->chip->irq_mask(data); | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Disable the IRQ at the core by removing the interrupt from | ||
| 165 | * the HW vector mapping. | ||
| 166 | */ | ||
| 167 | metag_out32(0, vec_addr); | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * meta_intc_ack_irq() - acknowledge an external irq | ||
| 172 | * @data: data for the external irq to ack | ||
| 173 | * | ||
| 174 | * Clear down an edge interrupt in the status register. | ||
| 175 | */ | ||
| 176 | static void meta_intc_ack_irq(struct irq_data *data) | ||
| 177 | { | ||
| 178 | irq_hw_number_t hw = data->hwirq; | ||
| 179 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 180 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | ||
| 181 | |||
| 182 | /* Ack the int, if it is still 'on'. | ||
| 183 | * NOTE - this only works for edge triggered interrupts. | ||
| 184 | */ | ||
| 185 | if (metag_in32(stat_addr) & bit) | ||
| 186 | metag_out32(bit, stat_addr); | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * record_irq_is_masked() - record the IRQ masked so it doesn't get handled | ||
| 191 | * @data: data for the external irq to record | ||
| 192 | * | ||
| 193 | * This should get called whenever an external IRQ is masked (by whichever | ||
| 194 | * callback is used). It records the IRQ masked so that it doesn't get handled | ||
| 195 | * if it still shows up in the status register. | ||
| 196 | */ | ||
| 197 | static void record_irq_is_masked(struct irq_data *data) | ||
| 198 | { | ||
| 199 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 200 | irq_hw_number_t hw = data->hwirq; | ||
| 201 | |||
| 202 | clear_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); | ||
| 203 | } | ||
| 204 | |||
| 205 | /** | ||
| 206 | * record_irq_is_unmasked() - record the IRQ unmasked so it can be handled | ||
| 207 | * @data: data for the external irq to record | ||
| 208 | * | ||
| 209 | * This should get called whenever an external IRQ is unmasked (by whichever | ||
| 210 | * callback is used). It records the IRQ unmasked so that it gets handled if it | ||
| 211 | * shows up in the status register. | ||
| 212 | */ | ||
| 213 | static void record_irq_is_unmasked(struct irq_data *data) | ||
| 214 | { | ||
| 215 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 216 | irq_hw_number_t hw = data->hwirq; | ||
| 217 | |||
| 218 | set_bit(meta_intc_offset(hw), &priv->unmasked[meta_intc_bank(hw)]); | ||
| 219 | } | ||
| 220 | |||
| 221 | /* | ||
| 222 | * For use by wrapper IRQ drivers | ||
| 223 | */ | ||
| 224 | |||
| 225 | /** | ||
| 226 | * meta_intc_mask_irq_simple() - minimal mask used by wrapper IRQ drivers | ||
| 227 | * @data: data for the external irq being masked | ||
| 228 | * | ||
| 229 | * This should be called by any wrapper IRQ driver mask functions. it doesn't do | ||
| 230 | * any masking but records the IRQ as masked so that the core code knows the | ||
| 231 | * mask has taken place. It is the callers responsibility to ensure that the IRQ | ||
| 232 | * won't trigger an interrupt to the core. | ||
| 233 | */ | ||
| 234 | void meta_intc_mask_irq_simple(struct irq_data *data) | ||
| 235 | { | ||
| 236 | record_irq_is_masked(data); | ||
| 237 | } | ||
| 238 | |||
| 239 | /** | ||
| 240 | * meta_intc_unmask_irq_simple() - minimal unmask used by wrapper IRQ drivers | ||
| 241 | * @data: data for the external irq being unmasked | ||
| 242 | * | ||
| 243 | * This should be called by any wrapper IRQ driver unmask functions. it doesn't | ||
| 244 | * do any unmasking but records the IRQ as unmasked so that the core code knows | ||
| 245 | * the unmask has taken place. It is the callers responsibility to ensure that | ||
| 246 | * the IRQ can now trigger an interrupt to the core. | ||
| 247 | */ | ||
| 248 | void meta_intc_unmask_irq_simple(struct irq_data *data) | ||
| 249 | { | ||
| 250 | record_irq_is_unmasked(data); | ||
| 251 | } | ||
| 252 | |||
| 253 | |||
| 254 | /** | ||
| 255 | * meta_intc_mask_irq() - mask an external irq using HWMASKEXT | ||
| 256 | * @data: data for the external irq to mask | ||
| 257 | * | ||
| 258 | * This is a default implementation of a mask function which makes use of the | ||
| 259 | * HWMASKEXT registers available in newer versions. | ||
| 260 | * | ||
| 261 | * Earlier versions without these registers should use SoC level IRQ masking | ||
| 262 | * which call the meta_intc_*_simple() functions above, or if that isn't | ||
| 263 | * available should use the fallback meta_intc_*_nomask() functions below. | ||
| 264 | */ | ||
| 265 | static void meta_intc_mask_irq(struct irq_data *data) | ||
| 266 | { | ||
| 267 | irq_hw_number_t hw = data->hwirq; | ||
| 268 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 269 | void __iomem *mask_addr = meta_intc_mask_addr(hw); | ||
| 270 | unsigned long flags; | ||
| 271 | |||
| 272 | record_irq_is_masked(data); | ||
| 273 | |||
| 274 | /* update the interrupt mask */ | ||
| 275 | __global_lock2(flags); | ||
| 276 | metag_out32(metag_in32(mask_addr) & ~bit, mask_addr); | ||
| 277 | __global_unlock2(flags); | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * meta_intc_unmask_irq() - unmask an external irq using HWMASKEXT | ||
| 282 | * @data: data for the external irq to unmask | ||
| 283 | * | ||
| 284 | * This is a default implementation of an unmask function which makes use of the | ||
| 285 | * HWMASKEXT registers available on new versions. It should be paired with | ||
| 286 | * meta_intc_mask_irq() above. | ||
| 287 | */ | ||
| 288 | static void meta_intc_unmask_irq(struct irq_data *data) | ||
| 289 | { | ||
| 290 | irq_hw_number_t hw = data->hwirq; | ||
| 291 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 292 | void __iomem *mask_addr = meta_intc_mask_addr(hw); | ||
| 293 | unsigned long flags; | ||
| 294 | |||
| 295 | record_irq_is_unmasked(data); | ||
| 296 | |||
| 297 | /* update the interrupt mask */ | ||
| 298 | __global_lock2(flags); | ||
| 299 | metag_out32(metag_in32(mask_addr) | bit, mask_addr); | ||
| 300 | __global_unlock2(flags); | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 304 | * meta_intc_mask_irq_nomask() - mask an external irq by unvectoring | ||
| 305 | * @data: data for the external irq to mask | ||
| 306 | * | ||
| 307 | * This is the version of the mask function for older versions which don't have | ||
| 308 | * HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the IRQ is | ||
| 309 | * unvectored from the core and retriggered if necessary later. | ||
| 310 | */ | ||
| 311 | static void meta_intc_mask_irq_nomask(struct irq_data *data) | ||
| 312 | { | ||
| 313 | irq_hw_number_t hw = data->hwirq; | ||
| 314 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 315 | |||
| 316 | record_irq_is_masked(data); | ||
| 317 | |||
| 318 | /* there is no interrupt mask, so unvector the interrupt */ | ||
| 319 | metag_out32(0, vec_addr); | ||
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring | ||
| 324 | * @data: data for the external irq to unmask | ||
| 325 | * | ||
| 326 | * This is the version of the unmask function for older versions which don't | ||
| 327 | * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the | ||
| 328 | * IRQ is revectored back to the core and retriggered if necessary. | ||
| 329 | * | ||
| 330 | * The retriggering done by this function is specific to edge interrupts. | ||
| 331 | */ | ||
| 332 | static void meta_intc_unmask_edge_irq_nomask(struct irq_data *data) | ||
| 333 | { | ||
| 334 | irq_hw_number_t hw = data->hwirq; | ||
| 335 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 336 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | ||
| 337 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 338 | unsigned int thread = hard_processor_id(); | ||
| 339 | |||
| 340 | record_irq_is_unmasked(data); | ||
| 341 | |||
| 342 | /* there is no interrupt mask, so revector the interrupt */ | ||
| 343 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | ||
| 344 | |||
| 345 | /* | ||
| 346 | * Re-trigger interrupt | ||
| 347 | * | ||
| 348 | * Writing a 1 toggles, and a 0->1 transition triggers. We only | ||
| 349 | * retrigger if the status bit is already set, which means we | ||
| 350 | * need to clear it first. Retriggering is fundamentally racy | ||
| 351 | * because if the interrupt fires again after we clear it we | ||
| 352 | * could end up clearing it again and the interrupt handler | ||
| 353 | * thinking it hasn't fired. Therefore we need to keep trying to | ||
| 354 | * retrigger until the bit is set. | ||
| 355 | */ | ||
| 356 | if (metag_in32(stat_addr) & bit) { | ||
| 357 | metag_out32(bit, stat_addr); | ||
| 358 | while (!(metag_in32(stat_addr) & bit)) | ||
| 359 | metag_out32(bit, stat_addr); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | /** | ||
| 364 | * meta_intc_unmask_level_irq_nomask() - unmask a level irq by revectoring | ||
| 365 | * @data: data for the external irq to unmask | ||
| 366 | * | ||
| 367 | * This is the version of the unmask function for older versions which don't | ||
| 368 | * have HWMASKEXT registers, or a SoC level means of masking IRQs. Instead the | ||
| 369 | * IRQ is revectored back to the core and retriggered if necessary. | ||
| 370 | * | ||
| 371 | * The retriggering done by this function is specific to level interrupts. | ||
| 372 | */ | ||
| 373 | static void meta_intc_unmask_level_irq_nomask(struct irq_data *data) | ||
| 374 | { | ||
| 375 | irq_hw_number_t hw = data->hwirq; | ||
| 376 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 377 | void __iomem *stat_addr = meta_intc_stat_addr(hw); | ||
| 378 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 379 | unsigned int thread = hard_processor_id(); | ||
| 380 | |||
| 381 | record_irq_is_unmasked(data); | ||
| 382 | |||
| 383 | /* there is no interrupt mask, so revector the interrupt */ | ||
| 384 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | ||
| 385 | |||
| 386 | /* Re-trigger interrupt */ | ||
| 387 | /* Writing a 1 triggers interrupt */ | ||
| 388 | if (metag_in32(stat_addr) & bit) | ||
| 389 | metag_out32(bit, stat_addr); | ||
| 390 | } | ||
| 391 | |||
| 392 | /** | ||
| 393 | * meta_intc_irq_set_type() - set the type of an external irq | ||
| 394 | * @data: data for the external irq to set the type of | ||
| 395 | * @flow_type: new irq flow type | ||
| 396 | * | ||
| 397 | * Set the flow type of an external interrupt. This updates the irq chip and irq | ||
| 398 | * handler depending on whether the irq is edge or level sensitive (the polarity | ||
| 399 | * is ignored), and also sets up the bit in HWLEVELEXT so the hardware knows | ||
| 400 | * when to trigger. | ||
| 401 | */ | ||
| 402 | static int meta_intc_irq_set_type(struct irq_data *data, unsigned int flow_type) | ||
| 403 | { | ||
| 404 | #ifdef CONFIG_METAG_SUSPEND_MEM | ||
| 405 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 406 | #endif | ||
| 407 | unsigned int irq = data->irq; | ||
| 408 | irq_hw_number_t hw = data->hwirq; | ||
| 409 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 410 | void __iomem *level_addr = meta_intc_level_addr(hw); | ||
| 411 | unsigned long flags; | ||
| 412 | unsigned int level; | ||
| 413 | |||
| 414 | /* update the chip/handler */ | ||
| 415 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | ||
| 416 | __irq_set_chip_handler_name_locked(irq, &meta_intc_level_chip, | ||
| 417 | handle_level_irq, NULL); | ||
| 418 | else | ||
| 419 | __irq_set_chip_handler_name_locked(irq, &meta_intc_edge_chip, | ||
| 420 | handle_edge_irq, NULL); | ||
| 421 | |||
| 422 | /* and clear/set the bit in HWLEVELEXT */ | ||
| 423 | __global_lock2(flags); | ||
| 424 | level = metag_in32(level_addr); | ||
| 425 | if (flow_type & IRQ_TYPE_LEVEL_MASK) | ||
| 426 | level |= bit; | ||
| 427 | else | ||
| 428 | level &= ~bit; | ||
| 429 | metag_out32(level, level_addr); | ||
| 430 | #ifdef CONFIG_METAG_SUSPEND_MEM | ||
| 431 | priv->levels_altered[meta_intc_bank(hw)] |= bit; | ||
| 432 | #endif | ||
| 433 | __global_unlock2(flags); | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * meta_intc_irq_demux() - external irq de-multiplexer | ||
| 440 | * @irq: the virtual interrupt number | ||
| 441 | * @desc: the interrupt description structure for this irq | ||
| 442 | * | ||
| 443 | * The cpu receives an interrupt on TR2 when a SoC interrupt has occurred. It is | ||
| 444 | * this function's job to demux this irq and figure out exactly which external | ||
| 445 | * irq needs servicing. | ||
| 446 | * | ||
| 447 | * Whilst using TR2 to detect external interrupts is a software convention it is | ||
| 448 | * (hopefully) unlikely to change. | ||
| 449 | */ | ||
| 450 | static void meta_intc_irq_demux(unsigned int irq, struct irq_desc *desc) | ||
| 451 | { | ||
| 452 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 453 | irq_hw_number_t hw; | ||
| 454 | unsigned int bank, irq_no, status; | ||
| 455 | void __iomem *stat_addr = meta_intc_stat_addr(0); | ||
| 456 | |||
| 457 | /* | ||
| 458 | * Locate which interrupt has caused our handler to run. | ||
| 459 | */ | ||
| 460 | for (bank = 0; bank < priv->nr_banks; ++bank) { | ||
| 461 | /* Which interrupts are currently pending in this bank? */ | ||
| 462 | recalculate: | ||
| 463 | status = metag_in32(stat_addr) & priv->unmasked[bank]; | ||
| 464 | |||
| 465 | for (hw = bank*32; status; status >>= 1, ++hw) { | ||
| 466 | if (status & 0x1) { | ||
| 467 | /* | ||
| 468 | * Map the hardware IRQ number to a virtual | ||
| 469 | * Linux IRQ number. | ||
| 470 | */ | ||
| 471 | irq_no = irq_linear_revmap(priv->domain, hw); | ||
| 472 | |||
| 473 | /* | ||
| 474 | * Only fire off external interrupts that are | ||
| 475 | * registered to be handled by the kernel. | ||
| 476 | * Other external interrupts are probably being | ||
| 477 | * handled by other Meta hardware threads. | ||
| 478 | */ | ||
| 479 | generic_handle_irq(irq_no); | ||
| 480 | |||
| 481 | /* | ||
| 482 | * The handler may have re-enabled interrupts | ||
| 483 | * which could have caused a nested invocation | ||
| 484 | * of this code and make the copy of the | ||
| 485 | * status register we are using invalid. | ||
| 486 | */ | ||
| 487 | goto recalculate; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | stat_addr += HWSTAT_STRIDE; | ||
| 491 | } | ||
| 492 | } | ||
| 493 | |||
| 494 | #ifdef CONFIG_SMP | ||
| 495 | /** | ||
| 496 | * meta_intc_set_affinity() - set the affinity for an interrupt | ||
| 497 | * @data: data for the external irq to set the affinity of | ||
| 498 | * @cpumask: cpu mask representing cpus which can handle the interrupt | ||
| 499 | * @force: whether to force (ignored) | ||
| 500 | * | ||
| 501 | * Revector the specified external irq onto a specific cpu's TR2 trigger, so | ||
| 502 | * that that cpu tends to be the one who handles it. | ||
| 503 | */ | ||
| 504 | static int meta_intc_set_affinity(struct irq_data *data, | ||
| 505 | const struct cpumask *cpumask, bool force) | ||
| 506 | { | ||
| 507 | irq_hw_number_t hw = data->hwirq; | ||
| 508 | void __iomem *vec_addr = meta_intc_vec_addr(hw); | ||
| 509 | unsigned int cpu, thread; | ||
| 510 | |||
| 511 | /* | ||
| 512 | * Wire up this interrupt from HWVECxEXT to the Meta core. | ||
| 513 | * | ||
| 514 | * Note that we can't wire up HWVECxEXT to interrupt more than | ||
| 515 | * one cpu (the interrupt code doesn't support it), so we just | ||
| 516 | * pick the first cpu we find in 'cpumask'. | ||
| 517 | */ | ||
| 518 | cpu = cpumask_any(cpumask); | ||
| 519 | thread = cpu_2_hwthread_id[cpu]; | ||
| 520 | |||
| 521 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR2(thread)), vec_addr); | ||
| 522 | |||
| 523 | return 0; | ||
| 524 | } | ||
| 525 | #else | ||
| 526 | #define meta_intc_set_affinity NULL | ||
| 527 | #endif | ||
| 528 | |||
| 529 | #ifdef CONFIG_PM_SLEEP | ||
| 530 | #define META_INTC_CHIP_FLAGS (IRQCHIP_MASK_ON_SUSPEND \ | ||
| 531 | | IRQCHIP_SKIP_SET_WAKE) | ||
| 532 | #else | ||
| 533 | #define META_INTC_CHIP_FLAGS 0 | ||
| 534 | #endif | ||
| 535 | |||
| 536 | /* public edge/level irq chips which SoCs can override */ | ||
| 537 | |||
| 538 | struct irq_chip meta_intc_edge_chip = { | ||
| 539 | .irq_startup = meta_intc_startup_irq, | ||
| 540 | .irq_shutdown = meta_intc_shutdown_irq, | ||
| 541 | .irq_ack = meta_intc_ack_irq, | ||
| 542 | .irq_mask = meta_intc_mask_irq, | ||
| 543 | .irq_unmask = meta_intc_unmask_irq, | ||
| 544 | .irq_set_type = meta_intc_irq_set_type, | ||
| 545 | .irq_set_affinity = meta_intc_set_affinity, | ||
| 546 | .flags = META_INTC_CHIP_FLAGS, | ||
| 547 | }; | ||
| 548 | |||
| 549 | struct irq_chip meta_intc_level_chip = { | ||
| 550 | .irq_startup = meta_intc_startup_irq, | ||
| 551 | .irq_shutdown = meta_intc_shutdown_irq, | ||
| 552 | .irq_set_type = meta_intc_irq_set_type, | ||
| 553 | .irq_mask = meta_intc_mask_irq, | ||
| 554 | .irq_unmask = meta_intc_unmask_irq, | ||
| 555 | .irq_set_affinity = meta_intc_set_affinity, | ||
| 556 | .flags = META_INTC_CHIP_FLAGS, | ||
| 557 | }; | ||
| 558 | |||
| 559 | /** | ||
| 560 | * meta_intc_map() - map an external irq | ||
| 561 | * @d: irq domain of external trigger block | ||
| 562 | * @irq: virtual irq number | ||
| 563 | * @hw: hardware irq number within external trigger block | ||
| 564 | * | ||
| 565 | * This sets up a virtual irq for a specified hardware interrupt. The irq chip | ||
| 566 | * and handler is configured, using the HWLEVELEXT registers to determine | ||
| 567 | * edge/level flow type. These registers will have been set when the irq type is | ||
| 568 | * set (or set to a default at init time). | ||
| 569 | */ | ||
| 570 | static int meta_intc_map(struct irq_domain *d, unsigned int irq, | ||
| 571 | irq_hw_number_t hw) | ||
| 572 | { | ||
| 573 | unsigned int bit = 1 << meta_intc_offset(hw); | ||
| 574 | void __iomem *level_addr = meta_intc_level_addr(hw); | ||
| 575 | |||
| 576 | /* Go by the current sense in the HWLEVELEXT register */ | ||
| 577 | if (metag_in32(level_addr) & bit) | ||
| 578 | irq_set_chip_and_handler(irq, &meta_intc_level_chip, | ||
| 579 | handle_level_irq); | ||
| 580 | else | ||
| 581 | irq_set_chip_and_handler(irq, &meta_intc_edge_chip, | ||
| 582 | handle_edge_irq); | ||
| 583 | return 0; | ||
| 584 | } | ||
| 585 | |||
| 586 | static const struct irq_domain_ops meta_intc_domain_ops = { | ||
| 587 | .map = meta_intc_map, | ||
| 588 | .xlate = irq_domain_xlate_twocell, | ||
| 589 | }; | ||
| 590 | |||
| 591 | #ifdef CONFIG_METAG_SUSPEND_MEM | ||
| 592 | |||
| 593 | /** | ||
| 594 | * struct meta_intc_context - suspend context | ||
| 595 | * @levels: State of HWLEVELEXT registers | ||
| 596 | * @masks: State of HWMASKEXT registers | ||
| 597 | * @vectors: State of HWVECEXT registers | ||
| 598 | * @txvecint: State of TxVECINT registers | ||
| 599 | * | ||
| 600 | * This structure stores the IRQ state across suspend. | ||
| 601 | */ | ||
| 602 | struct meta_intc_context { | ||
| 603 | u32 levels[4]; | ||
| 604 | u32 masks[4]; | ||
| 605 | u8 vectors[4*32]; | ||
| 606 | |||
| 607 | u8 txvecint[4][4]; | ||
| 608 | }; | ||
| 609 | |||
| 610 | /* suspend context */ | ||
| 611 | static struct meta_intc_context *meta_intc_context; | ||
| 612 | |||
| 613 | /** | ||
| 614 | * meta_intc_suspend() - store irq state | ||
| 615 | * | ||
| 616 | * To avoid interfering with other threads we only save the IRQ state of IRQs in | ||
| 617 | * use by Linux. | ||
| 618 | */ | ||
| 619 | static int meta_intc_suspend(void) | ||
| 620 | { | ||
| 621 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 622 | int i, j; | ||
| 623 | irq_hw_number_t hw; | ||
| 624 | unsigned int bank; | ||
| 625 | unsigned long flags; | ||
| 626 | struct meta_intc_context *context; | ||
| 627 | void __iomem *level_addr, *mask_addr, *vec_addr; | ||
| 628 | u32 mask, bit; | ||
| 629 | |||
| 630 | context = kzalloc(sizeof(*context), GFP_ATOMIC); | ||
| 631 | if (!context) | ||
| 632 | return -ENOMEM; | ||
| 633 | |||
| 634 | hw = 0; | ||
| 635 | level_addr = meta_intc_level_addr(0); | ||
| 636 | mask_addr = meta_intc_mask_addr(0); | ||
| 637 | for (bank = 0; bank < priv->nr_banks; ++bank) { | ||
| 638 | vec_addr = meta_intc_vec_addr(hw); | ||
| 639 | |||
| 640 | /* create mask of interrupts in use */ | ||
| 641 | mask = 0; | ||
| 642 | for (bit = 1; bit; bit <<= 1) { | ||
| 643 | i = irq_linear_revmap(priv->domain, hw); | ||
| 644 | /* save mapped irqs which are enabled or have actions */ | ||
| 645 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | ||
| 646 | irq_has_action(i))) { | ||
| 647 | mask |= bit; | ||
| 648 | |||
| 649 | /* save trigger vector */ | ||
| 650 | context->vectors[hw] = metag_in32(vec_addr); | ||
| 651 | } | ||
| 652 | |||
| 653 | ++hw; | ||
| 654 | vec_addr += HWVECnEXT_STRIDE; | ||
| 655 | } | ||
| 656 | |||
| 657 | /* save level state if any IRQ levels altered */ | ||
| 658 | if (priv->levels_altered[bank]) | ||
| 659 | context->levels[bank] = metag_in32(level_addr); | ||
| 660 | /* save mask state if any IRQs in use */ | ||
| 661 | if (mask) | ||
| 662 | context->masks[bank] = metag_in32(mask_addr); | ||
| 663 | |||
| 664 | level_addr += HWSTAT_STRIDE; | ||
| 665 | mask_addr += HWSTAT_STRIDE; | ||
| 666 | } | ||
| 667 | |||
| 668 | /* save trigger matrixing */ | ||
| 669 | __global_lock2(flags); | ||
| 670 | for (i = 0; i < 4; ++i) | ||
| 671 | for (j = 0; j < 4; ++j) | ||
| 672 | context->txvecint[i][j] = metag_in32(T0VECINT_BHALT + | ||
| 673 | TnVECINT_STRIDE*i + | ||
| 674 | 8*j); | ||
| 675 | __global_unlock2(flags); | ||
| 676 | |||
| 677 | meta_intc_context = context; | ||
| 678 | return 0; | ||
| 679 | } | ||
| 680 | |||
| 681 | /** | ||
| 682 | * meta_intc_resume() - restore saved irq state | ||
| 683 | * | ||
| 684 | * Restore the saved IRQ state and drop it. | ||
| 685 | */ | ||
| 686 | static void meta_intc_resume(void) | ||
| 687 | { | ||
| 688 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 689 | int i, j; | ||
| 690 | irq_hw_number_t hw; | ||
| 691 | unsigned int bank; | ||
| 692 | unsigned long flags; | ||
| 693 | struct meta_intc_context *context = meta_intc_context; | ||
| 694 | void __iomem *level_addr, *mask_addr, *vec_addr; | ||
| 695 | u32 mask, bit, tmp; | ||
| 696 | |||
| 697 | meta_intc_context = NULL; | ||
| 698 | |||
| 699 | hw = 0; | ||
| 700 | level_addr = meta_intc_level_addr(0); | ||
| 701 | mask_addr = meta_intc_mask_addr(0); | ||
| 702 | for (bank = 0; bank < priv->nr_banks; ++bank) { | ||
| 703 | vec_addr = meta_intc_vec_addr(hw); | ||
| 704 | |||
| 705 | /* create mask of interrupts in use */ | ||
| 706 | mask = 0; | ||
| 707 | for (bit = 1; bit; bit <<= 1) { | ||
| 708 | i = irq_linear_revmap(priv->domain, hw); | ||
| 709 | /* restore mapped irqs, enabled or with actions */ | ||
| 710 | if (i && (!irqd_irq_disabled(irq_get_irq_data(i)) || | ||
| 711 | irq_has_action(i))) { | ||
| 712 | mask |= bit; | ||
| 713 | |||
| 714 | /* restore trigger vector */ | ||
| 715 | metag_out32(context->vectors[hw], vec_addr); | ||
| 716 | } | ||
| 717 | |||
| 718 | ++hw; | ||
| 719 | vec_addr += HWVECnEXT_STRIDE; | ||
| 720 | } | ||
| 721 | |||
| 722 | if (mask) { | ||
| 723 | /* restore mask state */ | ||
| 724 | __global_lock2(flags); | ||
| 725 | tmp = metag_in32(mask_addr); | ||
| 726 | tmp = (tmp & ~mask) | (context->masks[bank] & mask); | ||
| 727 | metag_out32(tmp, mask_addr); | ||
| 728 | __global_unlock2(flags); | ||
| 729 | } | ||
| 730 | |||
| 731 | mask = priv->levels_altered[bank]; | ||
| 732 | if (mask) { | ||
| 733 | /* restore level state */ | ||
| 734 | __global_lock2(flags); | ||
| 735 | tmp = metag_in32(level_addr); | ||
| 736 | tmp = (tmp & ~mask) | (context->levels[bank] & mask); | ||
| 737 | metag_out32(tmp, level_addr); | ||
| 738 | __global_unlock2(flags); | ||
| 739 | } | ||
| 740 | |||
| 741 | level_addr += HWSTAT_STRIDE; | ||
| 742 | mask_addr += HWSTAT_STRIDE; | ||
| 743 | } | ||
| 744 | |||
| 745 | /* restore trigger matrixing */ | ||
| 746 | __global_lock2(flags); | ||
| 747 | for (i = 0; i < 4; ++i) { | ||
| 748 | for (j = 0; j < 4; ++j) { | ||
| 749 | metag_out32(context->txvecint[i][j], | ||
| 750 | T0VECINT_BHALT + | ||
| 751 | TnVECINT_STRIDE*i + | ||
| 752 | 8*j); | ||
| 753 | } | ||
| 754 | } | ||
| 755 | __global_unlock2(flags); | ||
| 756 | |||
| 757 | kfree(context); | ||
| 758 | } | ||
| 759 | |||
| 760 | static struct syscore_ops meta_intc_syscore_ops = { | ||
| 761 | .suspend = meta_intc_suspend, | ||
| 762 | .resume = meta_intc_resume, | ||
| 763 | }; | ||
| 764 | |||
| 765 | static void __init meta_intc_init_syscore_ops(struct meta_intc_priv *priv) | ||
| 766 | { | ||
| 767 | register_syscore_ops(&meta_intc_syscore_ops); | ||
| 768 | } | ||
| 769 | #else | ||
| 770 | #define meta_intc_init_syscore_ops(priv) do {} while (0) | ||
| 771 | #endif | ||
| 772 | |||
| 773 | /** | ||
| 774 | * meta_intc_init_cpu() - register with a Meta cpu | ||
| 775 | * @priv: private interrupt controller data | ||
| 776 | * @cpu: the CPU to register on | ||
| 777 | * | ||
| 778 | * Configure @cpu's TR2 irq so that we can demux external irqs. | ||
| 779 | */ | ||
| 780 | static void __init meta_intc_init_cpu(struct meta_intc_priv *priv, int cpu) | ||
| 781 | { | ||
| 782 | unsigned int thread = cpu_2_hwthread_id[cpu]; | ||
| 783 | unsigned int signum = TBID_SIGNUM_TR2(thread); | ||
| 784 | int irq = tbisig_map(signum); | ||
| 785 | |||
| 786 | /* Register the multiplexed IRQ handler */ | ||
| 787 | irq_set_chained_handler(irq, meta_intc_irq_demux); | ||
| 788 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | ||
| 789 | } | ||
| 790 | |||
| 791 | /** | ||
| 792 | * meta_intc_no_mask() - indicate lack of HWMASKEXT registers | ||
| 793 | * | ||
| 794 | * Called from SoC code (or init code below) to dynamically indicate the lack of | ||
| 795 | * HWMASKEXT registers (for example depending on some SoC revision register). | ||
| 796 | * This alters the irq mask and unmask callbacks to use the fallback | ||
| 797 | * unvectoring/retriggering technique instead of using HWMASKEXT registers. | ||
| 798 | */ | ||
| 799 | void __init meta_intc_no_mask(void) | ||
| 800 | { | ||
| 801 | meta_intc_edge_chip.irq_mask = meta_intc_mask_irq_nomask; | ||
| 802 | meta_intc_edge_chip.irq_unmask = meta_intc_unmask_edge_irq_nomask; | ||
| 803 | meta_intc_level_chip.irq_mask = meta_intc_mask_irq_nomask; | ||
| 804 | meta_intc_level_chip.irq_unmask = meta_intc_unmask_level_irq_nomask; | ||
| 805 | } | ||
| 806 | |||
| 807 | /** | ||
| 808 | * init_external_IRQ() - initialise the external irq controller | ||
| 809 | * | ||
| 810 | * Set up the external irq controller using device tree properties. This is | ||
| 811 | * called from init_IRQ(). | ||
| 812 | */ | ||
| 813 | int __init init_external_IRQ(void) | ||
| 814 | { | ||
| 815 | struct meta_intc_priv *priv = &meta_intc_priv; | ||
| 816 | struct device_node *node; | ||
| 817 | int ret, cpu; | ||
| 818 | u32 val; | ||
| 819 | bool no_masks = false; | ||
| 820 | |||
| 821 | node = of_find_compatible_node(NULL, NULL, "img,meta-intc"); | ||
| 822 | if (!node) | ||
| 823 | return -ENOENT; | ||
| 824 | |||
| 825 | /* Get number of banks */ | ||
| 826 | ret = of_property_read_u32(node, "num-banks", &val); | ||
| 827 | if (ret) { | ||
| 828 | pr_err("meta-intc: No num-banks property found\n"); | ||
| 829 | return ret; | ||
| 830 | } | ||
| 831 | if (val < 1 || val > 4) { | ||
| 832 | pr_err("meta-intc: num-banks (%u) out of range\n", val); | ||
| 833 | return -EINVAL; | ||
| 834 | } | ||
| 835 | priv->nr_banks = val; | ||
| 836 | |||
| 837 | /* Are any mask registers present? */ | ||
| 838 | if (of_get_property(node, "no-mask", NULL)) | ||
| 839 | no_masks = true; | ||
| 840 | |||
| 841 | /* No HWMASKEXT registers present? */ | ||
| 842 | if (no_masks) | ||
| 843 | meta_intc_no_mask(); | ||
| 844 | |||
| 845 | /* Set up an IRQ domain */ | ||
| 846 | /* | ||
| 847 | * This is a legacy IRQ domain for now until all the platform setup code | ||
| 848 | * has been converted to devicetree. | ||
| 849 | */ | ||
| 850 | priv->domain = irq_domain_add_linear(node, priv->nr_banks*32, | ||
| 851 | &meta_intc_domain_ops, priv); | ||
| 852 | if (unlikely(!priv->domain)) { | ||
| 853 | pr_err("meta-intc: cannot add IRQ domain\n"); | ||
| 854 | return -ENOMEM; | ||
| 855 | } | ||
| 856 | |||
| 857 | /* Setup TR2 for all cpus. */ | ||
| 858 | for_each_possible_cpu(cpu) | ||
| 859 | meta_intc_init_cpu(priv, cpu); | ||
| 860 | |||
| 861 | /* Set up system suspend/resume callbacks */ | ||
| 862 | meta_intc_init_syscore_ops(priv); | ||
| 863 | |||
| 864 | pr_info("meta-intc: External IRQ controller initialised (%u IRQs)\n", | ||
| 865 | priv->nr_banks*32); | ||
| 866 | |||
| 867 | return 0; | ||
| 868 | } | ||
diff --git a/drivers/irqchip/irq-metag.c b/drivers/irqchip/irq-metag.c new file mode 100644 index 000000000000..8e94d7a3b20d --- /dev/null +++ b/drivers/irqchip/irq-metag.c | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | /* | ||
| 2 | * Meta internal (HWSTATMETA) interrupt code. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011-2012 Imagination Technologies Ltd. | ||
| 5 | * | ||
| 6 | * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c | ||
| 7 | * The code base could be generalised/merged as a lot of the functionality is | ||
| 8 | * similar. Until this is done, we try to keep the code simple here. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/interrupt.h> | ||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/irqdomain.h> | ||
| 14 | |||
| 15 | #include <asm/irq.h> | ||
| 16 | #include <asm/hwthread.h> | ||
| 17 | |||
| 18 | #define PERF0VECINT 0x04820580 | ||
| 19 | #define PERF1VECINT 0x04820588 | ||
| 20 | #define PERF0TRIG_OFFSET 16 | ||
| 21 | #define PERF1TRIG_OFFSET 17 | ||
| 22 | |||
| 23 | /** | ||
| 24 | * struct metag_internal_irq_priv - private meta internal interrupt data | ||
| 25 | * @domain: IRQ domain for all internal Meta IRQs (HWSTATMETA) | ||
| 26 | * @unmasked: Record of unmasked IRQs | ||
| 27 | */ | ||
| 28 | struct metag_internal_irq_priv { | ||
| 29 | struct irq_domain *domain; | ||
| 30 | |||
| 31 | unsigned long unmasked; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* Private data for the one and only internal interrupt controller */ | ||
| 35 | static struct metag_internal_irq_priv metag_internal_irq_priv; | ||
| 36 | |||
| 37 | static unsigned int metag_internal_irq_startup(struct irq_data *data); | ||
| 38 | static void metag_internal_irq_shutdown(struct irq_data *data); | ||
| 39 | static void metag_internal_irq_ack(struct irq_data *data); | ||
| 40 | static void metag_internal_irq_mask(struct irq_data *data); | ||
| 41 | static void metag_internal_irq_unmask(struct irq_data *data); | ||
| 42 | #ifdef CONFIG_SMP | ||
| 43 | static int metag_internal_irq_set_affinity(struct irq_data *data, | ||
| 44 | const struct cpumask *cpumask, bool force); | ||
| 45 | #endif | ||
| 46 | |||
| 47 | static struct irq_chip internal_irq_edge_chip = { | ||
| 48 | .name = "HWSTATMETA-IRQ", | ||
| 49 | .irq_startup = metag_internal_irq_startup, | ||
| 50 | .irq_shutdown = metag_internal_irq_shutdown, | ||
| 51 | .irq_ack = metag_internal_irq_ack, | ||
| 52 | .irq_mask = metag_internal_irq_mask, | ||
| 53 | .irq_unmask = metag_internal_irq_unmask, | ||
| 54 | #ifdef CONFIG_SMP | ||
| 55 | .irq_set_affinity = metag_internal_irq_set_affinity, | ||
| 56 | #endif | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* | ||
| 60 | * metag_hwvec_addr - get the address of *VECINT regs of irq | ||
| 61 | * | ||
| 62 | * This function is a table of supported triggers on HWSTATMETA | ||
| 63 | * Could do with a structure, but better keep it simple. Changes | ||
| 64 | * in this code should be rare. | ||
| 65 | */ | ||
| 66 | static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw) | ||
| 67 | { | ||
| 68 | void __iomem *addr; | ||
| 69 | |||
| 70 | switch (hw) { | ||
| 71 | case PERF0TRIG_OFFSET: | ||
| 72 | addr = (void __iomem *)PERF0VECINT; | ||
| 73 | break; | ||
| 74 | case PERF1TRIG_OFFSET: | ||
| 75 | addr = (void __iomem *)PERF1VECINT; | ||
| 76 | break; | ||
| 77 | default: | ||
| 78 | addr = NULL; | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | return addr; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* | ||
| 85 | * metag_internal_startup - setup an internal irq | ||
| 86 | * @irq: the irq to startup | ||
| 87 | * | ||
| 88 | * Multiplex interrupts for @irq onto TR1. Clear any pending | ||
| 89 | * interrupts. | ||
| 90 | */ | ||
| 91 | static unsigned int metag_internal_irq_startup(struct irq_data *data) | ||
| 92 | { | ||
| 93 | /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ | ||
| 94 | metag_internal_irq_ack(data); | ||
| 95 | |||
| 96 | /* Enable the interrupt by unmasking it */ | ||
| 97 | metag_internal_irq_unmask(data); | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | ||
| 103 | * metag_internal_irq_shutdown - turn off the irq | ||
| 104 | * @irq: the irq number to turn off | ||
| 105 | * | ||
| 106 | * Mask @irq and clear any pending interrupts. | ||
| 107 | * Stop muxing @irq onto TR1. | ||
| 108 | */ | ||
| 109 | static void metag_internal_irq_shutdown(struct irq_data *data) | ||
| 110 | { | ||
| 111 | /* Disable the IRQ at the core by masking it. */ | ||
| 112 | metag_internal_irq_mask(data); | ||
| 113 | |||
| 114 | /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ | ||
| 115 | metag_internal_irq_ack(data); | ||
| 116 | } | ||
| 117 | |||
| 118 | /* | ||
| 119 | * metag_internal_irq_ack - acknowledge irq | ||
| 120 | * @irq: the irq to ack | ||
| 121 | */ | ||
| 122 | static void metag_internal_irq_ack(struct irq_data *data) | ||
| 123 | { | ||
| 124 | irq_hw_number_t hw = data->hwirq; | ||
| 125 | unsigned int bit = 1 << hw; | ||
| 126 | |||
| 127 | if (metag_in32(HWSTATMETA) & bit) | ||
| 128 | metag_out32(bit, HWSTATMETA); | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * metag_internal_irq_mask() - mask an internal irq by unvectoring | ||
| 133 | * @data: data for the internal irq to mask | ||
| 134 | * | ||
| 135 | * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core | ||
| 136 | * and retriggered if necessary later. | ||
| 137 | */ | ||
| 138 | static void metag_internal_irq_mask(struct irq_data *data) | ||
| 139 | { | ||
| 140 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
| 141 | irq_hw_number_t hw = data->hwirq; | ||
| 142 | void __iomem *vec_addr = metag_hwvec_addr(hw); | ||
| 143 | |||
| 144 | clear_bit(hw, &priv->unmasked); | ||
| 145 | |||
| 146 | /* there is no interrupt mask, so unvector the interrupt */ | ||
| 147 | metag_out32(0, vec_addr); | ||
| 148 | } | ||
| 149 | |||
| 150 | /** | ||
| 151 | * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring | ||
| 152 | * @data: data for the internal irq to unmask | ||
| 153 | * | ||
| 154 | * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the | ||
| 155 | * core and retriggered if necessary. | ||
| 156 | */ | ||
| 157 | static void metag_internal_irq_unmask(struct irq_data *data) | ||
| 158 | { | ||
| 159 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
| 160 | irq_hw_number_t hw = data->hwirq; | ||
| 161 | unsigned int bit = 1 << hw; | ||
| 162 | void __iomem *vec_addr = metag_hwvec_addr(hw); | ||
| 163 | unsigned int thread = hard_processor_id(); | ||
| 164 | |||
| 165 | set_bit(hw, &priv->unmasked); | ||
| 166 | |||
| 167 | /* there is no interrupt mask, so revector the interrupt */ | ||
| 168 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr); | ||
| 169 | |||
| 170 | /* | ||
| 171 | * Re-trigger interrupt | ||
| 172 | * | ||
| 173 | * Writing a 1 toggles, and a 0->1 transition triggers. We only | ||
| 174 | * retrigger if the status bit is already set, which means we | ||
| 175 | * need to clear it first. Retriggering is fundamentally racy | ||
| 176 | * because if the interrupt fires again after we clear it we | ||
| 177 | * could end up clearing it again and the interrupt handler | ||
| 178 | * thinking it hasn't fired. Therefore we need to keep trying to | ||
| 179 | * retrigger until the bit is set. | ||
| 180 | */ | ||
| 181 | if (metag_in32(HWSTATMETA) & bit) { | ||
| 182 | metag_out32(bit, HWSTATMETA); | ||
| 183 | while (!(metag_in32(HWSTATMETA) & bit)) | ||
| 184 | metag_out32(bit, HWSTATMETA); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #ifdef CONFIG_SMP | ||
| 189 | /* | ||
| 190 | * metag_internal_irq_set_affinity - set the affinity for an interrupt | ||
| 191 | */ | ||
| 192 | static int metag_internal_irq_set_affinity(struct irq_data *data, | ||
| 193 | const struct cpumask *cpumask, bool force) | ||
| 194 | { | ||
| 195 | unsigned int cpu, thread; | ||
| 196 | irq_hw_number_t hw = data->hwirq; | ||
| 197 | /* | ||
| 198 | * Wire up this interrupt from *VECINT to the Meta core. | ||
| 199 | * | ||
| 200 | * Note that we can't wire up *VECINT to interrupt more than | ||
| 201 | * one cpu (the interrupt code doesn't support it), so we just | ||
| 202 | * pick the first cpu we find in 'cpumask'. | ||
| 203 | */ | ||
| 204 | cpu = cpumask_any(cpumask); | ||
| 205 | thread = cpu_2_hwthread_id[cpu]; | ||
| 206 | |||
| 207 | metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), | ||
| 208 | metag_hwvec_addr(hw)); | ||
| 209 | |||
| 210 | return 0; | ||
| 211 | } | ||
| 212 | #endif | ||
| 213 | |||
| 214 | /* | ||
| 215 | * metag_internal_irq_demux - irq de-multiplexer | ||
| 216 | * @irq: the interrupt number | ||
| 217 | * @desc: the interrupt description structure for this irq | ||
| 218 | * | ||
| 219 | * The cpu receives an interrupt on TR1 when an interrupt has | ||
| 220 | * occurred. It is this function's job to demux this irq and | ||
| 221 | * figure out exactly which trigger needs servicing. | ||
| 222 | */ | ||
| 223 | static void metag_internal_irq_demux(unsigned int irq, struct irq_desc *desc) | ||
| 224 | { | ||
| 225 | struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc); | ||
| 226 | irq_hw_number_t hw; | ||
| 227 | unsigned int irq_no; | ||
| 228 | u32 status; | ||
| 229 | |||
| 230 | recalculate: | ||
| 231 | status = metag_in32(HWSTATMETA) & priv->unmasked; | ||
| 232 | |||
| 233 | for (hw = 0; status != 0; status >>= 1, ++hw) { | ||
| 234 | if (status & 0x1) { | ||
| 235 | /* | ||
| 236 | * Map the hardware IRQ number to a virtual Linux IRQ | ||
| 237 | * number. | ||
| 238 | */ | ||
| 239 | irq_no = irq_linear_revmap(priv->domain, hw); | ||
| 240 | |||
| 241 | /* | ||
| 242 | * Only fire off interrupts that are | ||
| 243 | * registered to be handled by the kernel. | ||
| 244 | * Other interrupts are probably being | ||
| 245 | * handled by other Meta hardware threads. | ||
| 246 | */ | ||
| 247 | generic_handle_irq(irq_no); | ||
| 248 | |||
| 249 | /* | ||
| 250 | * The handler may have re-enabled interrupts | ||
| 251 | * which could have caused a nested invocation | ||
| 252 | * of this code and make the copy of the | ||
| 253 | * status register we are using invalid. | ||
| 254 | */ | ||
| 255 | goto recalculate; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number. | ||
| 262 | * @hw: Number of the internal IRQ. Must be in range. | ||
| 263 | * | ||
| 264 | * Returns: The virtual IRQ number of the Meta internal IRQ specified by | ||
| 265 | * @hw. | ||
| 266 | */ | ||
| 267 | int internal_irq_map(unsigned int hw) | ||
| 268 | { | ||
| 269 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
| 270 | if (!priv->domain) | ||
| 271 | return -ENODEV; | ||
| 272 | return irq_create_mapping(priv->domain, hw); | ||
| 273 | } | ||
| 274 | |||
| 275 | /** | ||
| 276 | * metag_internal_irq_init_cpu - regsister with the Meta cpu | ||
| 277 | * @cpu: the CPU to register on | ||
| 278 | * | ||
| 279 | * Configure @cpu's TR1 irq so that we can demux irqs. | ||
| 280 | */ | ||
| 281 | static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv, | ||
| 282 | int cpu) | ||
| 283 | { | ||
| 284 | unsigned int thread = cpu_2_hwthread_id[cpu]; | ||
| 285 | unsigned int signum = TBID_SIGNUM_TR1(thread); | ||
| 286 | int irq = tbisig_map(signum); | ||
| 287 | |||
| 288 | /* Register the multiplexed IRQ handler */ | ||
| 289 | irq_set_handler_data(irq, priv); | ||
| 290 | irq_set_chained_handler(irq, metag_internal_irq_demux); | ||
| 291 | irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); | ||
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * metag_internal_intc_map() - map an internal irq | ||
| 296 | * @d: irq domain of internal trigger block | ||
| 297 | * @irq: virtual irq number | ||
| 298 | * @hw: hardware irq number within internal trigger block | ||
| 299 | * | ||
| 300 | * This sets up a virtual irq for a specified hardware interrupt. The irq chip | ||
| 301 | * and handler is configured. | ||
| 302 | */ | ||
| 303 | static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq, | ||
| 304 | irq_hw_number_t hw) | ||
| 305 | { | ||
| 306 | /* only register interrupt if it is mapped */ | ||
| 307 | if (!metag_hwvec_addr(hw)) | ||
| 308 | return -EINVAL; | ||
| 309 | |||
| 310 | irq_set_chip_and_handler(irq, &internal_irq_edge_chip, | ||
| 311 | handle_edge_irq); | ||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | static const struct irq_domain_ops metag_internal_intc_domain_ops = { | ||
| 316 | .map = metag_internal_intc_map, | ||
| 317 | }; | ||
| 318 | |||
| 319 | /** | ||
| 320 | * metag_internal_irq_register - register internal IRQs | ||
| 321 | * | ||
| 322 | * Register the irq chip and handler function for all internal IRQs | ||
| 323 | */ | ||
| 324 | int __init init_internal_IRQ(void) | ||
| 325 | { | ||
| 326 | struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; | ||
| 327 | unsigned int cpu; | ||
| 328 | |||
| 329 | /* Set up an IRQ domain */ | ||
| 330 | priv->domain = irq_domain_add_linear(NULL, 32, | ||
| 331 | &metag_internal_intc_domain_ops, | ||
| 332 | priv); | ||
| 333 | if (unlikely(!priv->domain)) { | ||
| 334 | pr_err("meta-internal-intc: cannot add IRQ domain\n"); | ||
| 335 | return -ENOMEM; | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Setup TR1 for all cpus. */ | ||
| 339 | for_each_possible_cpu(cpu) | ||
| 340 | metag_internal_irq_init_cpu(priv, cpu); | ||
| 341 | |||
| 342 | return 0; | ||
| 343 | }; | ||
