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