linux/drivers/irqchip/irq-tegra.c
<<
>>
Prefs
   1/*
   2 * Driver code for Tegra's Legacy Interrupt Controller
   3 *
   4 * Author: Marc Zyngier <marc.zyngier@arm.com>
   5 *
   6 * Heavily based on the original arch/arm/mach-tegra/irq.c code:
   7 * Copyright (C) 2011 Google, Inc.
   8 *
   9 * Author:
  10 *      Colin Cross <ccross@android.com>
  11 *
  12 * Copyright (C) 2010,2013, NVIDIA Corporation
  13 *
  14 * This software is licensed under the terms of the GNU General Public
  15 * License version 2, as published by the Free Software Foundation, and
  16 * may be copied, distributed, and modified under those terms.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 */
  24
  25#include <linux/io.h>
  26#include <linux/irq.h>
  27#include <linux/irqchip.h>
  28#include <linux/irqdomain.h>
  29#include <linux/of_address.h>
  30#include <linux/slab.h>
  31#include <linux/syscore_ops.h>
  32
  33#include <dt-bindings/interrupt-controller/arm-gic.h>
  34
  35#define ICTLR_CPU_IEP_VFIQ      0x08
  36#define ICTLR_CPU_IEP_FIR       0x14
  37#define ICTLR_CPU_IEP_FIR_SET   0x18
  38#define ICTLR_CPU_IEP_FIR_CLR   0x1c
  39
  40#define ICTLR_CPU_IER           0x20
  41#define ICTLR_CPU_IER_SET       0x24
  42#define ICTLR_CPU_IER_CLR       0x28
  43#define ICTLR_CPU_IEP_CLASS     0x2C
  44
  45#define ICTLR_COP_IER           0x30
  46#define ICTLR_COP_IER_SET       0x34
  47#define ICTLR_COP_IER_CLR       0x38
  48#define ICTLR_COP_IEP_CLASS     0x3c
  49
  50#define TEGRA_MAX_NUM_ICTLRS    6
  51
  52static unsigned int num_ictlrs;
  53
  54struct tegra_ictlr_soc {
  55        unsigned int num_ictlrs;
  56};
  57
  58static const struct tegra_ictlr_soc tegra20_ictlr_soc = {
  59        .num_ictlrs = 4,
  60};
  61
  62static const struct tegra_ictlr_soc tegra30_ictlr_soc = {
  63        .num_ictlrs = 5,
  64};
  65
  66static const struct tegra_ictlr_soc tegra210_ictlr_soc = {
  67        .num_ictlrs = 6,
  68};
  69
  70static const struct of_device_id ictlr_matches[] = {
  71        { .compatible = "nvidia,tegra210-ictlr", .data = &tegra210_ictlr_soc },
  72        { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc },
  73        { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc },
  74        { }
  75};
  76
  77struct tegra_ictlr_info {
  78        void __iomem *base[TEGRA_MAX_NUM_ICTLRS];
  79#ifdef CONFIG_PM_SLEEP
  80        u32 cop_ier[TEGRA_MAX_NUM_ICTLRS];
  81        u32 cop_iep[TEGRA_MAX_NUM_ICTLRS];
  82        u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];
  83        u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS];
  84
  85        u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS];
  86#endif
  87};
  88
  89static struct tegra_ictlr_info *lic;
  90
  91static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg)
  92{
  93        void __iomem *base = (void __iomem __force *)d->chip_data;
  94        u32 mask;
  95
  96        mask = BIT(d->hwirq % 32);
  97        writel_relaxed(mask, base + reg);
  98}
  99
 100static void tegra_mask(struct irq_data *d)
 101{
 102        tegra_ictlr_write_mask(d, ICTLR_CPU_IER_CLR);
 103        irq_chip_mask_parent(d);
 104}
 105
 106static void tegra_unmask(struct irq_data *d)
 107{
 108        tegra_ictlr_write_mask(d, ICTLR_CPU_IER_SET);
 109        irq_chip_unmask_parent(d);
 110}
 111
 112static void tegra_eoi(struct irq_data *d)
 113{
 114        tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_CLR);
 115        irq_chip_eoi_parent(d);
 116}
 117
 118static int tegra_retrigger(struct irq_data *d)
 119{
 120        tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_SET);
 121        return irq_chip_retrigger_hierarchy(d);
 122}
 123
 124#ifdef CONFIG_PM_SLEEP
 125static int tegra_set_wake(struct irq_data *d, unsigned int enable)
 126{
 127        u32 irq = d->hwirq;
 128        u32 index, mask;
 129
 130        index = (irq / 32);
 131        mask = BIT(irq % 32);
 132        if (enable)
 133                lic->ictlr_wake_mask[index] |= mask;
 134        else
 135                lic->ictlr_wake_mask[index] &= ~mask;
 136
 137        /*
 138         * Do *not* call into the parent, as the GIC doesn't have any
 139         * wake-up facility...
 140         */
 141        return 0;
 142}
 143
 144static int tegra_ictlr_suspend(void)
 145{
 146        unsigned long flags;
 147        unsigned int i;
 148
 149        local_irq_save(flags);
 150        for (i = 0; i < num_ictlrs; i++) {
 151                void __iomem *ictlr = lic->base[i];
 152
 153                /* Save interrupt state */
 154                lic->cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER);
 155                lic->cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS);
 156                lic->cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER);
 157                lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
 158
 159                /* Disable COP interrupts */
 160                writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
 161
 162                /* Disable CPU interrupts */
 163                writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
 164
 165                /* Enable the wakeup sources of ictlr */
 166                writel_relaxed(lic->ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET);
 167        }
 168        local_irq_restore(flags);
 169
 170        return 0;
 171}
 172
 173static void tegra_ictlr_resume(void)
 174{
 175        unsigned long flags;
 176        unsigned int i;
 177
 178        local_irq_save(flags);
 179        for (i = 0; i < num_ictlrs; i++) {
 180                void __iomem *ictlr = lic->base[i];
 181
 182                writel_relaxed(lic->cpu_iep[i],
 183                               ictlr + ICTLR_CPU_IEP_CLASS);
 184                writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
 185                writel_relaxed(lic->cpu_ier[i],
 186                               ictlr + ICTLR_CPU_IER_SET);
 187                writel_relaxed(lic->cop_iep[i],
 188                               ictlr + ICTLR_COP_IEP_CLASS);
 189                writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
 190                writel_relaxed(lic->cop_ier[i],
 191                               ictlr + ICTLR_COP_IER_SET);
 192        }
 193        local_irq_restore(flags);
 194}
 195
 196static struct syscore_ops tegra_ictlr_syscore_ops = {
 197        .suspend        = tegra_ictlr_suspend,
 198        .resume         = tegra_ictlr_resume,
 199};
 200
 201static void tegra_ictlr_syscore_init(void)
 202{
 203        register_syscore_ops(&tegra_ictlr_syscore_ops);
 204}
 205#else
 206#define tegra_set_wake  NULL
 207static inline void tegra_ictlr_syscore_init(void) {}
 208#endif
 209
 210static struct irq_chip tegra_ictlr_chip = {
 211        .name                   = "LIC",
 212        .irq_eoi                = tegra_eoi,
 213        .irq_mask               = tegra_mask,
 214        .irq_unmask             = tegra_unmask,
 215        .irq_retrigger          = tegra_retrigger,
 216        .irq_set_wake           = tegra_set_wake,
 217        .irq_set_type           = irq_chip_set_type_parent,
 218        .flags                  = IRQCHIP_MASK_ON_SUSPEND,
 219#ifdef CONFIG_SMP
 220        .irq_set_affinity       = irq_chip_set_affinity_parent,
 221#endif
 222};
 223
 224static int tegra_ictlr_domain_translate(struct irq_domain *d,
 225                                        struct irq_fwspec *fwspec,
 226                                        unsigned long *hwirq,
 227                                        unsigned int *type)
 228{
 229        if (is_of_node(fwspec->fwnode)) {
 230                if (fwspec->param_count != 3)
 231                        return -EINVAL;
 232
 233                /* No PPI should point to this domain */
 234                if (fwspec->param[0] != 0)
 235                        return -EINVAL;
 236
 237                *hwirq = fwspec->param[1];
 238                *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
 239                return 0;
 240        }
 241
 242        return -EINVAL;
 243}
 244
 245static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
 246                                    unsigned int virq,
 247                                    unsigned int nr_irqs, void *data)
 248{
 249        struct irq_fwspec *fwspec = data;
 250        struct irq_fwspec parent_fwspec;
 251        struct tegra_ictlr_info *info = domain->host_data;
 252        irq_hw_number_t hwirq;
 253        unsigned int i;
 254
 255        if (fwspec->param_count != 3)
 256                return -EINVAL; /* Not GIC compliant */
 257        if (fwspec->param[0] != GIC_SPI)
 258                return -EINVAL; /* No PPI should point to this domain */
 259
 260        hwirq = fwspec->param[1];
 261        if (hwirq >= (num_ictlrs * 32))
 262                return -EINVAL;
 263
 264        for (i = 0; i < nr_irqs; i++) {
 265                int ictlr = (hwirq + i) / 32;
 266
 267                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
 268                                              &tegra_ictlr_chip,
 269                                              (void __force *)info->base[ictlr]);
 270        }
 271
 272        parent_fwspec = *fwspec;
 273        parent_fwspec.fwnode = domain->parent->fwnode;
 274        return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
 275                                            &parent_fwspec);
 276}
 277
 278static const struct irq_domain_ops tegra_ictlr_domain_ops = {
 279        .translate      = tegra_ictlr_domain_translate,
 280        .alloc          = tegra_ictlr_domain_alloc,
 281        .free           = irq_domain_free_irqs_common,
 282};
 283
 284static int __init tegra_ictlr_init(struct device_node *node,
 285                                   struct device_node *parent)
 286{
 287        struct irq_domain *parent_domain, *domain;
 288        const struct of_device_id *match;
 289        const struct tegra_ictlr_soc *soc;
 290        unsigned int i;
 291        int err;
 292
 293        if (!parent) {
 294                pr_err("%pOF: no parent, giving up\n", node);
 295                return -ENODEV;
 296        }
 297
 298        parent_domain = irq_find_host(parent);
 299        if (!parent_domain) {
 300                pr_err("%pOF: unable to obtain parent domain\n", node);
 301                return -ENXIO;
 302        }
 303
 304        match = of_match_node(ictlr_matches, node);
 305        if (!match)             /* Should never happen... */
 306                return -ENODEV;
 307
 308        soc = match->data;
 309
 310        lic = kzalloc(sizeof(*lic), GFP_KERNEL);
 311        if (!lic)
 312                return -ENOMEM;
 313
 314        for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) {
 315                void __iomem *base;
 316
 317                base = of_iomap(node, i);
 318                if (!base)
 319                        break;
 320
 321                lic->base[i] = base;
 322
 323                /* Disable all interrupts */
 324                writel_relaxed(~0UL, base + ICTLR_CPU_IER_CLR);
 325                /* All interrupts target IRQ */
 326                writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS);
 327
 328                num_ictlrs++;
 329        }
 330
 331        if (!num_ictlrs) {
 332                pr_err("%pOF: no valid regions, giving up\n", node);
 333                err = -ENOMEM;
 334                goto out_free;
 335        }
 336
 337        WARN(num_ictlrs != soc->num_ictlrs,
 338             "%pOF: Found %u interrupt controllers in DT; expected %u.\n",
 339             node, num_ictlrs, soc->num_ictlrs);
 340
 341
 342        domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32,
 343                                          node, &tegra_ictlr_domain_ops,
 344                                          lic);
 345        if (!domain) {
 346                pr_err("%pOF: failed to allocated domain\n", node);
 347                err = -ENOMEM;
 348                goto out_unmap;
 349        }
 350
 351        tegra_ictlr_syscore_init();
 352
 353        pr_info("%pOF: %d interrupts forwarded to %pOF\n",
 354                node, num_ictlrs * 32, parent);
 355
 356        return 0;
 357
 358out_unmap:
 359        for (i = 0; i < num_ictlrs; i++)
 360                iounmap(lic->base[i]);
 361out_free:
 362        kfree(lic);
 363        return err;
 364}
 365
 366IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init);
 367IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init);
 368IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init);
 369