linux/drivers/irqchip/irq-hip04.c
<<
>>
Prefs
   1/*
   2 * Hisilicon HiP04 INTC
   3 *
   4 * Copyright (C) 2002-2014 ARM Limited.
   5 * Copyright (c) 2013-2014 Hisilicon Ltd.
   6 * Copyright (c) 2013-2014 Linaro Ltd.
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 * Interrupt architecture for the HIP04 INTC:
  13 *
  14 * o There is one Interrupt Distributor, which receives interrupts
  15 *   from system devices and sends them to the Interrupt Controllers.
  16 *
  17 * o There is one CPU Interface per CPU, which sends interrupts sent
  18 *   by the Distributor, and interrupts generated locally, to the
  19 *   associated CPU. The base address of the CPU interface is usually
  20 *   aliased so that the same address points to different chips depending
  21 *   on the CPU it is accessed from.
  22 *
  23 * Note that IRQs 0-31 are special - they are local to each CPU.
  24 * As such, the enable set/clear, pending set/clear and active bit
  25 * registers are banked per-cpu for these sources.
  26 */
  27
  28#include <linux/init.h>
  29#include <linux/kernel.h>
  30#include <linux/err.h>
  31#include <linux/module.h>
  32#include <linux/list.h>
  33#include <linux/smp.h>
  34#include <linux/cpu.h>
  35#include <linux/cpu_pm.h>
  36#include <linux/cpumask.h>
  37#include <linux/io.h>
  38#include <linux/of.h>
  39#include <linux/of_address.h>
  40#include <linux/of_irq.h>
  41#include <linux/irqdomain.h>
  42#include <linux/interrupt.h>
  43#include <linux/slab.h>
  44#include <linux/irqchip.h>
  45#include <linux/irqchip/arm-gic.h>
  46
  47#include <asm/irq.h>
  48#include <asm/exception.h>
  49#include <asm/smp_plat.h>
  50
  51#include "irq-gic-common.h"
  52
  53#define HIP04_MAX_IRQS          510
  54
  55struct hip04_irq_data {
  56        void __iomem *dist_base;
  57        void __iomem *cpu_base;
  58        struct irq_domain *domain;
  59        unsigned int nr_irqs;
  60};
  61
  62static DEFINE_RAW_SPINLOCK(irq_controller_lock);
  63
  64/*
  65 * The GIC mapping of CPU interfaces does not necessarily match
  66 * the logical CPU numbering.  Let's use a mapping as returned
  67 * by the GIC itself.
  68 */
  69#define NR_HIP04_CPU_IF 16
  70static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly;
  71
  72static struct hip04_irq_data hip04_data __read_mostly;
  73
  74static inline void __iomem *hip04_dist_base(struct irq_data *d)
  75{
  76        struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
  77        return hip04_data->dist_base;
  78}
  79
  80static inline void __iomem *hip04_cpu_base(struct irq_data *d)
  81{
  82        struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d);
  83        return hip04_data->cpu_base;
  84}
  85
  86static inline unsigned int hip04_irq(struct irq_data *d)
  87{
  88        return d->hwirq;
  89}
  90
  91/*
  92 * Routines to acknowledge, disable and enable interrupts
  93 */
  94static void hip04_mask_irq(struct irq_data *d)
  95{
  96        u32 mask = 1 << (hip04_irq(d) % 32);
  97
  98        raw_spin_lock(&irq_controller_lock);
  99        writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR +
 100                       (hip04_irq(d) / 32) * 4);
 101        raw_spin_unlock(&irq_controller_lock);
 102}
 103
 104static void hip04_unmask_irq(struct irq_data *d)
 105{
 106        u32 mask = 1 << (hip04_irq(d) % 32);
 107
 108        raw_spin_lock(&irq_controller_lock);
 109        writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET +
 110                       (hip04_irq(d) / 32) * 4);
 111        raw_spin_unlock(&irq_controller_lock);
 112}
 113
 114static void hip04_eoi_irq(struct irq_data *d)
 115{
 116        writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI);
 117}
 118
 119static int hip04_irq_set_type(struct irq_data *d, unsigned int type)
 120{
 121        void __iomem *base = hip04_dist_base(d);
 122        unsigned int irq = hip04_irq(d);
 123        int ret;
 124
 125        /* Interrupt configuration for SGIs can't be changed */
 126        if (irq < 16)
 127                return -EINVAL;
 128
 129        /* SPIs have restrictions on the supported types */
 130        if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
 131                         type != IRQ_TYPE_EDGE_RISING)
 132                return -EINVAL;
 133
 134        raw_spin_lock(&irq_controller_lock);
 135
 136        ret = gic_configure_irq(irq, type, base, NULL);
 137
 138        raw_spin_unlock(&irq_controller_lock);
 139
 140        return ret;
 141}
 142
 143#ifdef CONFIG_SMP
 144static int hip04_irq_set_affinity(struct irq_data *d,
 145                                  const struct cpumask *mask_val,
 146                                  bool force)
 147{
 148        void __iomem *reg;
 149        unsigned int cpu, shift = (hip04_irq(d) % 2) * 16;
 150        u32 val, mask, bit;
 151
 152        if (!force)
 153                cpu = cpumask_any_and(mask_val, cpu_online_mask);
 154        else
 155                cpu = cpumask_first(mask_val);
 156
 157        if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids)
 158                return -EINVAL;
 159
 160        raw_spin_lock(&irq_controller_lock);
 161        reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3);
 162        mask = 0xffff << shift;
 163        bit = hip04_cpu_map[cpu] << shift;
 164        val = readl_relaxed(reg) & ~mask;
 165        writel_relaxed(val | bit, reg);
 166        raw_spin_unlock(&irq_controller_lock);
 167
 168        return IRQ_SET_MASK_OK;
 169}
 170#endif
 171
 172static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
 173{
 174        u32 irqstat, irqnr;
 175        void __iomem *cpu_base = hip04_data.cpu_base;
 176
 177        do {
 178                irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
 179                irqnr = irqstat & GICC_IAR_INT_ID_MASK;
 180
 181                if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
 182                        handle_domain_irq(hip04_data.domain, irqnr, regs);
 183                        continue;
 184                }
 185                if (irqnr < 16) {
 186                        writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
 187#ifdef CONFIG_SMP
 188                        handle_IPI(irqnr, regs);
 189#endif
 190                        continue;
 191                }
 192                break;
 193        } while (1);
 194}
 195
 196static struct irq_chip hip04_irq_chip = {
 197        .name                   = "HIP04 INTC",
 198        .irq_mask               = hip04_mask_irq,
 199        .irq_unmask             = hip04_unmask_irq,
 200        .irq_eoi                = hip04_eoi_irq,
 201        .irq_set_type           = hip04_irq_set_type,
 202#ifdef CONFIG_SMP
 203        .irq_set_affinity       = hip04_irq_set_affinity,
 204#endif
 205        .flags                  = IRQCHIP_SET_TYPE_MASKED |
 206                                  IRQCHIP_SKIP_SET_WAKE |
 207                                  IRQCHIP_MASK_ON_SUSPEND,
 208};
 209
 210static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
 211{
 212        void __iomem *base = intc->dist_base;
 213        u32 mask, i;
 214
 215        for (i = mask = 0; i < 32; i += 2) {
 216                mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2);
 217                mask |= mask >> 16;
 218                if (mask)
 219                        break;
 220        }
 221
 222        if (!mask)
 223                pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
 224
 225        return mask;
 226}
 227
 228static void __init hip04_irq_dist_init(struct hip04_irq_data *intc)
 229{
 230        unsigned int i;
 231        u32 cpumask;
 232        unsigned int nr_irqs = intc->nr_irqs;
 233        void __iomem *base = intc->dist_base;
 234
 235        writel_relaxed(0, base + GIC_DIST_CTRL);
 236
 237        /*
 238         * Set all global interrupts to this CPU only.
 239         */
 240        cpumask = hip04_get_cpumask(intc);
 241        cpumask |= cpumask << 16;
 242        for (i = 32; i < nr_irqs; i += 2)
 243                writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3));
 244
 245        gic_dist_config(base, nr_irqs, NULL);
 246
 247        writel_relaxed(1, base + GIC_DIST_CTRL);
 248}
 249
 250static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
 251{
 252        void __iomem *dist_base = intc->dist_base;
 253        void __iomem *base = intc->cpu_base;
 254        unsigned int cpu_mask, cpu = smp_processor_id();
 255        int i;
 256
 257        /*
 258         * Get what the GIC says our CPU mask is.
 259         */
 260        BUG_ON(cpu >= NR_HIP04_CPU_IF);
 261        cpu_mask = hip04_get_cpumask(intc);
 262        hip04_cpu_map[cpu] = cpu_mask;
 263
 264        /*
 265         * Clear our mask from the other map entries in case they're
 266         * still undefined.
 267         */
 268        for (i = 0; i < NR_HIP04_CPU_IF; i++)
 269                if (i != cpu)
 270                        hip04_cpu_map[i] &= ~cpu_mask;
 271
 272        gic_cpu_config(dist_base, NULL);
 273
 274        writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
 275        writel_relaxed(1, base + GIC_CPU_CTRL);
 276}
 277
 278#ifdef CONFIG_SMP
 279static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
 280{
 281        int cpu;
 282        unsigned long flags, map = 0;
 283
 284        raw_spin_lock_irqsave(&irq_controller_lock, flags);
 285
 286        /* Convert our logical CPU mask into a physical one. */
 287        for_each_cpu(cpu, mask)
 288                map |= hip04_cpu_map[cpu];
 289
 290        /*
 291         * Ensure that stores to Normal memory are visible to the
 292         * other CPUs before they observe us issuing the IPI.
 293         */
 294        dmb(ishst);
 295
 296        /* this always happens on GIC0 */
 297        writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
 298
 299        raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
 300}
 301#endif
 302
 303static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
 304                                irq_hw_number_t hw)
 305{
 306        if (hw < 32) {
 307                irq_set_percpu_devid(irq);
 308                irq_set_chip_and_handler(irq, &hip04_irq_chip,
 309                                         handle_percpu_devid_irq);
 310                irq_set_status_flags(irq, IRQ_NOAUTOEN);
 311        } else {
 312                irq_set_chip_and_handler(irq, &hip04_irq_chip,
 313                                         handle_fasteoi_irq);
 314                irq_set_probe(irq);
 315        }
 316        irq_set_chip_data(irq, d->host_data);
 317        return 0;
 318}
 319
 320static int hip04_irq_domain_xlate(struct irq_domain *d,
 321                                  struct device_node *controller,
 322                                  const u32 *intspec, unsigned int intsize,
 323                                  unsigned long *out_hwirq,
 324                                  unsigned int *out_type)
 325{
 326        unsigned long ret = 0;
 327
 328        if (irq_domain_get_of_node(d) != controller)
 329                return -EINVAL;
 330        if (intsize < 3)
 331                return -EINVAL;
 332
 333        /* Get the interrupt number and add 16 to skip over SGIs */
 334        *out_hwirq = intspec[1] + 16;
 335
 336        /* For SPIs, we need to add 16 more to get the irq ID number */
 337        if (!intspec[0])
 338                *out_hwirq += 16;
 339
 340        *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
 341
 342        return ret;
 343}
 344
 345static int hip04_irq_starting_cpu(unsigned int cpu)
 346{
 347        hip04_irq_cpu_init(&hip04_data);
 348        return 0;
 349}
 350
 351static const struct irq_domain_ops hip04_irq_domain_ops = {
 352        .map    = hip04_irq_domain_map,
 353        .xlate  = hip04_irq_domain_xlate,
 354};
 355
 356static int __init
 357hip04_of_init(struct device_node *node, struct device_node *parent)
 358{
 359        irq_hw_number_t hwirq_base = 16;
 360        int nr_irqs, irq_base, i;
 361
 362        if (WARN_ON(!node))
 363                return -ENODEV;
 364
 365        hip04_data.dist_base = of_iomap(node, 0);
 366        WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n");
 367
 368        hip04_data.cpu_base = of_iomap(node, 1);
 369        WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n");
 370
 371        /*
 372         * Initialize the CPU interface map to all CPUs.
 373         * It will be refined as each CPU probes its ID.
 374         */
 375        for (i = 0; i < NR_HIP04_CPU_IF; i++)
 376                hip04_cpu_map[i] = 0xffff;
 377
 378        /*
 379         * Find out how many interrupts are supported.
 380         * The HIP04 INTC only supports up to 510 interrupt sources.
 381         */
 382        nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f;
 383        nr_irqs = (nr_irqs + 1) * 32;
 384        if (nr_irqs > HIP04_MAX_IRQS)
 385                nr_irqs = HIP04_MAX_IRQS;
 386        hip04_data.nr_irqs = nr_irqs;
 387
 388        nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */
 389
 390        irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
 391        if (irq_base < 0) {
 392                pr_err("failed to allocate IRQ numbers\n");
 393                return -EINVAL;
 394        }
 395
 396        hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
 397                                                  hwirq_base,
 398                                                  &hip04_irq_domain_ops,
 399                                                  &hip04_data);
 400
 401        if (WARN_ON(!hip04_data.domain))
 402                return -EINVAL;
 403
 404#ifdef CONFIG_SMP
 405        set_smp_cross_call(hip04_raise_softirq);
 406#endif
 407        set_handle_irq(hip04_handle_irq);
 408
 409        hip04_irq_dist_init(&hip04_data);
 410        cpuhp_setup_state(CPUHP_AP_IRQ_HIP04_STARTING, "irqchip/hip04:starting",
 411                          hip04_irq_starting_cpu, NULL);
 412        return 0;
 413}
 414IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init);
 415