linux/drivers/irqchip/irq-imx-gpcv2.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Freescale Semiconductor, Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/of_address.h>
  10#include <linux/of_irq.h>
  11#include <linux/slab.h>
  12#include <linux/irqchip.h>
  13#include <linux/syscore_ops.h>
  14
  15#define IMR_NUM                 4
  16#define GPC_MAX_IRQS            (IMR_NUM * 32)
  17
  18#define GPC_IMR1_CORE0          0x30
  19#define GPC_IMR1_CORE1          0x40
  20
  21struct gpcv2_irqchip_data {
  22        struct raw_spinlock     rlock;
  23        void __iomem            *gpc_base;
  24        u32                     wakeup_sources[IMR_NUM];
  25        u32                     saved_irq_mask[IMR_NUM];
  26        u32                     cpu2wakeup;
  27};
  28
  29static struct gpcv2_irqchip_data *imx_gpcv2_instance;
  30
  31/*
  32 * Interface for the low level wakeup code.
  33 */
  34u32 imx_gpcv2_get_wakeup_source(u32 **sources)
  35{
  36        if (!imx_gpcv2_instance)
  37                return 0;
  38
  39        if (sources)
  40                *sources = imx_gpcv2_instance->wakeup_sources;
  41
  42        return IMR_NUM;
  43}
  44
  45static int gpcv2_wakeup_source_save(void)
  46{
  47        struct gpcv2_irqchip_data *cd;
  48        void __iomem *reg;
  49        int i;
  50
  51        cd = imx_gpcv2_instance;
  52        if (!cd)
  53                return 0;
  54
  55        for (i = 0; i < IMR_NUM; i++) {
  56                reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
  57                cd->saved_irq_mask[i] = readl_relaxed(reg);
  58                writel_relaxed(cd->wakeup_sources[i], reg);
  59        }
  60
  61        return 0;
  62}
  63
  64static void gpcv2_wakeup_source_restore(void)
  65{
  66        struct gpcv2_irqchip_data *cd;
  67        void __iomem *reg;
  68        int i;
  69
  70        cd = imx_gpcv2_instance;
  71        if (!cd)
  72                return;
  73
  74        for (i = 0; i < IMR_NUM; i++) {
  75                reg = cd->gpc_base + cd->cpu2wakeup + i * 4;
  76                writel_relaxed(cd->saved_irq_mask[i], reg);
  77        }
  78}
  79
  80static struct syscore_ops imx_gpcv2_syscore_ops = {
  81        .suspend        = gpcv2_wakeup_source_save,
  82        .resume         = gpcv2_wakeup_source_restore,
  83};
  84
  85static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
  86{
  87        struct gpcv2_irqchip_data *cd = d->chip_data;
  88        unsigned int idx = d->hwirq / 32;
  89        unsigned long flags;
  90        void __iomem *reg;
  91        u32 mask, val;
  92
  93        raw_spin_lock_irqsave(&cd->rlock, flags);
  94        reg = cd->gpc_base + cd->cpu2wakeup + idx * 4;
  95        mask = 1 << d->hwirq % 32;
  96        val = cd->wakeup_sources[idx];
  97
  98        cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask);
  99        raw_spin_unlock_irqrestore(&cd->rlock, flags);
 100
 101        /*
 102         * Do *not* call into the parent, as the GIC doesn't have any
 103         * wake-up facility...
 104         */
 105
 106        return 0;
 107}
 108
 109static void imx_gpcv2_irq_unmask(struct irq_data *d)
 110{
 111        struct gpcv2_irqchip_data *cd = d->chip_data;
 112        void __iomem *reg;
 113        u32 val;
 114
 115        raw_spin_lock(&cd->rlock);
 116        reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
 117        val = readl_relaxed(reg);
 118        val &= ~(1 << d->hwirq % 32);
 119        writel_relaxed(val, reg);
 120        raw_spin_unlock(&cd->rlock);
 121
 122        irq_chip_unmask_parent(d);
 123}
 124
 125static void imx_gpcv2_irq_mask(struct irq_data *d)
 126{
 127        struct gpcv2_irqchip_data *cd = d->chip_data;
 128        void __iomem *reg;
 129        u32 val;
 130
 131        raw_spin_lock(&cd->rlock);
 132        reg = cd->gpc_base + cd->cpu2wakeup + d->hwirq / 32 * 4;
 133        val = readl_relaxed(reg);
 134        val |= 1 << (d->hwirq % 32);
 135        writel_relaxed(val, reg);
 136        raw_spin_unlock(&cd->rlock);
 137
 138        irq_chip_mask_parent(d);
 139}
 140
 141static struct irq_chip gpcv2_irqchip_data_chip = {
 142        .name                   = "GPCv2",
 143        .irq_eoi                = irq_chip_eoi_parent,
 144        .irq_mask               = imx_gpcv2_irq_mask,
 145        .irq_unmask             = imx_gpcv2_irq_unmask,
 146        .irq_set_wake           = imx_gpcv2_irq_set_wake,
 147        .irq_retrigger          = irq_chip_retrigger_hierarchy,
 148#ifdef CONFIG_SMP
 149        .irq_set_affinity       = irq_chip_set_affinity_parent,
 150#endif
 151};
 152
 153static int imx_gpcv2_domain_translate(struct irq_domain *d,
 154                                      struct irq_fwspec *fwspec,
 155                                      unsigned long *hwirq,
 156                                      unsigned int *type)
 157{
 158        if (is_of_node(fwspec->fwnode)) {
 159                if (fwspec->param_count != 3)
 160                        return -EINVAL;
 161
 162                /* No PPI should point to this domain */
 163                if (fwspec->param[0] != 0)
 164                        return -EINVAL;
 165
 166                *hwirq = fwspec->param[1];
 167                *type = fwspec->param[2];
 168                return 0;
 169        }
 170
 171        return -EINVAL;
 172}
 173
 174static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
 175                                  unsigned int irq, unsigned int nr_irqs,
 176                                  void *data)
 177{
 178        struct irq_fwspec *fwspec = data;
 179        struct irq_fwspec parent_fwspec;
 180        irq_hw_number_t hwirq;
 181        unsigned int type;
 182        int err;
 183        int i;
 184
 185        err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
 186        if (err)
 187                return err;
 188
 189        if (hwirq >= GPC_MAX_IRQS)
 190                return -EINVAL;
 191
 192        for (i = 0; i < nr_irqs; i++) {
 193                irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
 194                                &gpcv2_irqchip_data_chip, domain->host_data);
 195        }
 196
 197        parent_fwspec = *fwspec;
 198        parent_fwspec.fwnode = domain->parent->fwnode;
 199        return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
 200                                            &parent_fwspec);
 201}
 202
 203static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
 204        .translate      = imx_gpcv2_domain_translate,
 205        .alloc          = imx_gpcv2_domain_alloc,
 206        .free           = irq_domain_free_irqs_common,
 207};
 208
 209static int __init imx_gpcv2_irqchip_init(struct device_node *node,
 210                               struct device_node *parent)
 211{
 212        struct irq_domain *parent_domain, *domain;
 213        struct gpcv2_irqchip_data *cd;
 214        int i;
 215
 216        if (!parent) {
 217                pr_err("%s: no parent, giving up\n", node->full_name);
 218                return -ENODEV;
 219        }
 220
 221        parent_domain = irq_find_host(parent);
 222        if (!parent_domain) {
 223                pr_err("%s: unable to get parent domain\n", node->full_name);
 224                return -ENXIO;
 225        }
 226
 227        cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL);
 228        if (!cd) {
 229                pr_err("kzalloc failed!\n");
 230                return -ENOMEM;
 231        }
 232
 233        cd->gpc_base = of_iomap(node, 0);
 234        if (!cd->gpc_base) {
 235                pr_err("fsl-gpcv2: unable to map gpc registers\n");
 236                kfree(cd);
 237                return -ENOMEM;
 238        }
 239
 240        domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
 241                                node, &gpcv2_irqchip_data_domain_ops, cd);
 242        if (!domain) {
 243                iounmap(cd->gpc_base);
 244                kfree(cd);
 245                return -ENOMEM;
 246        }
 247        irq_set_default_host(domain);
 248
 249        /* Initially mask all interrupts */
 250        for (i = 0; i < IMR_NUM; i++) {
 251                writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE0 + i * 4);
 252                writel_relaxed(~0, cd->gpc_base + GPC_IMR1_CORE1 + i * 4);
 253                cd->wakeup_sources[i] = ~0;
 254        }
 255
 256        /* Let CORE0 as the default CPU to wake up by GPC */
 257        cd->cpu2wakeup = GPC_IMR1_CORE0;
 258
 259        /*
 260         * Due to hardware design failure, need to make sure GPR
 261         * interrupt(#32) is unmasked during RUN mode to avoid entering
 262         * DSM by mistake.
 263         */
 264        writel_relaxed(~0x1, cd->gpc_base + cd->cpu2wakeup);
 265
 266        imx_gpcv2_instance = cd;
 267        register_syscore_ops(&imx_gpcv2_syscore_ops);
 268
 269        return 0;
 270}
 271
 272IRQCHIP_DECLARE(imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init);
 273