projects/linux·blob master · refs

254 lines · 6.9 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * StarFive JHB100 External Interrupt Controller driver
4 *
5 * Copyright (C) 2023 StarFive Technology Co., Ltd.
6 *
7 * Author: Changhuang Liang <[email protected]>
8 */
9
10#include <linux/bitops.h>
11#include <linux/cleanup.h>
12#include <linux/clk.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/irqchip.h>
16#include <linux/irqchip/chained_irq.h>
17#include <linux/irqdomain.h>
18#include <linux/of_irq.h>
19#include <linux/platform_device.h>
20#include <linux/reset.h>
21#include <linux/spinlock.h>
22
23#define STARFIVE_INTC_SRC_TYPE(n) (0x04 + ((n) * 0x20))
24#define STARFIVE_INTC_SRC_CLEAR(n) (0x10 + ((n) * 0x20))
25#define STARFIVE_INTC_SRC_MASK(n) (0x14 + ((n) * 0x20))
26#define STARFIVE_INTC_SRC_INT(n) (0x1c + ((n) * 0x20))
27
28#define STARFIVE_INTC_TRIGGER_MASK 0x3
29#define STARFIVE_INTC_TRIGGER_HIGH 0
30#define STARFIVE_INTC_TRIGGER_LOW 1
31#define STARFIVE_INTC_TRIGGER_POSEDGE 2
32#define STARFIVE_INTC_TRIGGER_NEGEDGE 3
33
34#define STARFIVE_INTC_NUM 2
35#define STARFIVE_INTC_SRC_IRQ_NUM 32
36#define STARFIVE_INTC_TYPE_NUM 16
37
38struct starfive_irq_chip {
39 void __iomem *base;
40 struct irq_domain *domain;
41 raw_spinlock_t lock;
42};
43
44static void starfive_intc_mod(struct starfive_irq_chip *irqc, u32 reg, u32 mask, u32 data)
45{
46 u32 value;
47
48 value = ioread32(irqc->base + reg) & ~mask;
49 data &= mask;
50 data |= value;
51 iowrite32(data, irqc->base + reg);
52}
53
54static void starfive_intc_bit_set(struct starfive_irq_chip *irqc,
55 u32 reg, u32 bit_mask)
56{
57 u32 value;
58
59 value = ioread32(irqc->base + reg);
60 value |= bit_mask;
61 iowrite32(value, irqc->base + reg);
62}
63
64static void starfive_intc_bit_clear(struct starfive_irq_chip *irqc,
65 u32 reg, u32 bit_mask)
66{
67 u32 value;
68
69 value = ioread32(irqc->base + reg);
70 value &= ~bit_mask;
71 iowrite32(value, irqc->base + reg);
72}
73
74static void starfive_intc_unmask(struct irq_data *d)
75{
76 struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
77 int i, bitpos;
78
79 i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
80 bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
81
82 guard(raw_spinlock)(&irqc->lock);
83 starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
84}
85
86static void starfive_intc_mask(struct irq_data *d)
87{
88 struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
89 int i, bitpos;
90
91 i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
92 bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
93
94 guard(raw_spinlock)(&irqc->lock);
95 starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_MASK(i), BIT(bitpos));
96}
97
98static void starfive_intc_ack(struct irq_data *d)
99{
100 /* for handle_edge_irq, nothing to do */
101}
102
103static int starfive_intc_set_type(struct irq_data *d, unsigned int type)
104{
105 struct starfive_irq_chip *irqc = irq_data_get_irq_chip_data(d);
106 u32 i, bitpos, ty_pos, ty_shift, trigger, typeval;
107 irq_flow_handler_t handler;
108
109 i = d->hwirq / STARFIVE_INTC_SRC_IRQ_NUM;
110 bitpos = d->hwirq % STARFIVE_INTC_SRC_IRQ_NUM;
111 ty_pos = bitpos / STARFIVE_INTC_TYPE_NUM;
112 ty_shift = (bitpos % STARFIVE_INTC_TYPE_NUM) * 2;
113
114 switch (type) {
115 case IRQF_TRIGGER_LOW:
116 trigger = STARFIVE_INTC_TRIGGER_LOW;
117 handler = handle_level_irq;
118 break;
119 case IRQF_TRIGGER_HIGH:
120 trigger = STARFIVE_INTC_TRIGGER_HIGH;
121 handler = handle_level_irq;
122 break;
123 case IRQF_TRIGGER_FALLING:
124 trigger = STARFIVE_INTC_TRIGGER_NEGEDGE;
125 handler = handle_edge_irq;
126 break;
127 case IRQF_TRIGGER_RISING:
128 trigger = STARFIVE_INTC_TRIGGER_POSEDGE;
129 handler = handle_edge_irq;
130 break;
131 default:
132 return -EINVAL;
133 }
134
135 irq_set_handler_locked(d, handler);
136 typeval = trigger << ty_shift;
137
138 guard(raw_spinlock)(&irqc->lock);
139
140 starfive_intc_mod(irqc, STARFIVE_INTC_SRC_TYPE(i) + 4 * ty_pos,
141 STARFIVE_INTC_TRIGGER_MASK << ty_shift, typeval);
142
143 /* Once the type is updated, clear interrupt can help to reset the type value */
144 starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos));
145 starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(bitpos));
146
147 return 0;
148}
149
150static struct irq_chip intc_dev = {
151 .name = "StarFive JHB100 INTC",
152 .irq_unmask = starfive_intc_unmask,
153 .irq_mask = starfive_intc_mask,
154 .irq_ack = starfive_intc_ack,
155 .irq_set_type = starfive_intc_set_type,
156};
157
158static int starfive_intc_map(struct irq_domain *d, unsigned int irq,
159 irq_hw_number_t hwirq)
160{
161 irq_domain_set_info(d, irq, hwirq, &intc_dev, d->host_data,
162 handle_level_irq, NULL, NULL);
163
164 return 0;
165}
166
167static const struct irq_domain_ops starfive_intc_domain_ops = {
168 .xlate = irq_domain_xlate_onecell,
169 .map = starfive_intc_map,
170};
171
172static void starfive_intc_irq_handler(struct irq_desc *desc)
173{
174 struct starfive_irq_chip *irqc = irq_data_get_irq_handler_data(&desc->irq_data);
175 struct irq_chip *chip = irq_desc_get_chip(desc);
176 unsigned long value;
177 int hwirq;
178
179 chained_irq_enter(chip, desc);
180
181 for (int i = 0; i < STARFIVE_INTC_NUM; i++) {
182 value = ioread32(irqc->base + STARFIVE_INTC_SRC_INT(i));
183 while (value) {
184 hwirq = ffs(value) - 1;
185
186 generic_handle_domain_irq(irqc->domain,
187 hwirq + i * STARFIVE_INTC_SRC_IRQ_NUM);
188
189 starfive_intc_bit_set(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq));
190 starfive_intc_bit_clear(irqc, STARFIVE_INTC_SRC_CLEAR(i), BIT(hwirq));
191
192 __clear_bit(hwirq, &value);
193 }
194 }
195
196 chained_irq_exit(chip, desc);
197}
198
199static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent)
200{
201 struct device_node *intc = pdev->dev.of_node;
202 struct reset_control *rst;
203 struct clk *clk;
204 int parent_irq;
205
206 struct starfive_irq_chip *irqc __free(kfree) = kzalloc_obj(*irqc);
207 if (!irqc)
208 return -ENOMEM;
209
210 irqc->base = devm_platform_ioremap_resource(pdev, 0);
211 if (IS_ERR(irqc->base))
212 return dev_err_probe(&pdev->dev, PTR_ERR(irqc->base), "unable to map registers\n");
213
214 rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
215 if (IS_ERR(rst))
216 return dev_err_probe(&pdev->dev, PTR_ERR(rst),
217 "Unable to get and deassert reset control\n");
218
219 clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
220 if (IS_ERR(clk))
221 return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Unable to get and enable clock\n");
222
223
224 raw_spin_lock_init(&irqc->lock);
225
226 irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc),
227 STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM,
228 &starfive_intc_domain_ops, irqc);
229 if (!irqc->domain)
230 return dev_err_probe(&pdev->dev, -EINVAL, "Unable to create IRQ domain\n");
231
232 parent_irq = of_irq_get(intc, 0);
233 if (parent_irq < 0) {
234 irq_domain_remove(irqc->domain);
235 return dev_err_probe(&pdev->dev, parent_irq, "Failed to get main IRQ\n");
236 }
237
238 irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler,
239 irqc);
240
241 dev_info(&pdev->dev, "Interrupt controller register, nr_irqs %d\n",
242 STARFIVE_INTC_SRC_IRQ_NUM * STARFIVE_INTC_NUM);
243
244 retain_and_null_ptr(irqc);
245 return 0;
246}
247
248IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
249IRQCHIP_MATCH("starfive,jhb100-intc", starfive_intc_probe)
250IRQCHIP_PLATFORM_DRIVER_END(starfive_intc)
251
252MODULE_DESCRIPTION("StarFive JHB100 External Interrupt Controller");
253MODULE_LICENSE("GPL");
254MODULE_AUTHOR("Changhuang Liang <[email protected]>");