FILE
irq-tegra.c
File preview with syntax highlighting.
linux-6.19.14/drivers/irqchip/irq-tegra.c
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 *data) 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(GENMASK(31, 0), ictlr + ICTLR_COP_IER_CLR); 152 153 /* Disable CPU interrupts */ 154 writel_relaxed(GENMASK(31, 0), 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 *data) 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(GENMASK(31, 0), 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(GENMASK(31, 0), 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 const struct syscore_ops tegra_ictlr_syscore_ops = { 188 .suspend = tegra_ictlr_suspend, 189 .resume = tegra_ictlr_resume, 190}; 191 192static struct syscore tegra_ictlr_syscore = { 193 .ops = &tegra_ictlr_syscore_ops, 194}; 195 196static void tegra_ictlr_syscore_init(void) 197{ 198 register_syscore(&tegra_ictlr_syscore); 199} 200#else 201#define tegra_set_wake NULL 202static inline void tegra_ictlr_syscore_init(void) {} 203#endif 204 205static struct irq_chip tegra_ictlr_chip = { 206 .name = "LIC", 207 .irq_eoi = tegra_eoi, 208 .irq_mask = tegra_mask, 209 .irq_unmask = tegra_unmask, 210 .irq_retrigger = tegra_retrigger, 211 .irq_set_wake = tegra_set_wake, 212 .irq_set_type = irq_chip_set_type_parent, 213 .flags = IRQCHIP_MASK_ON_SUSPEND, 214#ifdef CONFIG_SMP 215 .irq_set_affinity = irq_chip_set_affinity_parent, 216#endif 217}; 218 219static int tegra_ictlr_domain_translate(struct irq_domain *d, 220 struct irq_fwspec *fwspec, 221 unsigned long *hwirq, 222 unsigned int *type) 223{ 224 if (is_of_node(fwspec->fwnode)) { 225 if (fwspec->param_count != 3) 226 return -EINVAL; 227 228 /* No PPI should point to this domain */ 229 if (fwspec->param[0] != 0) 230 return -EINVAL; 231 232 *hwirq = fwspec->param[1]; 233 *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 234 return 0; 235 } 236 237 return -EINVAL; 238} 239 240static int tegra_ictlr_domain_alloc(struct irq_domain *domain, 241 unsigned int virq, 242 unsigned int nr_irqs, void *data) 243{ 244 struct irq_fwspec *fwspec = data; 245 struct irq_fwspec parent_fwspec; 246 struct tegra_ictlr_info *info = domain->host_data; 247 irq_hw_number_t hwirq; 248 unsigned int i; 249 250 if (fwspec->param_count != 3) 251 return -EINVAL; /* Not GIC compliant */ 252 if (fwspec->param[0] != GIC_SPI) 253 return -EINVAL; /* No PPI should point to this domain */ 254 255 hwirq = fwspec->param[1]; 256 if (hwirq >= (num_ictlrs * 32)) 257 return -EINVAL; 258 259 for (i = 0; i < nr_irqs; i++) { 260 int ictlr = (hwirq + i) / 32; 261 262 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 263 &tegra_ictlr_chip, 264 (void __force *)info->base[ictlr]); 265 } 266 267 parent_fwspec = *fwspec; 268 parent_fwspec.fwnode = domain->parent->fwnode; 269 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, 270 &parent_fwspec); 271} 272 273static const struct irq_domain_ops tegra_ictlr_domain_ops = { 274 .translate = tegra_ictlr_domain_translate, 275 .alloc = tegra_ictlr_domain_alloc, 276 .free = irq_domain_free_irqs_common, 277}; 278 279static int __init tegra_ictlr_init(struct device_node *node, 280 struct device_node *parent) 281{ 282 struct irq_domain *parent_domain, *domain; 283 const struct of_device_id *match; 284 const struct tegra_ictlr_soc *soc; 285 unsigned int i; 286 int err; 287 288 if (!parent) { 289 pr_err("%pOF: no parent, giving up\n", node); 290 return -ENODEV; 291 } 292 293 parent_domain = irq_find_host(parent); 294 if (!parent_domain) { 295 pr_err("%pOF: unable to obtain parent domain\n", node); 296 return -ENXIO; 297 } 298 299 match = of_match_node(ictlr_matches, node); 300 if (!match) /* Should never happen... */ 301 return -ENODEV; 302 303 soc = match->data; 304 305 lic = kzalloc(sizeof(*lic), GFP_KERNEL); 306 if (!lic) 307 return -ENOMEM; 308 309 for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) { 310 void __iomem *base; 311 312 base = of_iomap(node, i); 313 if (!base) 314 break; 315 316 lic->base[i] = base; 317 318 /* Disable all interrupts */ 319 writel_relaxed(GENMASK(31, 0), base + ICTLR_CPU_IER_CLR); 320 /* All interrupts target IRQ */ 321 writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS); 322 323 num_ictlrs++; 324 } 325 326 if (!num_ictlrs) { 327 pr_err("%pOF: no valid regions, giving up\n", node); 328 err = -ENOMEM; 329 goto out_free; 330 } 331 332 WARN(num_ictlrs != soc->num_ictlrs, 333 "%pOF: Found %u interrupt controllers in DT; expected %u.\n", 334 node, num_ictlrs, soc->num_ictlrs); 335 336 337 domain = irq_domain_create_hierarchy(parent_domain, 0, num_ictlrs * 32, 338 of_fwnode_handle(node), &tegra_ictlr_domain_ops, lic); 339 if (!domain) { 340 pr_err("%pOF: failed to allocated domain\n", node); 341 err = -ENOMEM; 342 goto out_unmap; 343 } 344 345 tegra_ictlr_syscore_init(); 346 347 pr_info("%pOF: %d interrupts forwarded to %pOF\n", 348 node, num_ictlrs * 32, parent); 349 350 return 0; 351 352out_unmap: 353 for (i = 0; i < num_ictlrs; i++) 354 iounmap(lic->base[i]); 355out_free: 356 kfree(lic); 357 return err; 358} 359 360IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init); 361IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init); 362IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init);
~/
Read-only file preview.