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