Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+ 2 : : /* 3 : : * Root interrupt controller for the BCM2836 (Raspberry Pi 2). 4 : : * 5 : : * Copyright 2015 Broadcom 6 : : */ 7 : : 8 : : #include <linux/cpu.h> 9 : : #include <linux/of_address.h> 10 : : #include <linux/of_irq.h> 11 : : #include <linux/irqchip.h> 12 : : #include <linux/irqdomain.h> 13 : : #include <linux/irqchip/irq-bcm2836.h> 14 : : 15 : : #include <asm/exception.h> 16 : : 17 : : struct bcm2836_arm_irqchip_intc { 18 : : struct irq_domain *domain; 19 : : void __iomem *base; 20 : : }; 21 : : 22 : : static struct bcm2836_arm_irqchip_intc intc __read_mostly; 23 : : 24 : : void __iomem *arm_local_intc; 25 : : EXPORT_SYMBOL_GPL(arm_local_intc); 26 : : 27 : : static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset, 28 : : unsigned int bit, 29 : : int cpu) 30 : : { 31 : 0 : void __iomem *reg = intc.base + reg_offset + 4 * cpu; 32 : : 33 : 0 : writel(readl(reg) & ~BIT(bit), reg); 34 : : } 35 : : 36 : : static void bcm2836_arm_irqchip_unmask_per_cpu_irq(unsigned int reg_offset, 37 : : unsigned int bit, 38 : : int cpu) 39 : : { 40 : 3 : void __iomem *reg = intc.base + reg_offset + 4 * cpu; 41 : : 42 : 3 : writel(readl(reg) | BIT(bit), reg); 43 : : } 44 : : 45 : 0 : static void bcm2836_arm_irqchip_mask_timer_irq(struct irq_data *d) 46 : : { 47 : 0 : bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, 48 : 0 : d->hwirq - LOCAL_IRQ_CNTPSIRQ, 49 : 0 : smp_processor_id()); 50 : 0 : } 51 : : 52 : 3 : static void bcm2836_arm_irqchip_unmask_timer_irq(struct irq_data *d) 53 : : { 54 : 3 : bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, 55 : 3 : d->hwirq - LOCAL_IRQ_CNTPSIRQ, 56 : 3 : smp_processor_id()); 57 : 3 : } 58 : : 59 : : static struct irq_chip bcm2836_arm_irqchip_timer = { 60 : : .name = "bcm2836-timer", 61 : : .irq_mask = bcm2836_arm_irqchip_mask_timer_irq, 62 : : .irq_unmask = bcm2836_arm_irqchip_unmask_timer_irq, 63 : : }; 64 : : 65 : 0 : static void bcm2836_arm_irqchip_mask_pmu_irq(struct irq_data *d) 66 : : { 67 : 0 : writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_CLR); 68 : 0 : } 69 : : 70 : 3 : static void bcm2836_arm_irqchip_unmask_pmu_irq(struct irq_data *d) 71 : : { 72 : 3 : writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_SET); 73 : 3 : } 74 : : 75 : : static struct irq_chip bcm2836_arm_irqchip_pmu = { 76 : : .name = "bcm2836-pmu", 77 : : .irq_mask = bcm2836_arm_irqchip_mask_pmu_irq, 78 : : .irq_unmask = bcm2836_arm_irqchip_unmask_pmu_irq, 79 : : }; 80 : : 81 : 0 : static void bcm2836_arm_irqchip_mask_gpu_irq(struct irq_data *d) 82 : : { 83 : 0 : } 84 : : 85 : 3 : static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d) 86 : : { 87 : 3 : } 88 : : 89 : : #ifdef CONFIG_ARM64 90 : : 91 : : void bcm2836_arm_irqchip_spin_gpu_irq(void) 92 : : { 93 : : u32 i; 94 : : void __iomem *gpurouting = (intc.base + LOCAL_GPU_ROUTING); 95 : : u32 routing_val = readl(gpurouting); 96 : : 97 : : for (i = 1; i <= 3; i++) { 98 : : u32 new_routing_val = (routing_val + i) & 3; 99 : : 100 : : if (cpu_active(new_routing_val)) { 101 : : writel(new_routing_val, gpurouting); 102 : : return; 103 : : } 104 : : } 105 : : } 106 : : EXPORT_SYMBOL(bcm2836_arm_irqchip_spin_gpu_irq); 107 : : 108 : : #endif 109 : : 110 : : static struct irq_chip bcm2836_arm_irqchip_gpu = { 111 : : .name = "bcm2836-gpu", 112 : : .irq_mask = bcm2836_arm_irqchip_mask_gpu_irq, 113 : : .irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq, 114 : : }; 115 : : 116 : 3 : static int bcm2836_map(struct irq_domain *d, unsigned int irq, 117 : : irq_hw_number_t hw) 118 : : { 119 : : struct irq_chip *chip; 120 : : 121 : 3 : switch (hw) { 122 : : case LOCAL_IRQ_CNTPSIRQ: 123 : : case LOCAL_IRQ_CNTPNSIRQ: 124 : : case LOCAL_IRQ_CNTHPIRQ: 125 : : case LOCAL_IRQ_CNTVIRQ: 126 : : chip = &bcm2836_arm_irqchip_timer; 127 : : break; 128 : : case LOCAL_IRQ_GPU_FAST: 129 : : chip = &bcm2836_arm_irqchip_gpu; 130 : 3 : break; 131 : : case LOCAL_IRQ_PMU_FAST: 132 : : chip = &bcm2836_arm_irqchip_pmu; 133 : 3 : break; 134 : : default: 135 : 0 : pr_warn_once("Unexpected hw irq: %lu\n", hw); 136 : : return -EINVAL; 137 : : } 138 : : 139 : 3 : irq_set_percpu_devid(irq); 140 : 3 : irq_domain_set_info(d, irq, hw, chip, d->host_data, 141 : : handle_percpu_devid_irq, NULL, NULL); 142 : : irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW); 143 : : 144 : 3 : return 0; 145 : : } 146 : : 147 : : static void 148 : 3 : __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) 149 : : { 150 : 3 : int cpu = smp_processor_id(); 151 : : u32 stat; 152 : : 153 : 3 : stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); 154 : 3 : if (stat & BIT(LOCAL_IRQ_MAILBOX0)) { 155 : : #ifdef CONFIG_SMP 156 : 3 : void __iomem *mailbox0 = (intc.base + 157 : 3 : LOCAL_MAILBOX0_CLR0 + 16 * cpu); 158 : 3 : u32 mbox_val = readl(mailbox0); 159 : 3 : u32 ipi = ffs(mbox_val) - 1; 160 : : 161 : 3 : writel(1 << ipi, mailbox0); 162 : 3 : dsb(sy); 163 : 3 : handle_IPI(ipi, regs); 164 : : #endif 165 : 3 : } else if (stat) { 166 : 3 : u32 hwirq = ffs(stat) - 1; 167 : : 168 : 3 : handle_domain_irq(intc.domain, hwirq, regs); 169 : : } 170 : 3 : } 171 : : 172 : : #ifdef CONFIG_SMP 173 : 3 : static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask, 174 : : unsigned int ipi) 175 : : { 176 : : int cpu; 177 : 3 : void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0; 178 : : 179 : : /* 180 : : * Ensure that stores to normal memory are visible to the 181 : : * other CPUs before issuing the IPI. 182 : : */ 183 : 3 : smp_wmb(); 184 : : 185 : 3 : for_each_cpu(cpu, mask) { 186 : 3 : writel(1 << ipi, mailbox0_base + 16 * cpu); 187 : : } 188 : 3 : } 189 : : 190 : 3 : static int bcm2836_cpu_starting(unsigned int cpu) 191 : : { 192 : 3 : bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0, 193 : : cpu); 194 : 3 : return 0; 195 : : } 196 : : 197 : 0 : static int bcm2836_cpu_dying(unsigned int cpu) 198 : : { 199 : 0 : bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0, 200 : : cpu); 201 : 0 : return 0; 202 : : } 203 : : #endif 204 : : 205 : : static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { 206 : : .xlate = irq_domain_xlate_onetwocell, 207 : : .map = bcm2836_map, 208 : : }; 209 : : 210 : : static void 211 : 3 : bcm2836_arm_irqchip_smp_init(void) 212 : : { 213 : : #ifdef CONFIG_SMP 214 : : /* Unmask IPIs to the boot CPU. */ 215 : : cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING, 216 : : "irqchip/bcm2836:starting", bcm2836_cpu_starting, 217 : : bcm2836_cpu_dying); 218 : : 219 : 3 : set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); 220 : : #endif 221 : 3 : } 222 : : 223 : : /* 224 : : * The LOCAL_IRQ_CNT* timer firings are based off of the external 225 : : * oscillator with some scaling. The firmware sets up CNTFRQ to 226 : : * report 19.2Mhz, but doesn't set up the scaling registers. 227 : : */ 228 : : static void bcm2835_init_local_timer_frequency(void) 229 : : { 230 : : /* 231 : : * Set the timer to source from the 19.2Mhz crystal clock (bit 232 : : * 8 unset), and only increment by 1 instead of 2 (bit 9 233 : : * unset). 234 : : */ 235 : 3 : writel(0, intc.base + LOCAL_CONTROL); 236 : : 237 : : /* 238 : : * Set the timer prescaler to 1:1 (timer freq = input freq * 239 : : * 2**31 / prescaler) 240 : : */ 241 : 3 : writel(0x80000000, intc.base + LOCAL_PRESCALER); 242 : : } 243 : : 244 : 3 : static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, 245 : : struct device_node *parent) 246 : : { 247 : 3 : intc.base = of_iomap(node, 0); 248 : 3 : if (!intc.base) { 249 : 0 : panic("%pOF: unable to map local interrupt registers\n", node); 250 : : } 251 : : 252 : 3 : arm_local_intc = intc.base; 253 : : 254 : : bcm2835_init_local_timer_frequency(); 255 : : 256 : 3 : intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, 257 : : &bcm2836_arm_irqchip_intc_ops, 258 : : NULL); 259 : 3 : if (!intc.domain) 260 : 0 : panic("%pOF: unable to create IRQ domain\n", node); 261 : : 262 : 3 : bcm2836_arm_irqchip_smp_init(); 263 : : 264 : 3 : set_handle_irq(bcm2836_arm_irqchip_handle_irq); 265 : 3 : return 0; 266 : : } 267 : : 268 : : IRQCHIP_DECLARE(bcm2836_arm_irqchip_l1_intc, "brcm,bcm2836-l1-intc", 269 : : bcm2836_arm_irqchip_l1_intc_of_init);