Commit | Line | Data |
---|---|---|
19d99164 AB |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* | |
3 | * Microsemi Ocelot IRQ controller driver | |
4 | * | |
5 | * Copyright (c) 2017 Microsemi Corporation | |
6 | */ | |
7 | #include <linux/bitops.h> | |
8 | #include <linux/irq.h> | |
9 | #include <linux/of_address.h> | |
10 | #include <linux/of_irq.h> | |
11 | #include <linux/irqchip.h> | |
12 | #include <linux/irqchip/chained_irq.h> | |
13 | #include <linux/interrupt.h> | |
14 | ||
15 | #define ICPU_CFG_INTR_INTR_STICKY 0x10 | |
16 | #define ICPU_CFG_INTR_INTR_ENA 0x18 | |
17 | #define ICPU_CFG_INTR_INTR_ENA_CLR 0x1c | |
18 | #define ICPU_CFG_INTR_INTR_ENA_SET 0x20 | |
19 | #define ICPU_CFG_INTR_DST_INTR_IDENT(x) (0x38 + 0x4 * (x)) | |
20 | #define ICPU_CFG_INTR_INTR_TRIGGER(x) (0x5c + 0x4 * (x)) | |
21 | ||
22 | #define OCELOT_NR_IRQ 24 | |
23 | ||
24 | static void ocelot_irq_unmask(struct irq_data *data) | |
25 | { | |
26 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); | |
27 | struct irq_chip_type *ct = irq_data_get_chip_type(data); | |
28 | unsigned int mask = data->mask; | |
29 | u32 val; | |
30 | ||
31 | irq_gc_lock(gc); | |
32 | val = irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(0)) | | |
33 | irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(1)); | |
34 | if (!(val & mask)) | |
35 | irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_STICKY); | |
36 | ||
37 | *ct->mask_cache &= ~mask; | |
38 | irq_reg_writel(gc, mask, ICPU_CFG_INTR_INTR_ENA_SET); | |
39 | irq_gc_unlock(gc); | |
40 | } | |
41 | ||
42 | static void ocelot_irq_handler(struct irq_desc *desc) | |
43 | { | |
44 | struct irq_chip *chip = irq_desc_get_chip(desc); | |
45 | struct irq_domain *d = irq_desc_get_handler_data(desc); | |
46 | struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); | |
47 | u32 reg = irq_reg_readl(gc, ICPU_CFG_INTR_DST_INTR_IDENT(0)); | |
48 | ||
49 | chained_irq_enter(chip, desc); | |
50 | ||
51 | while (reg) { | |
52 | u32 hwirq = __fls(reg); | |
53 | ||
54 | generic_handle_irq(irq_find_mapping(d, hwirq)); | |
55 | reg &= ~(BIT(hwirq)); | |
56 | } | |
57 | ||
58 | chained_irq_exit(chip, desc); | |
59 | } | |
60 | ||
61 | static int __init ocelot_irq_init(struct device_node *node, | |
62 | struct device_node *parent) | |
63 | { | |
64 | struct irq_domain *domain; | |
65 | struct irq_chip_generic *gc; | |
66 | int parent_irq, ret; | |
67 | ||
68 | parent_irq = irq_of_parse_and_map(node, 0); | |
69 | if (!parent_irq) | |
70 | return -EINVAL; | |
71 | ||
72 | domain = irq_domain_add_linear(node, OCELOT_NR_IRQ, | |
73 | &irq_generic_chip_ops, NULL); | |
74 | if (!domain) { | |
f9c75bca | 75 | pr_err("%pOFn: unable to add irq domain\n", node); |
19d99164 AB |
76 | return -ENOMEM; |
77 | } | |
78 | ||
79 | ret = irq_alloc_domain_generic_chips(domain, OCELOT_NR_IRQ, 1, | |
80 | "icpu", handle_level_irq, | |
81 | 0, 0, 0); | |
82 | if (ret) { | |
f9c75bca | 83 | pr_err("%pOFn: unable to alloc irq domain gc\n", node); |
19d99164 AB |
84 | goto err_domain_remove; |
85 | } | |
86 | ||
87 | gc = irq_get_domain_generic_chip(domain, 0); | |
88 | gc->reg_base = of_iomap(node, 0); | |
89 | if (!gc->reg_base) { | |
f9c75bca | 90 | pr_err("%pOFn: unable to map resource\n", node); |
19d99164 AB |
91 | ret = -ENOMEM; |
92 | goto err_gc_free; | |
93 | } | |
94 | ||
95 | gc->chip_types[0].regs.ack = ICPU_CFG_INTR_INTR_STICKY; | |
96 | gc->chip_types[0].regs.mask = ICPU_CFG_INTR_INTR_ENA_CLR; | |
97 | gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; | |
98 | gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; | |
99 | gc->chip_types[0].chip.irq_unmask = ocelot_irq_unmask; | |
100 | ||
101 | /* Mask and ack all interrupts */ | |
102 | irq_reg_writel(gc, 0, ICPU_CFG_INTR_INTR_ENA); | |
103 | irq_reg_writel(gc, 0xffffffff, ICPU_CFG_INTR_INTR_STICKY); | |
104 | ||
105 | irq_set_chained_handler_and_data(parent_irq, ocelot_irq_handler, | |
106 | domain); | |
107 | ||
108 | return 0; | |
109 | ||
110 | err_gc_free: | |
111 | irq_free_generic_chip(gc); | |
112 | ||
113 | err_domain_remove: | |
114 | irq_domain_remove(domain); | |
115 | ||
116 | return ret; | |
117 | } | |
118 | IRQCHIP_DECLARE(ocelot_icpu, "mscc,ocelot-icpu-intr", ocelot_irq_init); |