1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Atheros AR71xx/AR724x/AR913x MISC interrupt controller |
4 | * |
5 | * Copyright (C) 2015 Alban Bedel <albeu@free.fr> |
6 | * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> |
7 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> |
8 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> |
9 | * |
10 | * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP |
11 | */ |
12 | |
13 | #include <linux/irqchip.h> |
14 | #include <linux/irqchip/chained_irq.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/of_irq.h> |
17 | |
18 | #define AR71XX_RESET_REG_MISC_INT_STATUS 0 |
19 | #define AR71XX_RESET_REG_MISC_INT_ENABLE 4 |
20 | |
21 | #define ATH79_MISC_IRQ_COUNT 32 |
22 | #define ATH79_MISC_PERF_IRQ 5 |
23 | |
24 | static int ath79_perfcount_irq; |
25 | |
26 | int get_c0_perfcount_int(void) |
27 | { |
28 | return ath79_perfcount_irq; |
29 | } |
30 | EXPORT_SYMBOL_GPL(get_c0_perfcount_int); |
31 | |
32 | static void ath79_misc_irq_handler(struct irq_desc *desc) |
33 | { |
34 | struct irq_domain *domain = irq_desc_get_handler_data(desc); |
35 | struct irq_chip *chip = irq_desc_get_chip(desc); |
36 | void __iomem *base = domain->host_data; |
37 | u32 pending; |
38 | |
39 | chained_irq_enter(chip, desc); |
40 | |
41 | pending = __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_STATUS) & |
42 | __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
43 | |
44 | if (!pending) { |
45 | spurious_interrupt(); |
46 | chained_irq_exit(chip, desc); |
47 | return; |
48 | } |
49 | |
50 | while (pending) { |
51 | int bit = __ffs(pending); |
52 | |
53 | generic_handle_domain_irq(domain, hwirq: bit); |
54 | pending &= ~BIT(bit); |
55 | } |
56 | |
57 | chained_irq_exit(chip, desc); |
58 | } |
59 | |
60 | static void ar71xx_misc_irq_unmask(struct irq_data *d) |
61 | { |
62 | void __iomem *base = irq_data_get_irq_chip_data(d); |
63 | unsigned int irq = d->hwirq; |
64 | u32 t; |
65 | |
66 | t = __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
67 | __raw_writel(val: t | BIT(irq), addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
68 | |
69 | /* flush write */ |
70 | __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
71 | } |
72 | |
73 | static void ar71xx_misc_irq_mask(struct irq_data *d) |
74 | { |
75 | void __iomem *base = irq_data_get_irq_chip_data(d); |
76 | unsigned int irq = d->hwirq; |
77 | u32 t; |
78 | |
79 | t = __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
80 | __raw_writel(val: t & ~BIT(irq), addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
81 | |
82 | /* flush write */ |
83 | __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
84 | } |
85 | |
86 | static void ar724x_misc_irq_ack(struct irq_data *d) |
87 | { |
88 | void __iomem *base = irq_data_get_irq_chip_data(d); |
89 | unsigned int irq = d->hwirq; |
90 | u32 t; |
91 | |
92 | t = __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_STATUS); |
93 | __raw_writel(val: t & ~BIT(irq), addr: base + AR71XX_RESET_REG_MISC_INT_STATUS); |
94 | |
95 | /* flush write */ |
96 | __raw_readl(addr: base + AR71XX_RESET_REG_MISC_INT_STATUS); |
97 | } |
98 | |
99 | static struct irq_chip ath79_misc_irq_chip = { |
100 | .name = "MISC" , |
101 | .irq_unmask = ar71xx_misc_irq_unmask, |
102 | .irq_mask = ar71xx_misc_irq_mask, |
103 | }; |
104 | |
105 | static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) |
106 | { |
107 | irq_set_chip_and_handler(irq, chip: &ath79_misc_irq_chip, handle: handle_level_irq); |
108 | irq_set_chip_data(irq, data: d->host_data); |
109 | return 0; |
110 | } |
111 | |
112 | static const struct irq_domain_ops misc_irq_domain_ops = { |
113 | .xlate = irq_domain_xlate_onecell, |
114 | .map = misc_map, |
115 | }; |
116 | |
117 | static void __init ath79_misc_intc_domain_init( |
118 | struct irq_domain *domain, int irq) |
119 | { |
120 | void __iomem *base = domain->host_data; |
121 | |
122 | ath79_perfcount_irq = irq_create_mapping(host: domain, ATH79_MISC_PERF_IRQ); |
123 | |
124 | /* Disable and clear all interrupts */ |
125 | __raw_writel(val: 0, addr: base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
126 | __raw_writel(val: 0, addr: base + AR71XX_RESET_REG_MISC_INT_STATUS); |
127 | |
128 | irq_set_chained_handler_and_data(irq, handle: ath79_misc_irq_handler, data: domain); |
129 | } |
130 | |
131 | static int __init ath79_misc_intc_of_init( |
132 | struct device_node *node, struct device_node *parent) |
133 | { |
134 | struct irq_domain *domain; |
135 | void __iomem *base; |
136 | int irq; |
137 | |
138 | irq = irq_of_parse_and_map(node, index: 0); |
139 | if (!irq) { |
140 | pr_err("Failed to get MISC IRQ\n" ); |
141 | return -EINVAL; |
142 | } |
143 | |
144 | base = of_iomap(node, index: 0); |
145 | if (!base) { |
146 | pr_err("Failed to get MISC IRQ registers\n" ); |
147 | return -ENOMEM; |
148 | } |
149 | |
150 | domain = irq_domain_add_linear(of_node: node, ATH79_MISC_IRQ_COUNT, |
151 | ops: &misc_irq_domain_ops, host_data: base); |
152 | if (!domain) { |
153 | pr_err("Failed to add MISC irqdomain\n" ); |
154 | return -EINVAL; |
155 | } |
156 | |
157 | ath79_misc_intc_domain_init(domain, irq); |
158 | return 0; |
159 | } |
160 | |
161 | static int __init ar7100_misc_intc_of_init( |
162 | struct device_node *node, struct device_node *parent) |
163 | { |
164 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; |
165 | return ath79_misc_intc_of_init(node, parent); |
166 | } |
167 | |
168 | IRQCHIP_DECLARE(ar7100_misc_intc, "qca,ar7100-misc-intc" , |
169 | ar7100_misc_intc_of_init); |
170 | |
171 | static int __init ar7240_misc_intc_of_init( |
172 | struct device_node *node, struct device_node *parent) |
173 | { |
174 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; |
175 | return ath79_misc_intc_of_init(node, parent); |
176 | } |
177 | |
178 | IRQCHIP_DECLARE(ar7240_misc_intc, "qca,ar7240-misc-intc" , |
179 | ar7240_misc_intc_of_init); |
180 | |
181 | void __init ath79_misc_irq_init(void __iomem *regs, int irq, |
182 | int irq_base, bool is_ar71xx) |
183 | { |
184 | struct irq_domain *domain; |
185 | |
186 | if (is_ar71xx) |
187 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; |
188 | else |
189 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; |
190 | |
191 | domain = irq_domain_add_legacy(NULL, ATH79_MISC_IRQ_COUNT, |
192 | first_irq: irq_base, first_hwirq: 0, ops: &misc_irq_domain_ops, host_data: regs); |
193 | if (!domain) |
194 | panic(fmt: "Failed to create MISC irqdomain" ); |
195 | |
196 | ath79_misc_intc_domain_init(domain, irq); |
197 | } |
198 | |