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        irq_data_update_effective_affinity(d, cpumask_of(cpu));
 169
 170        return IRQ_SET_MASK_OK;
 171}
 172#endif
 173
 174static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs)
 175{
 176        u32 irqstat, irqnr;
 177        void __iomem *cpu_base = hip04_data.cpu_base;
 178
 179        do {
 180                irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
 181                irqnr = irqstat & GICC_IAR_INT_ID_MASK;
 182
 183                if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) {
 184                        handle_domain_irq(hip04_data.domain, irqnr, regs);
 185                        continue;
 186                }
 187                if (irqnr < 16) {
 188                        writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
 189#ifdef CONFIG_SMP
 190                        handle_IPI(irqnr, regs);
 191#endif
 192                        continue;
 193                }
 194                break;
 195        } while (1);
 196}
 197
 198static struct irq_chip hip04_irq_chip = {
 199        .name                   = "HIP04 INTC",
 200        .irq_mask               = hip04_mask_irq,
 201        .irq_unmask             = hip04_unmask_irq,
 202        .irq_eoi                = hip04_eoi_irq,
 203        .irq_set_type           = hip04_irq_set_type,
 204#ifdef CONFIG_SMP
 205        .irq_set_affinity       = hip04_irq_set_affinity,
 206#endif
 207        .flags                  = IRQCHIP_SET_TYPE_MASKED |
 208                                  IRQCHIP_SKIP_SET_WAKE |
 209                                  IRQCHIP_MASK_ON_SUSPEND,
 210};
 211
 212static u16 hip04_get_cpumask(struct hip04_irq_data *intc)
 213{
 214        void __iomem *base = intc->dist_base;
 215        u32 mask, i;
 216
 217        for (i = mask = 0; i < 32; i += 2) {
 218                mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2);
 219                mask |= mask >> 16;
 220                if (mask)
 221                        break;
 222        }
 223
 224        if (!mask)
 225                pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
 226
 227        return mask;
 228}
 229
 230static void __init hip04_irq_dist_init(struct hip04_irq_data *intc)
 231{
 232        unsigned int i;
 233        u32 cpumask;
 234        unsigned int nr_irqs = intc->nr_irqs;
 235        void __iomem *base = intc->dist_base;
 236
 237        writel_relaxed(0, base + GIC_DIST_CTRL);
 238
 239        /*
 240         * Set all global interrupts to this CPU only.
 241         */
 242        cpumask = hip04_get_cpumask(intc);
 243        cpumask |= cpumask << 16;
 244        for (i = 32; i < nr_irqs; i += 2)
 245                writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3));
 246
 247        gic_dist_config(base, nr_irqs, NULL);
 248
 249        writel_relaxed(1, base + GIC_DIST_CTRL);
 250}
 251
 252static void hip04_irq_cpu_init(struct hip04_irq_data *intc)
 253{
 254        void __iomem *dist_base = intc->dist_base;
 255        void __iomem *base = intc->cpu_base;
 256        unsigned int cpu_mask, cpu = smp_processor_id();
 257        int i;
 258
 259        /*
 260         * Get what the GIC says our CPU mask is.
 261         */
 262        BUG_ON(cpu >= NR_HIP04_CPU_IF);
 263        cpu_mask = hip04_get_cpumask(intc);
 264        hip04_cpu_map[cpu] = cpu_mask;
 265
 266        /*
 267         * Clear our mask from the other map entries in case they're
 268         * still undefined.
 269         */
 270        for (i = 0; i < NR_HIP04_CPU_IF; i++)
 271                if (i != cpu)
 272                        hip04_cpu_map[i] &= ~cpu_mask;
 273
 274        gic_cpu_config(dist_base, NULL);
 275
 276        writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
 277        writel_relaxed(1, base + GIC_CPU_CTRL);
 278}
 279
 280#ifdef CONFIG_SMP
 281static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq)
 282{
 283        int cpu;
 284        unsigned long flags, map = 0;
 285
 286        raw_spin_lock_irqsave(&irq_controller_lock, flags);
 287
 288        /* Convert our logical CPU mask into a physical one. */
 289        for_each_cpu(cpu, mask)
 290                map |= hip04_cpu_map[cpu];
 291
 292        /*
 293         * Ensure that stores to Normal memory are visible to the
 294         * other CPUs before they observe us issuing the IPI.
 295         */
 296        dmb(ishst);
 297
 298        /* this always happens on GIC0 */
 299        writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT);
 300
 301        raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
 302}
 303#endif
 304
 305static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
 306                                irq_hw_number_t hw)
 307{
 308        if (hw < 32) {
 309                irq_set_percpu_devid(irq);
 310                irq_set_chip_and_handler(irq, &hip04_irq_chip,
 311                                         handle_percpu_devid_irq);
 312                irq_set_status_flags(irq, IRQ_NOAUTOEN);
 313        } else {
 314                irq_set_chip_and_handler(irq, &hip04_irq_chip,
 315                                         handle_fasteoi_irq);
 316                irq_set_probe(irq);
 317                irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
 318        }
 319        irq_set_chip_data(irq, d->host_data);
 320        return 0;
 321}
 322
 323static int hip04_irq_domain_xlate(struct irq_domain *d,
 324                                  struct device_node *controller,
 325                                  const u32 *intspec, unsigned int intsize,
 326                                  unsigned long *out_hwirq,
 327                                  unsigned int *out_type)
 328{
 329        unsigned long ret = 0;
 330
 331        if (irq_domain_get_of_node(d) != controller)
 332                return -EINVAL;
 333        if (intsize < 3)
 334                return -EINVAL;
 335
 336        /* Get the interrupt number and add 16 to skip over SGIs */
 337        *out_hwirq = intspec[1] + 16;
 338
 339        /* For SPIs, we need to add 16 more to get the irq ID number */
 340        if (!intspec[0])
 341                *out_hwirq += 16;
 342
 343        *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
 344
 345        return ret;
 346}
 347
 348static int hip04_irq_starting_cpu(unsigned int cpu)
 349{
 350        hip04_irq_cpu_init(&hip04_data);
 351        return 0;
 352}
 353
 354static const struct irq_domain_ops hip04_irq_domain_ops = {
 355        .map    = hip04_irq_domain_map,
 356        .xlate  = hip04_irq_domain_xlate,
 357};
 358
 359static int __init
 360hip04_of_init(struct device_node *node, struct device_node *parent)
 361{
 362        irq_hw_number_t hwirq_base = 16;
 363        int nr_irqs, irq_base, i;
 364
 365        if (WARN_ON(!node))
 366                return -ENODEV;
 367
 368        hip04_data.dist_base = of_iomap(node, 0);
 369        WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n");
 370
 371        hip04_data.cpu_base = of_iomap(node, 1);
 372        WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n");
 373
 374        /*
 375         * Initialize the CPU interface map to all CPUs.
 376         * It will be refined as each CPU probes its ID.
 377         */
 378        for (i = 0; i < NR_HIP04_CPU_IF; i++)
 379                hip04_cpu_map[i] = 0xffff;
 380
 381        /*
 382         * Find out how many interrupts are supported.
 383         * The HIP04 INTC only supports up to 510 interrupt sources.
 384         */
 385        nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f;
 386        nr_irqs = (nr_irqs + 1) * 32;
 387        if (nr_irqs > HIP04_MAX_IRQS)
 388                nr_irqs = HIP04_MAX_IRQS;
 389        hip04_data.nr_irqs = nr_irqs;
 390
 391        nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */
 392
 393        irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id());
 394        if (irq_base < 0) {
 395                pr_err("failed to allocate IRQ numbers\n");
 396                return -EINVAL;
 397        }
 398
 399        hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base,
 400                                                  hwirq_base,
 401                                                  &hip04_irq_domain_ops,
 402                                                  &hip04_data);
 403
 404        if (WARN_ON(!hip04_data.domain))
 405                return -EINVAL;
 406
 407#ifdef CONFIG_SMP
 408        set_smp_cross_call(hip04_raise_softirq);
 409#endif
 410        set_handle_irq(hip04_handle_irq);
 411
 412        hip04_irq_dist_init(&hip04_data);
 413        cpuhp_setup_state(CPUHP_AP_IRQ_HIP04_STARTING, "irqchip/hip04:starting",
 414                          hip04_irq_starting_cpu, NULL);
 415        return 0;
 416}
 417IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init);
 418