hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/irqchip/irq-bcm7038-l1.c
....@@ -1,12 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * Broadcom BCM7038 style Level 1 interrupt controller driver
34 *
45 * Copyright (C) 2014 Broadcom Corporation
56 * Author: Kevin Cernekee
6
- *
7
- * This program is free software; you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License version 2 as
9
- * published by the Free Software Foundation.
107 */
118
129 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
....@@ -30,6 +27,10 @@
3027 #include <linux/types.h>
3128 #include <linux/irqchip.h>
3229 #include <linux/irqchip/chained_irq.h>
30
+#include <linux/syscore_ops.h>
31
+#ifdef CONFIG_ARM
32
+#include <asm/smp_plat.h>
33
+#endif
3334
3435 #define IRQS_PER_WORD 32
3536 #define REG_BYTES_PER_IRQ_WORD (sizeof(u32) * 4)
....@@ -42,12 +43,17 @@
4243 unsigned int n_words;
4344 struct irq_domain *domain;
4445 struct bcm7038_l1_cpu *cpus[NR_CPUS];
46
+#ifdef CONFIG_PM_SLEEP
47
+ struct list_head list;
48
+ u32 wake_mask[MAX_WORDS];
49
+#endif
50
+ u32 irq_fwd_mask[MAX_WORDS];
4551 u8 affinity[MAX_WORDS * IRQS_PER_WORD];
4652 };
4753
4854 struct bcm7038_l1_cpu {
4955 void __iomem *map_base;
50
- u32 mask_cache[0];
56
+ u32 mask_cache[];
5157 };
5258
5359 /*
....@@ -252,6 +258,7 @@
252258 resource_size_t sz;
253259 struct bcm7038_l1_cpu *cpu;
254260 unsigned int i, n_words, parent_irq;
261
+ int ret;
255262
256263 if (of_address_to_resource(dn, idx, &res))
257264 return -EINVAL;
....@@ -265,6 +272,14 @@
265272 else if (intc->n_words != n_words)
266273 return -EINVAL;
267274
275
+ ret = of_property_read_u32_array(dn , "brcm,int-fwd-mask",
276
+ intc->irq_fwd_mask, n_words);
277
+ if (ret != 0 && ret != -EINVAL) {
278
+ /* property exists but has the wrong number of words */
279
+ pr_err("invalid brcm,int-fwd-mask property\n");
280
+ return -EINVAL;
281
+ }
282
+
268283 cpu = intc->cpus[idx] = kzalloc(sizeof(*cpu) + n_words * sizeof(u32),
269284 GFP_KERNEL);
270285 if (!cpu)
....@@ -275,8 +290,11 @@
275290 return -ENOMEM;
276291
277292 for (i = 0; i < n_words; i++) {
278
- l1_writel(0xffffffff, cpu->map_base + reg_mask_set(intc, i));
279
- cpu->mask_cache[i] = 0xffffffff;
293
+ l1_writel(~intc->irq_fwd_mask[i],
294
+ cpu->map_base + reg_mask_set(intc, i));
295
+ l1_writel(intc->irq_fwd_mask[i],
296
+ cpu->map_base + reg_mask_clr(intc, i));
297
+ cpu->mask_cache[i] = ~intc->irq_fwd_mask[i];
280298 }
281299
282300 parent_irq = irq_of_parse_and_map(dn, idx);
....@@ -294,6 +312,87 @@
294312 return 0;
295313 }
296314
315
+#ifdef CONFIG_PM_SLEEP
316
+/*
317
+ * We keep a list of bcm7038_l1_chip used for suspend/resume. This hack is
318
+ * used because the struct chip_type suspend/resume hooks are not called
319
+ * unless chip_type is hooked onto a generic_chip. Since this driver does
320
+ * not use generic_chip, we need to manually hook our resume/suspend to
321
+ * syscore_ops.
322
+ */
323
+static LIST_HEAD(bcm7038_l1_intcs_list);
324
+static DEFINE_RAW_SPINLOCK(bcm7038_l1_intcs_lock);
325
+
326
+static int bcm7038_l1_suspend(void)
327
+{
328
+ struct bcm7038_l1_chip *intc;
329
+ int boot_cpu, word;
330
+ u32 val;
331
+
332
+ /* Wakeup interrupt should only come from the boot cpu */
333
+#ifdef CONFIG_SMP
334
+ boot_cpu = cpu_logical_map(0);
335
+#else
336
+ boot_cpu = 0;
337
+#endif
338
+
339
+ list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) {
340
+ for (word = 0; word < intc->n_words; word++) {
341
+ val = intc->wake_mask[word] | intc->irq_fwd_mask[word];
342
+ l1_writel(~val,
343
+ intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word));
344
+ l1_writel(val,
345
+ intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word));
346
+ }
347
+ }
348
+
349
+ return 0;
350
+}
351
+
352
+static void bcm7038_l1_resume(void)
353
+{
354
+ struct bcm7038_l1_chip *intc;
355
+ int boot_cpu, word;
356
+
357
+#ifdef CONFIG_SMP
358
+ boot_cpu = cpu_logical_map(0);
359
+#else
360
+ boot_cpu = 0;
361
+#endif
362
+
363
+ list_for_each_entry(intc, &bcm7038_l1_intcs_list, list) {
364
+ for (word = 0; word < intc->n_words; word++) {
365
+ l1_writel(intc->cpus[boot_cpu]->mask_cache[word],
366
+ intc->cpus[boot_cpu]->map_base + reg_mask_set(intc, word));
367
+ l1_writel(~intc->cpus[boot_cpu]->mask_cache[word],
368
+ intc->cpus[boot_cpu]->map_base + reg_mask_clr(intc, word));
369
+ }
370
+ }
371
+}
372
+
373
+static struct syscore_ops bcm7038_l1_syscore_ops = {
374
+ .suspend = bcm7038_l1_suspend,
375
+ .resume = bcm7038_l1_resume,
376
+};
377
+
378
+static int bcm7038_l1_set_wake(struct irq_data *d, unsigned int on)
379
+{
380
+ struct bcm7038_l1_chip *intc = irq_data_get_irq_chip_data(d);
381
+ unsigned long flags;
382
+ u32 word = d->hwirq / IRQS_PER_WORD;
383
+ u32 mask = BIT(d->hwirq % IRQS_PER_WORD);
384
+
385
+ raw_spin_lock_irqsave(&intc->lock, flags);
386
+ if (on)
387
+ intc->wake_mask[word] |= mask;
388
+ else
389
+ intc->wake_mask[word] &= ~mask;
390
+ raw_spin_unlock_irqrestore(&intc->lock, flags);
391
+
392
+ return 0;
393
+}
394
+#endif
395
+
297396 static struct irq_chip bcm7038_l1_irq_chip = {
298397 .name = "bcm7038-l1",
299398 .irq_mask = bcm7038_l1_mask,
....@@ -302,11 +401,21 @@
302401 #ifdef CONFIG_SMP
303402 .irq_cpu_offline = bcm7038_l1_cpu_offline,
304403 #endif
404
+#ifdef CONFIG_PM_SLEEP
405
+ .irq_set_wake = bcm7038_l1_set_wake,
406
+#endif
305407 };
306408
307409 static int bcm7038_l1_map(struct irq_domain *d, unsigned int virq,
308410 irq_hw_number_t hw_irq)
309411 {
412
+ struct bcm7038_l1_chip *intc = d->host_data;
413
+ u32 mask = BIT(hw_irq % IRQS_PER_WORD);
414
+ u32 word = hw_irq / IRQS_PER_WORD;
415
+
416
+ if (intc->irq_fwd_mask[word] & mask)
417
+ return -EPERM;
418
+
310419 irq_set_chip_and_handler(virq, &bcm7038_l1_irq_chip, handle_level_irq);
311420 irq_set_chip_data(virq, d->host_data);
312421 irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq)));
....@@ -318,7 +427,7 @@
318427 .map = bcm7038_l1_map,
319428 };
320429
321
-int __init bcm7038_l1_of_init(struct device_node *dn,
430
+static int __init bcm7038_l1_of_init(struct device_node *dn,
322431 struct device_node *parent)
323432 {
324433 struct bcm7038_l1_chip *intc;
....@@ -347,6 +456,19 @@
347456 goto out_unmap;
348457 }
349458
459
+#ifdef CONFIG_PM_SLEEP
460
+ /* Add bcm7038_l1_chip into a list */
461
+ raw_spin_lock(&bcm7038_l1_intcs_lock);
462
+ list_add_tail(&intc->list, &bcm7038_l1_intcs_list);
463
+ raw_spin_unlock(&bcm7038_l1_intcs_lock);
464
+
465
+ if (list_is_singular(&bcm7038_l1_intcs_list))
466
+ register_syscore_ops(&bcm7038_l1_syscore_ops);
467
+#endif
468
+
469
+ pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n",
470
+ dn, IRQS_PER_WORD * intc->n_words);
471
+
350472 return 0;
351473
352474 out_unmap: