1171bb2f1SJohn Crispin /* 2171bb2f1SJohn Crispin * This program is free software; you can redistribute it and/or modify it 3171bb2f1SJohn Crispin * under the terms of the GNU General Public License version 2 as published 4171bb2f1SJohn Crispin * by the Free Software Foundation. 5171bb2f1SJohn Crispin * 6171bb2f1SJohn Crispin * Copyright (C) 2010 John Crispin <blogic@openwrt.org> 7171bb2f1SJohn Crispin * Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com> 8171bb2f1SJohn Crispin */ 9171bb2f1SJohn Crispin 10171bb2f1SJohn Crispin #include <linux/interrupt.h> 11171bb2f1SJohn Crispin #include <linux/ioport.h> 12171bb2f1SJohn Crispin 13171bb2f1SJohn Crispin #include <asm/bootinfo.h> 14171bb2f1SJohn Crispin #include <asm/irq_cpu.h> 15171bb2f1SJohn Crispin 16171bb2f1SJohn Crispin #include <lantiq_soc.h> 17171bb2f1SJohn Crispin #include <irq.h> 18171bb2f1SJohn Crispin 19171bb2f1SJohn Crispin /* register definitions */ 20171bb2f1SJohn Crispin #define LTQ_ICU_IM0_ISR 0x0000 21171bb2f1SJohn Crispin #define LTQ_ICU_IM0_IER 0x0008 22171bb2f1SJohn Crispin #define LTQ_ICU_IM0_IOSR 0x0010 23171bb2f1SJohn Crispin #define LTQ_ICU_IM0_IRSR 0x0018 24171bb2f1SJohn Crispin #define LTQ_ICU_IM0_IMR 0x0020 25171bb2f1SJohn Crispin #define LTQ_ICU_IM1_ISR 0x0028 26171bb2f1SJohn Crispin #define LTQ_ICU_OFFSET (LTQ_ICU_IM1_ISR - LTQ_ICU_IM0_ISR) 27171bb2f1SJohn Crispin 28171bb2f1SJohn Crispin #define LTQ_EIU_EXIN_C 0x0000 29171bb2f1SJohn Crispin #define LTQ_EIU_EXIN_INIC 0x0004 30171bb2f1SJohn Crispin #define LTQ_EIU_EXIN_INEN 0x000C 31171bb2f1SJohn Crispin 32171bb2f1SJohn Crispin /* irq numbers used by the external interrupt unit (EIU) */ 33171bb2f1SJohn Crispin #define LTQ_EIU_IR0 (INT_NUM_IM4_IRL0 + 30) 34171bb2f1SJohn Crispin #define LTQ_EIU_IR1 (INT_NUM_IM3_IRL0 + 31) 35171bb2f1SJohn Crispin #define LTQ_EIU_IR2 (INT_NUM_IM1_IRL0 + 26) 36171bb2f1SJohn Crispin #define LTQ_EIU_IR3 INT_NUM_IM1_IRL0 37171bb2f1SJohn Crispin #define LTQ_EIU_IR4 (INT_NUM_IM1_IRL0 + 1) 38171bb2f1SJohn Crispin #define LTQ_EIU_IR5 (INT_NUM_IM1_IRL0 + 2) 39171bb2f1SJohn Crispin #define LTQ_EIU_IR6 (INT_NUM_IM2_IRL0 + 30) 40171bb2f1SJohn Crispin 41171bb2f1SJohn Crispin #define MAX_EIU 6 42171bb2f1SJohn Crispin 43171bb2f1SJohn Crispin /* irqs generated by device attached to the EBU need to be acked in 44171bb2f1SJohn Crispin * a special manner 45171bb2f1SJohn Crispin */ 46171bb2f1SJohn Crispin #define LTQ_ICU_EBU_IRQ 22 47171bb2f1SJohn Crispin 48171bb2f1SJohn Crispin #define ltq_icu_w32(x, y) ltq_w32((x), ltq_icu_membase + (y)) 49171bb2f1SJohn Crispin #define ltq_icu_r32(x) ltq_r32(ltq_icu_membase + (x)) 50171bb2f1SJohn Crispin 51171bb2f1SJohn Crispin #define ltq_eiu_w32(x, y) ltq_w32((x), ltq_eiu_membase + (y)) 52171bb2f1SJohn Crispin #define ltq_eiu_r32(x) ltq_r32(ltq_eiu_membase + (x)) 53171bb2f1SJohn Crispin 54171bb2f1SJohn Crispin static unsigned short ltq_eiu_irq[MAX_EIU] = { 55171bb2f1SJohn Crispin LTQ_EIU_IR0, 56171bb2f1SJohn Crispin LTQ_EIU_IR1, 57171bb2f1SJohn Crispin LTQ_EIU_IR2, 58171bb2f1SJohn Crispin LTQ_EIU_IR3, 59171bb2f1SJohn Crispin LTQ_EIU_IR4, 60171bb2f1SJohn Crispin LTQ_EIU_IR5, 61171bb2f1SJohn Crispin }; 62171bb2f1SJohn Crispin 63171bb2f1SJohn Crispin static struct resource ltq_icu_resource = { 64171bb2f1SJohn Crispin .name = "icu", 65171bb2f1SJohn Crispin .start = LTQ_ICU_BASE_ADDR, 66171bb2f1SJohn Crispin .end = LTQ_ICU_BASE_ADDR + LTQ_ICU_SIZE - 1, 67171bb2f1SJohn Crispin .flags = IORESOURCE_MEM, 68171bb2f1SJohn Crispin }; 69171bb2f1SJohn Crispin 70171bb2f1SJohn Crispin static struct resource ltq_eiu_resource = { 71171bb2f1SJohn Crispin .name = "eiu", 72171bb2f1SJohn Crispin .start = LTQ_EIU_BASE_ADDR, 73171bb2f1SJohn Crispin .end = LTQ_EIU_BASE_ADDR + LTQ_ICU_SIZE - 1, 74171bb2f1SJohn Crispin .flags = IORESOURCE_MEM, 75171bb2f1SJohn Crispin }; 76171bb2f1SJohn Crispin 77171bb2f1SJohn Crispin static void __iomem *ltq_icu_membase; 78171bb2f1SJohn Crispin static void __iomem *ltq_eiu_membase; 79171bb2f1SJohn Crispin 80171bb2f1SJohn Crispin void ltq_disable_irq(struct irq_data *d) 81171bb2f1SJohn Crispin { 82171bb2f1SJohn Crispin u32 ier = LTQ_ICU_IM0_IER; 83171bb2f1SJohn Crispin int irq_nr = d->irq - INT_NUM_IRQ0; 84171bb2f1SJohn Crispin 85171bb2f1SJohn Crispin ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET); 86171bb2f1SJohn Crispin irq_nr %= INT_NUM_IM_OFFSET; 87171bb2f1SJohn Crispin ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier); 88171bb2f1SJohn Crispin } 89171bb2f1SJohn Crispin 90171bb2f1SJohn Crispin void ltq_mask_and_ack_irq(struct irq_data *d) 91171bb2f1SJohn Crispin { 92171bb2f1SJohn Crispin u32 ier = LTQ_ICU_IM0_IER; 93171bb2f1SJohn Crispin u32 isr = LTQ_ICU_IM0_ISR; 94171bb2f1SJohn Crispin int irq_nr = d->irq - INT_NUM_IRQ0; 95171bb2f1SJohn Crispin 96171bb2f1SJohn Crispin ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET); 97171bb2f1SJohn Crispin isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET); 98171bb2f1SJohn Crispin irq_nr %= INT_NUM_IM_OFFSET; 99171bb2f1SJohn Crispin ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier); 100171bb2f1SJohn Crispin ltq_icu_w32((1 << irq_nr), isr); 101171bb2f1SJohn Crispin } 102171bb2f1SJohn Crispin 103171bb2f1SJohn Crispin static void ltq_ack_irq(struct irq_data *d) 104171bb2f1SJohn Crispin { 105171bb2f1SJohn Crispin u32 isr = LTQ_ICU_IM0_ISR; 106171bb2f1SJohn Crispin int irq_nr = d->irq - INT_NUM_IRQ0; 107171bb2f1SJohn Crispin 108171bb2f1SJohn Crispin isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET); 109171bb2f1SJohn Crispin irq_nr %= INT_NUM_IM_OFFSET; 110171bb2f1SJohn Crispin ltq_icu_w32((1 << irq_nr), isr); 111171bb2f1SJohn Crispin } 112171bb2f1SJohn Crispin 113171bb2f1SJohn Crispin void ltq_enable_irq(struct irq_data *d) 114171bb2f1SJohn Crispin { 115171bb2f1SJohn Crispin u32 ier = LTQ_ICU_IM0_IER; 116171bb2f1SJohn Crispin int irq_nr = d->irq - INT_NUM_IRQ0; 117171bb2f1SJohn Crispin 118171bb2f1SJohn Crispin ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET); 119171bb2f1SJohn Crispin irq_nr %= INT_NUM_IM_OFFSET; 120171bb2f1SJohn Crispin ltq_icu_w32(ltq_icu_r32(ier) | (1 << irq_nr), ier); 121171bb2f1SJohn Crispin } 122171bb2f1SJohn Crispin 123171bb2f1SJohn Crispin static unsigned int ltq_startup_eiu_irq(struct irq_data *d) 124171bb2f1SJohn Crispin { 125171bb2f1SJohn Crispin int i; 126171bb2f1SJohn Crispin 127171bb2f1SJohn Crispin ltq_enable_irq(d); 128171bb2f1SJohn Crispin for (i = 0; i < MAX_EIU; i++) { 12977fbdb30SJohn Crispin if (d->irq == ltq_eiu_irq[i]) { 130171bb2f1SJohn Crispin /* low level - we should really handle set_type */ 131171bb2f1SJohn Crispin ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | 132171bb2f1SJohn Crispin (0x6 << (i * 4)), LTQ_EIU_EXIN_C); 133171bb2f1SJohn Crispin /* clear all pending */ 134171bb2f1SJohn Crispin ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~(1 << i), 135171bb2f1SJohn Crispin LTQ_EIU_EXIN_INIC); 136171bb2f1SJohn Crispin /* enable */ 137171bb2f1SJohn Crispin ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | (1 << i), 138171bb2f1SJohn Crispin LTQ_EIU_EXIN_INEN); 139171bb2f1SJohn Crispin break; 140171bb2f1SJohn Crispin } 141171bb2f1SJohn Crispin } 142171bb2f1SJohn Crispin 143171bb2f1SJohn Crispin return 0; 144171bb2f1SJohn Crispin } 145171bb2f1SJohn Crispin 146171bb2f1SJohn Crispin static void ltq_shutdown_eiu_irq(struct irq_data *d) 147171bb2f1SJohn Crispin { 148171bb2f1SJohn Crispin int i; 149171bb2f1SJohn Crispin 150171bb2f1SJohn Crispin ltq_disable_irq(d); 151171bb2f1SJohn Crispin for (i = 0; i < MAX_EIU; i++) { 15277fbdb30SJohn Crispin if (d->irq == ltq_eiu_irq[i]) { 153171bb2f1SJohn Crispin /* disable */ 154171bb2f1SJohn Crispin ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~(1 << i), 155171bb2f1SJohn Crispin LTQ_EIU_EXIN_INEN); 156171bb2f1SJohn Crispin break; 157171bb2f1SJohn Crispin } 158171bb2f1SJohn Crispin } 159171bb2f1SJohn Crispin } 160171bb2f1SJohn Crispin 161171bb2f1SJohn Crispin static struct irq_chip ltq_irq_type = { 162171bb2f1SJohn Crispin "icu", 163171bb2f1SJohn Crispin .irq_enable = ltq_enable_irq, 164171bb2f1SJohn Crispin .irq_disable = ltq_disable_irq, 165171bb2f1SJohn Crispin .irq_unmask = ltq_enable_irq, 166171bb2f1SJohn Crispin .irq_ack = ltq_ack_irq, 167171bb2f1SJohn Crispin .irq_mask = ltq_disable_irq, 168171bb2f1SJohn Crispin .irq_mask_ack = ltq_mask_and_ack_irq, 169171bb2f1SJohn Crispin }; 170171bb2f1SJohn Crispin 171171bb2f1SJohn Crispin static struct irq_chip ltq_eiu_type = { 172171bb2f1SJohn Crispin "eiu", 173171bb2f1SJohn Crispin .irq_startup = ltq_startup_eiu_irq, 174171bb2f1SJohn Crispin .irq_shutdown = ltq_shutdown_eiu_irq, 175171bb2f1SJohn Crispin .irq_enable = ltq_enable_irq, 176171bb2f1SJohn Crispin .irq_disable = ltq_disable_irq, 177171bb2f1SJohn Crispin .irq_unmask = ltq_enable_irq, 178171bb2f1SJohn Crispin .irq_ack = ltq_ack_irq, 179171bb2f1SJohn Crispin .irq_mask = ltq_disable_irq, 180171bb2f1SJohn Crispin .irq_mask_ack = ltq_mask_and_ack_irq, 181171bb2f1SJohn Crispin }; 182171bb2f1SJohn Crispin 183171bb2f1SJohn Crispin static void ltq_hw_irqdispatch(int module) 184171bb2f1SJohn Crispin { 185171bb2f1SJohn Crispin u32 irq; 186171bb2f1SJohn Crispin 187171bb2f1SJohn Crispin irq = ltq_icu_r32(LTQ_ICU_IM0_IOSR + (module * LTQ_ICU_OFFSET)); 188171bb2f1SJohn Crispin if (irq == 0) 189171bb2f1SJohn Crispin return; 190171bb2f1SJohn Crispin 191171bb2f1SJohn Crispin /* silicon bug causes only the msb set to 1 to be valid. all 192171bb2f1SJohn Crispin * other bits might be bogus 193171bb2f1SJohn Crispin */ 194171bb2f1SJohn Crispin irq = __fls(irq); 195171bb2f1SJohn Crispin do_IRQ((int)irq + INT_NUM_IM0_IRL0 + (INT_NUM_IM_OFFSET * module)); 196171bb2f1SJohn Crispin 197171bb2f1SJohn Crispin /* if this is a EBU irq, we need to ack it or get a deadlock */ 198171bb2f1SJohn Crispin if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0)) 199171bb2f1SJohn Crispin ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | 0x10, 200171bb2f1SJohn Crispin LTQ_EBU_PCC_ISTAT); 201171bb2f1SJohn Crispin } 202171bb2f1SJohn Crispin 203171bb2f1SJohn Crispin #define DEFINE_HWx_IRQDISPATCH(x) \ 204171bb2f1SJohn Crispin static void ltq_hw ## x ## _irqdispatch(void) \ 205171bb2f1SJohn Crispin { \ 206171bb2f1SJohn Crispin ltq_hw_irqdispatch(x); \ 207171bb2f1SJohn Crispin } 208171bb2f1SJohn Crispin DEFINE_HWx_IRQDISPATCH(0) 209171bb2f1SJohn Crispin DEFINE_HWx_IRQDISPATCH(1) 210171bb2f1SJohn Crispin DEFINE_HWx_IRQDISPATCH(2) 211171bb2f1SJohn Crispin DEFINE_HWx_IRQDISPATCH(3) 212171bb2f1SJohn Crispin DEFINE_HWx_IRQDISPATCH(4) 213171bb2f1SJohn Crispin 214171bb2f1SJohn Crispin static void ltq_hw5_irqdispatch(void) 215171bb2f1SJohn Crispin { 216171bb2f1SJohn Crispin do_IRQ(MIPS_CPU_TIMER_IRQ); 217171bb2f1SJohn Crispin } 218171bb2f1SJohn Crispin 219171bb2f1SJohn Crispin asmlinkage void plat_irq_dispatch(void) 220171bb2f1SJohn Crispin { 221171bb2f1SJohn Crispin unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; 222171bb2f1SJohn Crispin unsigned int i; 223171bb2f1SJohn Crispin 224171bb2f1SJohn Crispin if (pending & CAUSEF_IP7) { 225171bb2f1SJohn Crispin do_IRQ(MIPS_CPU_TIMER_IRQ); 226171bb2f1SJohn Crispin goto out; 227171bb2f1SJohn Crispin } else { 228171bb2f1SJohn Crispin for (i = 0; i < 5; i++) { 229171bb2f1SJohn Crispin if (pending & (CAUSEF_IP2 << i)) { 230171bb2f1SJohn Crispin ltq_hw_irqdispatch(i); 231171bb2f1SJohn Crispin goto out; 232171bb2f1SJohn Crispin } 233171bb2f1SJohn Crispin } 234171bb2f1SJohn Crispin } 235171bb2f1SJohn Crispin pr_alert("Spurious IRQ: CAUSE=0x%08x\n", read_c0_status()); 236171bb2f1SJohn Crispin 237171bb2f1SJohn Crispin out: 238171bb2f1SJohn Crispin return; 239171bb2f1SJohn Crispin } 240171bb2f1SJohn Crispin 241171bb2f1SJohn Crispin static struct irqaction cascade = { 242171bb2f1SJohn Crispin .handler = no_action, 243171bb2f1SJohn Crispin .name = "cascade", 244171bb2f1SJohn Crispin }; 245171bb2f1SJohn Crispin 246171bb2f1SJohn Crispin void __init arch_init_irq(void) 247171bb2f1SJohn Crispin { 248171bb2f1SJohn Crispin int i; 249171bb2f1SJohn Crispin 250171bb2f1SJohn Crispin if (insert_resource(&iomem_resource, <q_icu_resource) < 0) 251ab75dc02SRalf Baechle panic("Failed to insert icu memory"); 252171bb2f1SJohn Crispin 253171bb2f1SJohn Crispin if (request_mem_region(ltq_icu_resource.start, 254171bb2f1SJohn Crispin resource_size(<q_icu_resource), "icu") < 0) 255ab75dc02SRalf Baechle panic("Failed to request icu memory"); 256171bb2f1SJohn Crispin 257171bb2f1SJohn Crispin ltq_icu_membase = ioremap_nocache(ltq_icu_resource.start, 258171bb2f1SJohn Crispin resource_size(<q_icu_resource)); 259171bb2f1SJohn Crispin if (!ltq_icu_membase) 260ab75dc02SRalf Baechle panic("Failed to remap icu memory"); 261171bb2f1SJohn Crispin 262171bb2f1SJohn Crispin if (insert_resource(&iomem_resource, <q_eiu_resource) < 0) 263ab75dc02SRalf Baechle panic("Failed to insert eiu memory"); 264171bb2f1SJohn Crispin 265171bb2f1SJohn Crispin if (request_mem_region(ltq_eiu_resource.start, 266171bb2f1SJohn Crispin resource_size(<q_eiu_resource), "eiu") < 0) 267ab75dc02SRalf Baechle panic("Failed to request eiu memory"); 268171bb2f1SJohn Crispin 269171bb2f1SJohn Crispin ltq_eiu_membase = ioremap_nocache(ltq_eiu_resource.start, 270171bb2f1SJohn Crispin resource_size(<q_eiu_resource)); 271171bb2f1SJohn Crispin if (!ltq_eiu_membase) 272ab75dc02SRalf Baechle panic("Failed to remap eiu memory"); 273171bb2f1SJohn Crispin 274*16f70b56SJohn Crispin /* turn off all irqs by default */ 275*16f70b56SJohn Crispin for (i = 0; i < 5; i++) { 276171bb2f1SJohn Crispin /* make sure all irqs are turned off by default */ 277171bb2f1SJohn Crispin ltq_icu_w32(0, LTQ_ICU_IM0_IER + (i * LTQ_ICU_OFFSET)); 278171bb2f1SJohn Crispin /* clear all possibly pending interrupts */ 279171bb2f1SJohn Crispin ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET)); 280*16f70b56SJohn Crispin } 281171bb2f1SJohn Crispin 282171bb2f1SJohn Crispin mips_cpu_irq_init(); 283171bb2f1SJohn Crispin 284171bb2f1SJohn Crispin for (i = 2; i <= 6; i++) 285171bb2f1SJohn Crispin setup_irq(i, &cascade); 286171bb2f1SJohn Crispin 287171bb2f1SJohn Crispin if (cpu_has_vint) { 288171bb2f1SJohn Crispin pr_info("Setting up vectored interrupts\n"); 289171bb2f1SJohn Crispin set_vi_handler(2, ltq_hw0_irqdispatch); 290171bb2f1SJohn Crispin set_vi_handler(3, ltq_hw1_irqdispatch); 291171bb2f1SJohn Crispin set_vi_handler(4, ltq_hw2_irqdispatch); 292171bb2f1SJohn Crispin set_vi_handler(5, ltq_hw3_irqdispatch); 293171bb2f1SJohn Crispin set_vi_handler(6, ltq_hw4_irqdispatch); 294171bb2f1SJohn Crispin set_vi_handler(7, ltq_hw5_irqdispatch); 295171bb2f1SJohn Crispin } 296171bb2f1SJohn Crispin 297171bb2f1SJohn Crispin for (i = INT_NUM_IRQ0; 298171bb2f1SJohn Crispin i <= (INT_NUM_IRQ0 + (5 * INT_NUM_IM_OFFSET)); i++) 299171bb2f1SJohn Crispin if ((i == LTQ_EIU_IR0) || (i == LTQ_EIU_IR1) || 300171bb2f1SJohn Crispin (i == LTQ_EIU_IR2)) 301171bb2f1SJohn Crispin irq_set_chip_and_handler(i, <q_eiu_type, 302171bb2f1SJohn Crispin handle_level_irq); 303171bb2f1SJohn Crispin /* EIU3-5 only exist on ar9 and vr9 */ 304171bb2f1SJohn Crispin else if (((i == LTQ_EIU_IR3) || (i == LTQ_EIU_IR4) || 305171bb2f1SJohn Crispin (i == LTQ_EIU_IR5)) && (ltq_is_ar9() || ltq_is_vr9())) 306171bb2f1SJohn Crispin irq_set_chip_and_handler(i, <q_eiu_type, 307171bb2f1SJohn Crispin handle_level_irq); 308171bb2f1SJohn Crispin else 309171bb2f1SJohn Crispin irq_set_chip_and_handler(i, <q_irq_type, 310171bb2f1SJohn Crispin handle_level_irq); 311171bb2f1SJohn Crispin 312171bb2f1SJohn Crispin #if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC) 313171bb2f1SJohn Crispin set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | 314171bb2f1SJohn Crispin IE_IRQ3 | IE_IRQ4 | IE_IRQ5); 315171bb2f1SJohn Crispin #else 316171bb2f1SJohn Crispin set_c0_status(IE_SW0 | IE_SW1 | IE_IRQ0 | IE_IRQ1 | 317171bb2f1SJohn Crispin IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5); 318171bb2f1SJohn Crispin #endif 319171bb2f1SJohn Crispin } 320171bb2f1SJohn Crispin 321171bb2f1SJohn Crispin unsigned int __cpuinit get_c0_compare_int(void) 322171bb2f1SJohn Crispin { 323171bb2f1SJohn Crispin return CP0_LEGACY_COMPARE_IRQ; 324171bb2f1SJohn Crispin } 325