Blame view
drivers/irqchip/irq-imx-gpcv2.c
9.99 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
e324c4dc4 irqchip/imx-gpcv2... |
2 3 |
/* * Copyright (C) 2015 Freescale Semiconductor, Inc. |
e324c4dc4 irqchip/imx-gpcv2... |
4 |
*/ |
439b9fed9 irqchip: irq-imx-... |
5 6 7 |
#include <linux/arm-smccc.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/mfd/syscon.h> |
e324c4dc4 irqchip/imx-gpcv2... |
8 9 |
#include <linux/of_address.h> #include <linux/of_irq.h> |
439b9fed9 irqchip: irq-imx-... |
10 |
#include <linux/regmap.h> |
e324c4dc4 irqchip/imx-gpcv2... |
11 12 13 |
#include <linux/slab.h> #include <linux/irqchip.h> #include <linux/syscore_ops.h> |
439b9fed9 irqchip: irq-imx-... |
14 |
#include <linux/smp.h> |
ca421e39f irqchip: irq-imx-... |
15 |
#include <linux/cpuidle.h> |
439b9fed9 irqchip: irq-imx-... |
16 |
|
7bbbcfd92 MLK-21399 irqchip... |
17 18 19 20 21 22 23 |
#define FSL_SIP_GPC 0xC2000000 #define FSL_SIP_CONFIG_GPC_MASK 0x00 #define FSL_SIP_CONFIG_GPC_UNMASK 0x01 #define FSL_SIP_CONFIG_GPC_SET_WAKE 0x02 #define FSL_SIP_CONFIG_GPC_PM_DOMAIN 0x03 #define FSL_SIP_CONFIG_GPC_SET_AFF 0x04 #define FSL_SIP_CONFIG_GPC_CORE_WAKE 0x05 |
e324c4dc4 irqchip/imx-gpcv2... |
24 25 26 27 28 29 |
#define IMR_NUM 4 #define GPC_MAX_IRQS (IMR_NUM * 32) #define GPC_IMR1_CORE0 0x30 #define GPC_IMR1_CORE1 0x40 |
ed01edc0a irqchip/irq-imx-g... |
30 31 |
#define GPC_IMR1_CORE2 0x1c0 #define GPC_IMR1_CORE3 0x1d0 |
7bbbcfd92 MLK-21399 irqchip... |
32 |
static unsigned int err11171; |
e324c4dc4 irqchip/imx-gpcv2... |
33 34 35 36 37 38 39 40 41 42 |
struct gpcv2_irqchip_data { struct raw_spinlock rlock; void __iomem *gpc_base; u32 wakeup_sources[IMR_NUM]; u32 saved_irq_mask[IMR_NUM]; u32 cpu2wakeup; }; static struct gpcv2_irqchip_data *imx_gpcv2_instance; |
bd654fb67 irqchip/irq-imx-g... |
43 44 45 46 |
static void __iomem *gpcv2_idx_to_reg(struct gpcv2_irqchip_data *cd, int i) { return cd->gpc_base + cd->cpu2wakeup + i * 4; } |
e324c4dc4 irqchip/imx-gpcv2... |
47 48 49 50 51 52 53 54 55 56 57 |
static int gpcv2_wakeup_source_save(void) { struct gpcv2_irqchip_data *cd; void __iomem *reg; int i; cd = imx_gpcv2_instance; if (!cd) return 0; for (i = 0; i < IMR_NUM; i++) { |
bd654fb67 irqchip/irq-imx-g... |
58 |
reg = gpcv2_idx_to_reg(cd, i); |
e324c4dc4 irqchip/imx-gpcv2... |
59 60 61 62 63 64 65 66 67 68 |
cd->saved_irq_mask[i] = readl_relaxed(reg); writel_relaxed(cd->wakeup_sources[i], reg); } return 0; } static void gpcv2_wakeup_source_restore(void) { struct gpcv2_irqchip_data *cd; |
e324c4dc4 irqchip/imx-gpcv2... |
69 70 71 72 73 |
int i; cd = imx_gpcv2_instance; if (!cd) return; |
bd654fb67 irqchip/irq-imx-g... |
74 75 |
for (i = 0; i < IMR_NUM; i++) writel_relaxed(cd->saved_irq_mask[i], gpcv2_idx_to_reg(cd, i)); |
e324c4dc4 irqchip/imx-gpcv2... |
76 77 78 79 80 81 |
} static struct syscore_ops imx_gpcv2_syscore_ops = { .suspend = gpcv2_wakeup_source_save, .resume = gpcv2_wakeup_source_restore, }; |
454af0e61 LF-125 irqchip: i... |
82 |
#ifdef CONFIG_SMP |
9edb5ab27 LF-2438 drivers: ... |
83 |
void imx_gpcv2_raise_softirq(const struct cpumask *mask, |
439b9fed9 irqchip: irq-imx-... |
84 85 86 |
unsigned int irq) { struct arm_smccc_res res; |
9edb5ab27 LF-2438 drivers: ... |
87 88 |
if (!err11171) return; |
439b9fed9 irqchip: irq-imx-... |
89 90 91 92 93 |
/* now call into EL3 and take care of the wakeup */ arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_CORE_WAKE, *cpumask_bits(mask), 0, 0, 0, 0, 0, &res); } |
454af0e61 LF-125 irqchip: i... |
94 |
#endif |
439b9fed9 irqchip: irq-imx-... |
95 96 97 98 |
static void imx_gpcv2_wake_request_fixup(void) { struct regmap *iomux_gpr; |
ca421e39f irqchip: irq-imx-... |
99 100 101 102 103 104 105 106 |
struct arm_smccc_res res; arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_CORE_WAKE, 0, 0, 0, 0, 0, 0, &res); if (res.a0) { pr_warn("irq-imx-gpcv2: EL3 does not support FSL_SIP_CONFIG_GPC_CORE_WAKE, disabling cpuidle. "); |
9edb5ab27 LF-2438 drivers: ... |
107 |
err11171 = false; |
ca421e39f irqchip: irq-imx-... |
108 109 110 |
disable_cpuidle(); return; } |
439b9fed9 irqchip: irq-imx-... |
111 |
|
439b9fed9 irqchip: irq-imx-... |
112 113 114 115 116 |
iomux_gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (!IS_ERR(iomux_gpr)) regmap_update_bits(iomux_gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT, IMX6Q_GPR1_GINT); } |
e324c4dc4 irqchip/imx-gpcv2... |
117 118 119 |
static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on) { struct gpcv2_irqchip_data *cd = d->chip_data; |
508bc53ad irqchip: irq-imx-... |
120 |
#ifdef CONFIG_ARM64 |
ab850d655 irqchip: irq-imx-... |
121 |
struct arm_smccc_res res; |
508bc53ad irqchip: irq-imx-... |
122 |
#endif |
e324c4dc4 irqchip/imx-gpcv2... |
123 124 |
unsigned int idx = d->hwirq / 32; unsigned long flags; |
e324c4dc4 irqchip/imx-gpcv2... |
125 126 127 |
u32 mask, val; raw_spin_lock_irqsave(&cd->rlock, flags); |
f2dace5f9 irqchip/irq-imx-g... |
128 |
mask = BIT(d->hwirq % 32); |
e324c4dc4 irqchip/imx-gpcv2... |
129 130 131 |
val = cd->wakeup_sources[idx]; cd->wakeup_sources[idx] = on ? (val & ~mask) : (val | mask); |
ab850d655 irqchip: irq-imx-... |
132 133 134 135 136 |
#ifdef CONFIG_ARM64 arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_SET_WAKE, d->hwirq, on, 0, 0, 0, 0, &res); #endif |
e324c4dc4 irqchip/imx-gpcv2... |
137 |
raw_spin_unlock_irqrestore(&cd->rlock, flags); |
ab850d655 irqchip: irq-imx-... |
138 |
|
e324c4dc4 irqchip/imx-gpcv2... |
139 140 141 142 143 144 145 |
/* * Do *not* call into the parent, as the GIC doesn't have any * wake-up facility... */ return 0; } |
7bbbcfd92 MLK-21399 irqchip... |
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
static int imx8mq_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on) { struct arm_smccc_res res; arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_SET_WAKE, d->hwirq, on, 0, 0, 0, 0, &res); /* * Do *not* call into the parent, as the GIC doesn't have any * wake-up facility... */ return 0; } |
e324c4dc4 irqchip/imx-gpcv2... |
160 161 162 163 164 165 166 |
static void imx_gpcv2_irq_unmask(struct irq_data *d) { struct gpcv2_irqchip_data *cd = d->chip_data; void __iomem *reg; u32 val; raw_spin_lock(&cd->rlock); |
bd654fb67 irqchip/irq-imx-g... |
167 |
reg = gpcv2_idx_to_reg(cd, d->hwirq / 32); |
e324c4dc4 irqchip/imx-gpcv2... |
168 |
val = readl_relaxed(reg); |
f2dace5f9 irqchip/irq-imx-g... |
169 |
val &= ~BIT(d->hwirq % 32); |
e324c4dc4 irqchip/imx-gpcv2... |
170 171 172 173 174 |
writel_relaxed(val, reg); raw_spin_unlock(&cd->rlock); irq_chip_unmask_parent(d); } |
7bbbcfd92 MLK-21399 irqchip... |
175 176 177 178 179 180 181 182 183 |
static void imx8mq_gpcv2_irq_unmask(struct irq_data *d) { struct arm_smccc_res res; arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_UNMASK, d->hwirq, 0, 0, 0, 0, 0, &res); irq_chip_unmask_parent(d); } |
e324c4dc4 irqchip/imx-gpcv2... |
184 185 186 187 188 189 190 |
static void imx_gpcv2_irq_mask(struct irq_data *d) { struct gpcv2_irqchip_data *cd = d->chip_data; void __iomem *reg; u32 val; raw_spin_lock(&cd->rlock); |
bd654fb67 irqchip/irq-imx-g... |
191 |
reg = gpcv2_idx_to_reg(cd, d->hwirq / 32); |
e324c4dc4 irqchip/imx-gpcv2... |
192 |
val = readl_relaxed(reg); |
f2dace5f9 irqchip/irq-imx-g... |
193 |
val |= BIT(d->hwirq % 32); |
e324c4dc4 irqchip/imx-gpcv2... |
194 195 196 197 198 |
writel_relaxed(val, reg); raw_spin_unlock(&cd->rlock); irq_chip_mask_parent(d); } |
7bbbcfd92 MLK-21399 irqchip... |
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
static void imx8mq_gpcv2_irq_mask(struct irq_data *d) { struct arm_smccc_res res; arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_MASK, d->hwirq, 0, 0, 0, 0, 0, &res); irq_chip_mask_parent(d); } int imx8mq_gpcv2_irq_set_affinity(struct irq_data *d, const struct cpumask *cpumask, bool force) { struct arm_smccc_res res; int cpu = cpumask_any_and(cpumask, cpu_online_mask); arm_smccc_smc(FSL_SIP_GPC, FSL_SIP_CONFIG_GPC_SET_AFF, d->hwirq, cpu, 0, 0, 0, 0, &res); return irq_chip_set_affinity_parent(d, cpumask, force); } static struct irq_chip gpcv2_imx8mq_irqchip_data_chip = { .name = "GPCv2 i.MX8MQ", .irq_eoi = irq_chip_eoi_parent, .irq_mask = imx8mq_gpcv2_irq_mask, .irq_unmask = imx8mq_gpcv2_irq_unmask, .irq_set_wake = imx8mq_gpcv2_irq_set_wake, .irq_retrigger = irq_chip_retrigger_hierarchy, .irq_set_type = irq_chip_set_type_parent, #ifdef CONFIG_SMP .irq_set_affinity = imx8mq_gpcv2_irq_set_affinity, #endif }; |
e324c4dc4 irqchip/imx-gpcv2... |
233 234 235 236 237 238 239 |
static struct irq_chip gpcv2_irqchip_data_chip = { .name = "GPCv2", .irq_eoi = irq_chip_eoi_parent, .irq_mask = imx_gpcv2_irq_mask, .irq_unmask = imx_gpcv2_irq_unmask, .irq_set_wake = imx_gpcv2_irq_set_wake, .irq_retrigger = irq_chip_retrigger_hierarchy, |
9a446ef08 irqchip/irq-imx-g... |
240 |
.irq_set_type = irq_chip_set_type_parent, |
e324c4dc4 irqchip/imx-gpcv2... |
241 242 243 244 |
#ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif }; |
f833f57ff irqchip: Convert ... |
245 246 247 248 |
static int imx_gpcv2_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) |
e324c4dc4 irqchip/imx-gpcv2... |
249 |
{ |
f833f57ff irqchip: Convert ... |
250 251 252 |
if (is_of_node(fwspec->fwnode)) { if (fwspec->param_count != 3) return -EINVAL; |
e324c4dc4 irqchip/imx-gpcv2... |
253 |
|
f833f57ff irqchip: Convert ... |
254 255 256 |
/* No PPI should point to this domain */ if (fwspec->param[0] != 0) return -EINVAL; |
e324c4dc4 irqchip/imx-gpcv2... |
257 |
|
f833f57ff irqchip: Convert ... |
258 259 260 261 |
*hwirq = fwspec->param[1]; *type = fwspec->param[2]; return 0; } |
e324c4dc4 irqchip/imx-gpcv2... |
262 |
|
f833f57ff irqchip: Convert ... |
263 |
return -EINVAL; |
e324c4dc4 irqchip/imx-gpcv2... |
264 265 266 267 268 269 |
} static int imx_gpcv2_domain_alloc(struct irq_domain *domain, unsigned int irq, unsigned int nr_irqs, void *data) { |
f833f57ff irqchip: Convert ... |
270 271 |
struct irq_fwspec *fwspec = data; struct irq_fwspec parent_fwspec; |
e324c4dc4 irqchip/imx-gpcv2... |
272 |
irq_hw_number_t hwirq; |
f833f57ff irqchip: Convert ... |
273 274 |
unsigned int type; int err; |
e324c4dc4 irqchip/imx-gpcv2... |
275 |
int i; |
f833f57ff irqchip: Convert ... |
276 277 278 |
err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type); if (err) return err; |
e324c4dc4 irqchip/imx-gpcv2... |
279 |
|
f26d20d31 MLK-24914-01 irqc... |
280 |
if (hwirq >= GPC_MAX_IRQS) |
e324c4dc4 irqchip/imx-gpcv2... |
281 282 283 284 |
return -EINVAL; for (i = 0; i < nr_irqs; i++) { irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i, |
7bbbcfd92 MLK-21399 irqchip... |
285 |
err11171 ? &gpcv2_imx8mq_irqchip_data_chip : |
e324c4dc4 irqchip/imx-gpcv2... |
286 287 |
&gpcv2_irqchip_data_chip, domain->host_data); } |
f833f57ff irqchip: Convert ... |
288 289 290 291 |
parent_fwspec = *fwspec; parent_fwspec.fwnode = domain->parent->fwnode; return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_fwspec); |
e324c4dc4 irqchip/imx-gpcv2... |
292 |
} |
dcbbefceb irqchip/irq-imx-g... |
293 |
static const struct irq_domain_ops gpcv2_irqchip_data_domain_ops = { |
f833f57ff irqchip: Convert ... |
294 295 296 |
.translate = imx_gpcv2_domain_translate, .alloc = imx_gpcv2_domain_alloc, .free = irq_domain_free_irqs_common, |
e324c4dc4 irqchip/imx-gpcv2... |
297 |
}; |
ed01edc0a irqchip/irq-imx-g... |
298 299 300 301 302 |
static const struct of_device_id gpcv2_of_match[] = { { .compatible = "fsl,imx7d-gpc", .data = (const void *) 2 }, { .compatible = "fsl,imx8mq-gpc", .data = (const void *) 4 }, { /* END */ } }; |
e324c4dc4 irqchip/imx-gpcv2... |
303 304 305 306 307 |
static int __init imx_gpcv2_irqchip_init(struct device_node *node, struct device_node *parent) { struct irq_domain *parent_domain, *domain; struct gpcv2_irqchip_data *cd; |
ed01edc0a irqchip/irq-imx-g... |
308 309 |
const struct of_device_id *id; unsigned long core_num; |
e324c4dc4 irqchip/imx-gpcv2... |
310 311 312 |
int i; if (!parent) { |
e81f54c66 irqchip: Convert ... |
313 314 |
pr_err("%pOF: no parent, giving up ", node); |
e324c4dc4 irqchip/imx-gpcv2... |
315 316 |
return -ENODEV; } |
ed01edc0a irqchip/irq-imx-g... |
317 318 319 320 321 322 323 324 |
id = of_match_node(gpcv2_of_match, node); if (!id) { pr_err("%pOF: unknown compatibility string ", node); return -ENODEV; } core_num = (unsigned long)id->data; |
e324c4dc4 irqchip/imx-gpcv2... |
325 326 |
parent_domain = irq_find_host(parent); if (!parent_domain) { |
e81f54c66 irqchip: Convert ... |
327 328 |
pr_err("%pOF: unable to get parent domain ", node); |
e324c4dc4 irqchip/imx-gpcv2... |
329 330 331 332 333 |
return -ENXIO; } cd = kzalloc(sizeof(struct gpcv2_irqchip_data), GFP_KERNEL); if (!cd) { |
fb7348abb irqchip/irq-imx-g... |
334 335 |
pr_err("%pOF: kzalloc failed! ", node); |
e324c4dc4 irqchip/imx-gpcv2... |
336 337 |
return -ENOMEM; } |
75eb5e1e7 irqchip/irq-imx-g... |
338 |
raw_spin_lock_init(&cd->rlock); |
e324c4dc4 irqchip/imx-gpcv2... |
339 340 |
cd->gpc_base = of_iomap(node, 0); if (!cd->gpc_base) { |
fb7348abb irqchip/irq-imx-g... |
341 342 |
pr_err("%pOF: unable to map gpc registers ", node); |
e324c4dc4 irqchip/imx-gpcv2... |
343 344 345 |
kfree(cd); return -ENOMEM; } |
f26d20d31 MLK-24914-01 irqc... |
346 |
domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS, |
e324c4dc4 irqchip/imx-gpcv2... |
347 348 349 350 351 352 353 |
node, &gpcv2_irqchip_data_domain_ops, cd); if (!domain) { iounmap(cd->gpc_base); kfree(cd); return -ENOMEM; } irq_set_default_host(domain); |
41338bee6 MLK-24914-02 irqc... |
354 |
if (of_machine_is_compatible("fsl,imx8mq")) { |
7bbbcfd92 MLK-21399 irqchip... |
355 356 357 |
/* sw workaround for IPI can't wakeup CORE ERRATA(ERR011171) on i.MX8MQ */ err11171 = true; |
439b9fed9 irqchip: irq-imx-... |
358 |
imx_gpcv2_wake_request_fixup(); |
7bbbcfd92 MLK-21399 irqchip... |
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
} else { /* Initially mask all interrupts */ for (i = 0; i < IMR_NUM; i++) { void __iomem *reg = cd->gpc_base + i * 4; switch (core_num) { case 4: writel_relaxed(~0, reg + GPC_IMR1_CORE2); writel_relaxed(~0, reg + GPC_IMR1_CORE3); fallthrough; case 2: writel_relaxed(~0, reg + GPC_IMR1_CORE0); writel_relaxed(~0, reg + GPC_IMR1_CORE1); } cd->wakeup_sources[i] = ~0; } |
439b9fed9 irqchip: irq-imx-... |
375 |
|
7bbbcfd92 MLK-21399 irqchip... |
376 377 |
/* Let CORE0 as the default CPU to wake up by GPC */ cd->cpu2wakeup = GPC_IMR1_CORE0; |
e324c4dc4 irqchip/imx-gpcv2... |
378 |
|
7bbbcfd92 MLK-21399 irqchip... |
379 380 381 382 383 384 385 |
/* * Due to hardware design failure, need to make sure GPR * interrupt(#32) is unmasked during RUN mode to avoid entering * DSM by mistake. */ writel_relaxed(~0x1, cd->gpc_base + cd->cpu2wakeup); } |
e324c4dc4 irqchip/imx-gpcv2... |
386 387 |
imx_gpcv2_instance = cd; |
7bbbcfd92 MLK-21399 irqchip... |
388 389 390 |
if (!err11171) register_syscore_ops(&imx_gpcv2_syscore_ops); |
e324c4dc4 irqchip/imx-gpcv2... |
391 |
|
9d4b5bdc5 irqchip/irq-imx-g... |
392 393 394 395 396 |
/* * Clear the OF_POPULATED flag set in of_irq_init so that * later the GPC power domain driver will not be skipped. */ of_node_clear_flag(node, OF_POPULATED); |
e324c4dc4 irqchip/imx-gpcv2... |
397 398 |
return 0; } |
8ca66b7cc irqchip/irq-imx-g... |
399 400 |
IRQCHIP_DECLARE(imx_gpcv2_imx7d, "fsl,imx7d-gpc", imx_gpcv2_irqchip_init); IRQCHIP_DECLARE(imx_gpcv2_imx8mq, "fsl,imx8mq-gpc", imx_gpcv2_irqchip_init); |