linux/drivers/irqchip/irq-armada-370-xp.c
<<
>>
Prefs
   1/*
   2 * Marvell Armada 370 and Armada XP SoC IRQ handling
   3 *
   4 * Copyright (C) 2012 Marvell
   5 *
   6 * Lior Amsalem <alior@marvell.com>
   7 * Gregory CLEMENT <gregory.clement@free-electrons.com>
   8 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   9 * Ben Dooks <ben.dooks@codethink.co.uk>
  10 *
  11 * This file is licensed under the terms of the GNU General Public
  12 * License version 2.  This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/init.h>
  19#include <linux/irq.h>
  20#include <linux/interrupt.h>
  21#include <linux/irqchip/chained_irq.h>
  22#include <linux/io.h>
  23#include <linux/of_address.h>
  24#include <linux/of_irq.h>
  25#include <linux/of_pci.h>
  26#include <linux/irqdomain.h>
  27#include <linux/slab.h>
  28#include <linux/msi.h>
  29#include <asm/mach/arch.h>
  30#include <asm/exception.h>
  31#include <asm/smp_plat.h>
  32#include <asm/mach/irq.h>
  33
  34#include "irqchip.h"
  35
  36/* Interrupt Controller Registers Map */
  37#define ARMADA_370_XP_INT_SET_MASK_OFFS         (0x48)
  38#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS       (0x4C)
  39
  40#define ARMADA_370_XP_INT_CONTROL               (0x00)
  41#define ARMADA_370_XP_INT_SET_ENABLE_OFFS       (0x30)
  42#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS     (0x34)
  43#define ARMADA_370_XP_INT_SOURCE_CTL(irq)       (0x100 + irq*4)
  44#define ARMADA_370_XP_INT_SOURCE_CPU_MASK       0xF
  45
  46#define ARMADA_370_XP_CPU_INTACK_OFFS           (0x44)
  47#define ARMADA_375_PPI_CAUSE                    (0x10)
  48
  49#define ARMADA_370_XP_SW_TRIG_INT_OFFS           (0x4)
  50#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS          (0xc)
  51#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS        (0x8)
  52
  53#define ARMADA_370_XP_MAX_PER_CPU_IRQS          (28)
  54
  55#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ        (5)
  56
  57#define IPI_DOORBELL_START                      (0)
  58#define IPI_DOORBELL_END                        (8)
  59#define IPI_DOORBELL_MASK                       0xFF
  60#define PCI_MSI_DOORBELL_START                  (16)
  61#define PCI_MSI_DOORBELL_NR                     (16)
  62#define PCI_MSI_DOORBELL_END                    (32)
  63#define PCI_MSI_DOORBELL_MASK                   0xFFFF0000
  64
  65static void __iomem *per_cpu_int_base;
  66static void __iomem *main_int_base;
  67static struct irq_domain *armada_370_xp_mpic_domain;
  68#ifdef CONFIG_PCI_MSI
  69static struct irq_domain *armada_370_xp_msi_domain;
  70static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
  71static DEFINE_MUTEX(msi_used_lock);
  72static phys_addr_t msi_doorbell_addr;
  73#endif
  74
  75/*
  76 * In SMP mode:
  77 * For shared global interrupts, mask/unmask global enable bit
  78 * For CPU interrupts, mask/unmask the calling CPU's bit
  79 */
  80static void armada_370_xp_irq_mask(struct irq_data *d)
  81{
  82        irq_hw_number_t hwirq = irqd_to_hwirq(d);
  83
  84        if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
  85                writel(hwirq, main_int_base +
  86                                ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
  87        else
  88                writel(hwirq, per_cpu_int_base +
  89                                ARMADA_370_XP_INT_SET_MASK_OFFS);
  90}
  91
  92static void armada_370_xp_irq_unmask(struct irq_data *d)
  93{
  94        irq_hw_number_t hwirq = irqd_to_hwirq(d);
  95
  96        if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
  97                writel(hwirq, main_int_base +
  98                                ARMADA_370_XP_INT_SET_ENABLE_OFFS);
  99        else
 100                writel(hwirq, per_cpu_int_base +
 101                                ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 102}
 103
 104#ifdef CONFIG_PCI_MSI
 105
 106static int armada_370_xp_alloc_msi(void)
 107{
 108        int hwirq;
 109
 110        mutex_lock(&msi_used_lock);
 111        hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
 112        if (hwirq >= PCI_MSI_DOORBELL_NR)
 113                hwirq = -ENOSPC;
 114        else
 115                set_bit(hwirq, msi_used);
 116        mutex_unlock(&msi_used_lock);
 117
 118        return hwirq;
 119}
 120
 121static void armada_370_xp_free_msi(int hwirq)
 122{
 123        mutex_lock(&msi_used_lock);
 124        if (!test_bit(hwirq, msi_used))
 125                pr_err("trying to free unused MSI#%d\n", hwirq);
 126        else
 127                clear_bit(hwirq, msi_used);
 128        mutex_unlock(&msi_used_lock);
 129}
 130
 131static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
 132                                       struct pci_dev *pdev,
 133                                       struct msi_desc *desc)
 134{
 135        struct msi_msg msg;
 136        int virq, hwirq;
 137
 138        hwirq = armada_370_xp_alloc_msi();
 139        if (hwirq < 0)
 140                return hwirq;
 141
 142        virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
 143        if (!virq) {
 144                armada_370_xp_free_msi(hwirq);
 145                return -EINVAL;
 146        }
 147
 148        irq_set_msi_desc(virq, desc);
 149
 150        msg.address_lo = msi_doorbell_addr;
 151        msg.address_hi = 0;
 152        msg.data = 0xf00 | (hwirq + 16);
 153
 154        write_msi_msg(virq, &msg);
 155        return 0;
 156}
 157
 158static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
 159                                           unsigned int irq)
 160{
 161        struct irq_data *d = irq_get_irq_data(irq);
 162        unsigned long hwirq = d->hwirq;
 163
 164        irq_dispose_mapping(irq);
 165        armada_370_xp_free_msi(hwirq);
 166}
 167
 168static int armada_370_xp_check_msi_device(struct msi_chip *chip, struct pci_dev *dev,
 169                                          int nvec, int type)
 170{
 171        /* We support MSI, but not MSI-X */
 172        if (type == PCI_CAP_ID_MSI)
 173                return 0;
 174        return -EINVAL;
 175}
 176
 177static struct irq_chip armada_370_xp_msi_irq_chip = {
 178        .name = "armada_370_xp_msi_irq",
 179        .irq_enable = unmask_msi_irq,
 180        .irq_disable = mask_msi_irq,
 181        .irq_mask = mask_msi_irq,
 182        .irq_unmask = unmask_msi_irq,
 183};
 184
 185static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
 186                                 irq_hw_number_t hw)
 187{
 188        irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
 189                                 handle_simple_irq);
 190        set_irq_flags(virq, IRQF_VALID);
 191
 192        return 0;
 193}
 194
 195static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
 196        .map = armada_370_xp_msi_map,
 197};
 198
 199static int armada_370_xp_msi_init(struct device_node *node,
 200                                  phys_addr_t main_int_phys_base)
 201{
 202        struct msi_chip *msi_chip;
 203        u32 reg;
 204        int ret;
 205
 206        msi_doorbell_addr = main_int_phys_base +
 207                ARMADA_370_XP_SW_TRIG_INT_OFFS;
 208
 209        msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
 210        if (!msi_chip)
 211                return -ENOMEM;
 212
 213        msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
 214        msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
 215        msi_chip->check_device = armada_370_xp_check_msi_device;
 216        msi_chip->of_node = node;
 217
 218        armada_370_xp_msi_domain =
 219                irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
 220                                      &armada_370_xp_msi_irq_ops,
 221                                      NULL);
 222        if (!armada_370_xp_msi_domain) {
 223                kfree(msi_chip);
 224                return -ENOMEM;
 225        }
 226
 227        ret = of_pci_msi_chip_add(msi_chip);
 228        if (ret < 0) {
 229                irq_domain_remove(armada_370_xp_msi_domain);
 230                kfree(msi_chip);
 231                return ret;
 232        }
 233
 234        reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
 235                | PCI_MSI_DOORBELL_MASK;
 236
 237        writel(reg, per_cpu_int_base +
 238               ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
 239
 240        /* Unmask IPI interrupt */
 241        writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 242
 243        return 0;
 244}
 245#else
 246static inline int armada_370_xp_msi_init(struct device_node *node,
 247                                         phys_addr_t main_int_phys_base)
 248{
 249        return 0;
 250}
 251#endif
 252
 253#ifdef CONFIG_SMP
 254static DEFINE_RAW_SPINLOCK(irq_controller_lock);
 255
 256static int armada_xp_set_affinity(struct irq_data *d,
 257                                  const struct cpumask *mask_val, bool force)
 258{
 259        irq_hw_number_t hwirq = irqd_to_hwirq(d);
 260        unsigned long reg, mask;
 261        int cpu;
 262
 263        /* Select a single core from the affinity mask which is online */
 264        cpu = cpumask_any_and(mask_val, cpu_online_mask);
 265        mask = 1UL << cpu_logical_map(cpu);
 266
 267        raw_spin_lock(&irq_controller_lock);
 268        reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
 269        reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask;
 270        writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
 271        raw_spin_unlock(&irq_controller_lock);
 272
 273        return 0;
 274}
 275#endif
 276
 277static struct irq_chip armada_370_xp_irq_chip = {
 278        .name           = "armada_370_xp_irq",
 279        .irq_mask       = armada_370_xp_irq_mask,
 280        .irq_mask_ack   = armada_370_xp_irq_mask,
 281        .irq_unmask     = armada_370_xp_irq_unmask,
 282#ifdef CONFIG_SMP
 283        .irq_set_affinity = armada_xp_set_affinity,
 284#endif
 285};
 286
 287static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
 288                                      unsigned int virq, irq_hw_number_t hw)
 289{
 290        armada_370_xp_irq_mask(irq_get_irq_data(virq));
 291        if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
 292                writel(hw, per_cpu_int_base +
 293                        ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 294        else
 295                writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
 296        irq_set_status_flags(virq, IRQ_LEVEL);
 297
 298        if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) {
 299                irq_set_percpu_devid(virq);
 300                irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
 301                                        handle_percpu_devid_irq);
 302
 303        } else {
 304                irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
 305                                        handle_level_irq);
 306        }
 307        set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
 308
 309        return 0;
 310}
 311
 312#ifdef CONFIG_SMP
 313void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
 314{
 315        int cpu;
 316        unsigned long map = 0;
 317
 318        /* Convert our logical CPU mask into a physical one. */
 319        for_each_cpu(cpu, mask)
 320                map |= 1 << cpu_logical_map(cpu);
 321
 322        /*
 323         * Ensure that stores to Normal memory are visible to the
 324         * other CPUs before issuing the IPI.
 325         */
 326        dsb();
 327
 328        /* submit softirq */
 329        writel((map << 8) | irq, main_int_base +
 330                ARMADA_370_XP_SW_TRIG_INT_OFFS);
 331}
 332
 333void armada_xp_mpic_smp_cpu_init(void)
 334{
 335        /* Clear pending IPIs */
 336        writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
 337
 338        /* Enable first 8 IPIs */
 339        writel(IPI_DOORBELL_MASK, per_cpu_int_base +
 340                ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
 341
 342        /* Unmask IPI interrupt */
 343        writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 344}
 345#endif /* CONFIG_SMP */
 346
 347static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
 348        .map = armada_370_xp_mpic_irq_map,
 349        .xlate = irq_domain_xlate_onecell,
 350};
 351
 352#ifdef CONFIG_PCI_MSI
 353static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
 354{
 355        u32 msimask, msinr;
 356
 357        msimask = readl_relaxed(per_cpu_int_base +
 358                                ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
 359                & PCI_MSI_DOORBELL_MASK;
 360
 361        writel(~msimask, per_cpu_int_base +
 362               ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
 363
 364        for (msinr = PCI_MSI_DOORBELL_START;
 365             msinr < PCI_MSI_DOORBELL_END; msinr++) {
 366                int irq;
 367
 368                if (!(msimask & BIT(msinr)))
 369                        continue;
 370
 371                irq = irq_find_mapping(armada_370_xp_msi_domain,
 372                                       msinr - 16);
 373
 374                if (is_chained)
 375                        generic_handle_irq(irq);
 376                else
 377                        handle_IRQ(irq, regs);
 378        }
 379}
 380#else
 381static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
 382#endif
 383
 384static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq,
 385                                                  struct irq_desc *desc)
 386{
 387        struct irq_chip *chip = irq_get_chip(irq);
 388        unsigned long irqmap, irqn;
 389        unsigned int cascade_irq;
 390
 391        chained_irq_enter(chip, desc);
 392
 393        irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
 394
 395        if (irqmap & BIT(0)) {
 396                armada_370_xp_handle_msi_irq(NULL, true);
 397                irqmap &= ~BIT(0);
 398        }
 399
 400        for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
 401                cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
 402                generic_handle_irq(cascade_irq);
 403        }
 404
 405        chained_irq_exit(chip, desc);
 406}
 407
 408static void __exception_irq_entry
 409armada_370_xp_handle_irq(struct pt_regs *regs)
 410{
 411        u32 irqstat, irqnr;
 412
 413        do {
 414                irqstat = readl_relaxed(per_cpu_int_base +
 415                                        ARMADA_370_XP_CPU_INTACK_OFFS);
 416                irqnr = irqstat & 0x3FF;
 417
 418                if (irqnr > 1022)
 419                        break;
 420
 421                if (irqnr > 1) {
 422                        irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
 423                                        irqnr);
 424                        handle_IRQ(irqnr, regs);
 425                        continue;
 426                }
 427
 428                /* MSI handling */
 429                if (irqnr == 1)
 430                        armada_370_xp_handle_msi_irq(regs, false);
 431
 432#ifdef CONFIG_SMP
 433                /* IPI Handling */
 434                if (irqnr == 0) {
 435                        u32 ipimask, ipinr;
 436
 437                        ipimask = readl_relaxed(per_cpu_int_base +
 438                                                ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
 439                                & IPI_DOORBELL_MASK;
 440
 441                        writel(~ipimask, per_cpu_int_base +
 442                                ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
 443
 444                        /* Handle all pending doorbells */
 445                        for (ipinr = IPI_DOORBELL_START;
 446                             ipinr < IPI_DOORBELL_END; ipinr++) {
 447                                if (ipimask & (0x1 << ipinr))
 448                                        handle_IPI(ipinr, regs);
 449                        }
 450                        continue;
 451                }
 452#endif
 453
 454        } while (1);
 455}
 456
 457static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 458                                             struct device_node *parent)
 459{
 460        struct resource main_int_res, per_cpu_int_res;
 461        int parent_irq;
 462        u32 control;
 463
 464        BUG_ON(of_address_to_resource(node, 0, &main_int_res));
 465        BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res));
 466
 467        BUG_ON(!request_mem_region(main_int_res.start,
 468                                   resource_size(&main_int_res),
 469                                   node->full_name));
 470        BUG_ON(!request_mem_region(per_cpu_int_res.start,
 471                                   resource_size(&per_cpu_int_res),
 472                                   node->full_name));
 473
 474        main_int_base = ioremap(main_int_res.start,
 475                                resource_size(&main_int_res));
 476        BUG_ON(!main_int_base);
 477
 478        per_cpu_int_base = ioremap(per_cpu_int_res.start,
 479                                   resource_size(&per_cpu_int_res));
 480        BUG_ON(!per_cpu_int_base);
 481
 482        control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
 483
 484        armada_370_xp_mpic_domain =
 485                irq_domain_add_linear(node, (control >> 2) & 0x3ff,
 486                                &armada_370_xp_mpic_irq_ops, NULL);
 487
 488        BUG_ON(!armada_370_xp_mpic_domain);
 489
 490#ifdef CONFIG_SMP
 491        armada_xp_mpic_smp_cpu_init();
 492#endif
 493
 494        armada_370_xp_msi_init(node, main_int_res.start);
 495
 496        parent_irq = irq_of_parse_and_map(node, 0);
 497        if (parent_irq <= 0) {
 498                irq_set_default_host(armada_370_xp_mpic_domain);
 499                set_handle_irq(armada_370_xp_handle_irq);
 500        } else {
 501                irq_set_chained_handler(parent_irq,
 502                                        armada_370_xp_mpic_handle_cascade_irq);
 503        }
 504
 505        return 0;
 506}
 507
 508IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init);
 509