root/arch/arm/mach-imx/gpc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. imx_gpc_set_arm_power_up_timing
  2. imx_gpc_set_arm_power_down_timing
  3. imx_gpc_set_arm_power_in_lpm
  4. imx_gpc_set_l2_mem_power_in_lpm
  5. imx_gpc_pre_suspend
  6. imx_gpc_post_resume
  7. imx_gpc_irq_set_wake
  8. imx_gpc_mask_all
  9. imx_gpc_restore_all
  10. imx_gpc_hwirq_unmask
  11. imx_gpc_hwirq_mask
  12. imx_gpc_irq_unmask
  13. imx_gpc_irq_mask
  14. imx_gpc_domain_translate
  15. imx_gpc_domain_alloc
  16. imx_gpc_init
  17. imx_gpc_check_dt

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright 2011-2013 Freescale Semiconductor, Inc.
   4  * Copyright 2011 Linaro Ltd.
   5  */
   6 
   7 #include <linux/io.h>
   8 #include <linux/irq.h>
   9 #include <linux/irqchip.h>
  10 #include <linux/of.h>
  11 #include <linux/of_address.h>
  12 #include <linux/of_irq.h>
  13 #include <linux/irqchip/arm-gic.h>
  14 #include "common.h"
  15 #include "hardware.h"
  16 
  17 #define GPC_CNTR                0x0
  18 #define GPC_IMR1                0x008
  19 #define GPC_PGC_CPU_PDN         0x2a0
  20 #define GPC_PGC_CPU_PUPSCR      0x2a4
  21 #define GPC_PGC_CPU_PDNSCR      0x2a8
  22 #define GPC_PGC_SW2ISO_SHIFT    0x8
  23 #define GPC_PGC_SW_SHIFT        0x0
  24 
  25 #define GPC_CNTR_L2_PGE_SHIFT   22
  26 
  27 #define IMR_NUM                 4
  28 #define GPC_MAX_IRQS            (IMR_NUM * 32)
  29 
  30 static void __iomem *gpc_base;
  31 static u32 gpc_wake_irqs[IMR_NUM];
  32 static u32 gpc_saved_imrs[IMR_NUM];
  33 
  34 void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw)
  35 {
  36         writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
  37                 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PUPSCR);
  38 }
  39 
  40 void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw)
  41 {
  42         writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
  43                 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PDNSCR);
  44 }
  45 
  46 void imx_gpc_set_arm_power_in_lpm(bool power_off)
  47 {
  48         writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN);
  49 }
  50 
  51 void imx_gpc_set_l2_mem_power_in_lpm(bool power_off)
  52 {
  53         u32 val;
  54 
  55         val = readl_relaxed(gpc_base + GPC_CNTR);
  56         val &= ~(1 << GPC_CNTR_L2_PGE_SHIFT);
  57         if (power_off)
  58                 val |= 1 << GPC_CNTR_L2_PGE_SHIFT;
  59         writel_relaxed(val, gpc_base + GPC_CNTR);
  60 }
  61 
  62 void imx_gpc_pre_suspend(bool arm_power_off)
  63 {
  64         void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
  65         int i;
  66 
  67         /* Tell GPC to power off ARM core when suspend */
  68         if (arm_power_off)
  69                 imx_gpc_set_arm_power_in_lpm(arm_power_off);
  70 
  71         for (i = 0; i < IMR_NUM; i++) {
  72                 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
  73                 writel_relaxed(~gpc_wake_irqs[i], reg_imr1 + i * 4);
  74         }
  75 }
  76 
  77 void imx_gpc_post_resume(void)
  78 {
  79         void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
  80         int i;
  81 
  82         /* Keep ARM core powered on for other low-power modes */
  83         imx_gpc_set_arm_power_in_lpm(false);
  84 
  85         for (i = 0; i < IMR_NUM; i++)
  86                 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
  87 }
  88 
  89 static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
  90 {
  91         unsigned int idx = d->hwirq / 32;
  92         u32 mask;
  93 
  94         mask = 1 << d->hwirq % 32;
  95         gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
  96                                   gpc_wake_irqs[idx] & ~mask;
  97 
  98         /*
  99          * Do *not* call into the parent, as the GIC doesn't have any
 100          * wake-up facility...
 101          */
 102         return 0;
 103 }
 104 
 105 void imx_gpc_mask_all(void)
 106 {
 107         void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
 108         int i;
 109 
 110         for (i = 0; i < IMR_NUM; i++) {
 111                 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
 112                 writel_relaxed(~0, reg_imr1 + i * 4);
 113         }
 114 
 115 }
 116 
 117 void imx_gpc_restore_all(void)
 118 {
 119         void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
 120         int i;
 121 
 122         for (i = 0; i < IMR_NUM; i++)
 123                 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
 124 }
 125 
 126 void imx_gpc_hwirq_unmask(unsigned int hwirq)
 127 {
 128         void __iomem *reg;
 129         u32 val;
 130 
 131         reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
 132         val = readl_relaxed(reg);
 133         val &= ~(1 << hwirq % 32);
 134         writel_relaxed(val, reg);
 135 }
 136 
 137 void imx_gpc_hwirq_mask(unsigned int hwirq)
 138 {
 139         void __iomem *reg;
 140         u32 val;
 141 
 142         reg = gpc_base + GPC_IMR1 + hwirq / 32 * 4;
 143         val = readl_relaxed(reg);
 144         val |= 1 << (hwirq % 32);
 145         writel_relaxed(val, reg);
 146 }
 147 
 148 static void imx_gpc_irq_unmask(struct irq_data *d)
 149 {
 150         imx_gpc_hwirq_unmask(d->hwirq);
 151         irq_chip_unmask_parent(d);
 152 }
 153 
 154 static void imx_gpc_irq_mask(struct irq_data *d)
 155 {
 156         imx_gpc_hwirq_mask(d->hwirq);
 157         irq_chip_mask_parent(d);
 158 }
 159 
 160 static struct irq_chip imx_gpc_chip = {
 161         .name                   = "GPC",
 162         .irq_eoi                = irq_chip_eoi_parent,
 163         .irq_mask               = imx_gpc_irq_mask,
 164         .irq_unmask             = imx_gpc_irq_unmask,
 165         .irq_retrigger          = irq_chip_retrigger_hierarchy,
 166         .irq_set_wake           = imx_gpc_irq_set_wake,
 167         .irq_set_type           = irq_chip_set_type_parent,
 168 #ifdef CONFIG_SMP
 169         .irq_set_affinity       = irq_chip_set_affinity_parent,
 170 #endif
 171 };
 172 
 173 static int imx_gpc_domain_translate(struct irq_domain *d,
 174                                     struct irq_fwspec *fwspec,
 175                                     unsigned long *hwirq,
 176                                     unsigned int *type)
 177 {
 178         if (is_of_node(fwspec->fwnode)) {
 179                 if (fwspec->param_count != 3)
 180                         return -EINVAL;
 181 
 182                 /* No PPI should point to this domain */
 183                 if (fwspec->param[0] != 0)
 184                         return -EINVAL;
 185 
 186                 *hwirq = fwspec->param[1];
 187                 *type = fwspec->param[2];
 188                 return 0;
 189         }
 190 
 191         return -EINVAL;
 192 }
 193 
 194 static int imx_gpc_domain_alloc(struct irq_domain *domain,
 195                                   unsigned int irq,
 196                                   unsigned int nr_irqs, void *data)
 197 {
 198         struct irq_fwspec *fwspec = data;
 199         struct irq_fwspec parent_fwspec;
 200         irq_hw_number_t hwirq;
 201         int i;
 202 
 203         if (fwspec->param_count != 3)
 204                 return -EINVAL; /* Not GIC compliant */
 205         if (fwspec->param[0] != 0)
 206                 return -EINVAL; /* No PPI should point to this domain */
 207 
 208         hwirq = fwspec->param[1];
 209         if (hwirq >= GPC_MAX_IRQS)
 210                 return -EINVAL; /* Can't deal with this */
 211 
 212         for (i = 0; i < nr_irqs; i++)
 213                 irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
 214                                               &imx_gpc_chip, NULL);
 215 
 216         parent_fwspec = *fwspec;
 217         parent_fwspec.fwnode = domain->parent->fwnode;
 218         return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
 219                                             &parent_fwspec);
 220 }
 221 
 222 static const struct irq_domain_ops imx_gpc_domain_ops = {
 223         .translate      = imx_gpc_domain_translate,
 224         .alloc          = imx_gpc_domain_alloc,
 225         .free           = irq_domain_free_irqs_common,
 226 };
 227 
 228 static int __init imx_gpc_init(struct device_node *node,
 229                                struct device_node *parent)
 230 {
 231         struct irq_domain *parent_domain, *domain;
 232         int i;
 233 
 234         if (!parent) {
 235                 pr_err("%pOF: no parent, giving up\n", node);
 236                 return -ENODEV;
 237         }
 238 
 239         parent_domain = irq_find_host(parent);
 240         if (!parent_domain) {
 241                 pr_err("%pOF: unable to obtain parent domain\n", node);
 242                 return -ENXIO;
 243         }
 244 
 245         gpc_base = of_iomap(node, 0);
 246         if (WARN_ON(!gpc_base))
 247                 return -ENOMEM;
 248 
 249         domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
 250                                           node, &imx_gpc_domain_ops,
 251                                           NULL);
 252         if (!domain) {
 253                 iounmap(gpc_base);
 254                 return -ENOMEM;
 255         }
 256 
 257         /* Initially mask all interrupts */
 258         for (i = 0; i < IMR_NUM; i++)
 259                 writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4);
 260 
 261         /*
 262          * Clear the OF_POPULATED flag set in of_irq_init so that
 263          * later the GPC power domain driver will not be skipped.
 264          */
 265         of_node_clear_flag(node, OF_POPULATED);
 266 
 267         return 0;
 268 }
 269 IRQCHIP_DECLARE(imx_gpc, "fsl,imx6q-gpc", imx_gpc_init);
 270 
 271 void __init imx_gpc_check_dt(void)
 272 {
 273         struct device_node *np;
 274 
 275         np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
 276         if (WARN_ON(!np))
 277                 return;
 278 
 279         if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL))) {
 280                 pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
 281 
 282                 /* map GPC, so that at least CPUidle and WARs keep working */
 283                 gpc_base = of_iomap(np, 0);
 284         }
 285 }

/* [<][>][^][v][top][bottom][index][help] */