xref: /linux/drivers/gpio/gpio-spacemit-k1.c (revision fbf5df34a4dbcd09d433dd4f0916bf9b2ddb16de)
1 // SPDX-License-Identifier: GPL-2.0 OR MIT
2 /*
3  * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
4  * Copyright (C) 2025 Yixun Lan <dlan@gentoo.org>
5  */
6 
7 #include <linux/clk.h>
8 #include <linux/gpio/driver.h>
9 #include <linux/gpio/generic.h>
10 #include <linux/init.h>
11 #include <linux/interrupt.h>
12 #include <linux/io.h>
13 #include <linux/irq.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/seq_file.h>
17 
18 #define SPACEMIT_NR_BANKS		4
19 #define SPACEMIT_NR_GPIOS_PER_BANK	32
20 
21 #define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc)
22 #define to_spacemit_gpio_regs(gb) ((gb)->sg->data->offsets)
23 
24 enum spacemit_gpio_registers {
25 	SPACEMIT_GPLR,		/* port level - R */
26 	SPACEMIT_GPDR,		/* port direction - R/W */
27 	SPACEMIT_GPSR,		/* port set - W */
28 	SPACEMIT_GPCR,		/* port clear - W */
29 	SPACEMIT_GRER,		/* port rising edge R/W */
30 	SPACEMIT_GFER,		/* port falling edge R/W */
31 	SPACEMIT_GEDR,		/* edge detect status - R/W1C */
32 	SPACEMIT_GSDR,		/* (set) direction - W */
33 	SPACEMIT_GCDR,		/* (clear) direction - W */
34 	SPACEMIT_GSRER,		/* (set) rising edge detect enable - W */
35 	SPACEMIT_GCRER,		/* (clear) rising edge detect enable - W */
36 	SPACEMIT_GSFER,		/* (set) falling edge detect enable - W */
37 	SPACEMIT_GCFER,		/* (clear) falling edge detect enable - W */
38 	SPACEMIT_GAPMASK,	/* interrupt mask , 0 disable, 1 enable - R/W */
39 	SPACEMIT_GCPMASK,	/* interrupt mask for K3 */
40 };
41 
42 struct spacemit_gpio;
43 
44 struct spacemit_gpio_data {
45 	const unsigned int *offsets;
46 	u32 bank_offsets[SPACEMIT_NR_BANKS];
47 };
48 
49 struct spacemit_gpio_bank {
50 	struct gpio_generic_chip chip;
51 	struct spacemit_gpio *sg;
52 	void __iomem *base;
53 	u32 irq_mask;
54 	u32 irq_rising_edge;
55 	u32 irq_falling_edge;
56 };
57 
58 struct spacemit_gpio {
59 	struct device *dev;
60 	const struct spacemit_gpio_data *data;
61 	struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS];
62 };
63 
64 static u32 spacemit_gpio_read(struct spacemit_gpio_bank *gb,
65 			      enum spacemit_gpio_registers reg)
66 {
67 	return readl(gb->base + to_spacemit_gpio_regs(gb)[reg]);
68 }
69 
70 static void spacemit_gpio_write(struct spacemit_gpio_bank *gb,
71 				enum spacemit_gpio_registers reg, u32 val)
72 {
73 	writel(val, gb->base + to_spacemit_gpio_regs(gb)[reg]);
74 }
75 
76 static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb)
77 {
78 	return (u32)(gb - gb->sg->sgb);
79 }
80 
81 static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id)
82 {
83 	struct spacemit_gpio_bank *gb = dev_id;
84 	unsigned long pending;
85 	u32 n, gedr;
86 
87 	gedr = spacemit_gpio_read(gb, SPACEMIT_GEDR);
88 	if (!gedr)
89 		return IRQ_NONE;
90 	spacemit_gpio_write(gb, SPACEMIT_GEDR, gedr);
91 
92 	pending = gedr & gb->irq_mask;
93 	if (!pending)
94 		return IRQ_NONE;
95 
96 	for_each_set_bit(n, &pending, BITS_PER_LONG)
97 		handle_nested_irq(irq_find_mapping(gb->chip.gc.irq.domain, n));
98 
99 	return IRQ_HANDLED;
100 }
101 
102 static void spacemit_gpio_irq_ack(struct irq_data *d)
103 {
104 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
105 
106 	spacemit_gpio_write(gb, SPACEMIT_GEDR, BIT(irqd_to_hwirq(d)));
107 }
108 
109 static void spacemit_gpio_irq_mask(struct irq_data *d)
110 {
111 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
112 	u32 bit = BIT(irqd_to_hwirq(d));
113 
114 	gb->irq_mask &= ~bit;
115 	spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
116 
117 	if (bit & gb->irq_rising_edge)
118 		spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
119 
120 	if (bit & gb->irq_falling_edge)
121 		spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
122 }
123 
124 static void spacemit_gpio_irq_unmask(struct irq_data *d)
125 {
126 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
127 	u32 bit = BIT(irqd_to_hwirq(d));
128 
129 	gb->irq_mask |= bit;
130 
131 	if (bit & gb->irq_rising_edge)
132 		spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
133 
134 	if (bit & gb->irq_falling_edge)
135 		spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
136 
137 	spacemit_gpio_write(gb, SPACEMIT_GAPMASK, gb->irq_mask);
138 }
139 
140 static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
141 {
142 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
143 	u32 bit = BIT(irqd_to_hwirq(d));
144 
145 	if (type & IRQ_TYPE_EDGE_RISING) {
146 		gb->irq_rising_edge |= bit;
147 		spacemit_gpio_write(gb, SPACEMIT_GSRER, bit);
148 	} else {
149 		gb->irq_rising_edge &= ~bit;
150 		spacemit_gpio_write(gb, SPACEMIT_GCRER, bit);
151 	}
152 
153 	if (type & IRQ_TYPE_EDGE_FALLING) {
154 		gb->irq_falling_edge |= bit;
155 		spacemit_gpio_write(gb, SPACEMIT_GSFER, bit);
156 	} else {
157 		gb->irq_falling_edge &= ~bit;
158 		spacemit_gpio_write(gb, SPACEMIT_GCFER, bit);
159 	}
160 
161 	return 0;
162 }
163 
164 static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p)
165 {
166 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data);
167 
168 	seq_printf(p, "%s-%d", dev_name(gb->chip.gc.parent), spacemit_gpio_bank_index(gb));
169 }
170 
171 static struct irq_chip spacemit_gpio_chip = {
172 	.name		= "k1-gpio-irqchip",
173 	.irq_ack	= spacemit_gpio_irq_ack,
174 	.irq_mask	= spacemit_gpio_irq_mask,
175 	.irq_unmask	= spacemit_gpio_irq_unmask,
176 	.irq_set_type	= spacemit_gpio_irq_set_type,
177 	.irq_print_chip	= spacemit_gpio_irq_print_chip,
178 	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
179 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
180 };
181 
182 static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i)
183 {
184 	struct spacemit_gpio_bank *gb = gpiochip_get_data(gc);
185 	struct spacemit_gpio *sg = gb->sg;
186 
187 	if (i >= SPACEMIT_NR_BANKS)
188 		return false;
189 
190 	return (gc == &sg->sgb[i].chip.gc);
191 }
192 
193 static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
194 				  void __iomem *regs,
195 				  int index, int irq)
196 {
197 	struct spacemit_gpio_bank *gb = &sg->sgb[index];
198 	struct gpio_generic_chip_config config;
199 	struct gpio_chip *gc = &gb->chip.gc;
200 	struct device *dev = sg->dev;
201 	struct gpio_irq_chip *girq;
202 	void __iomem *dat, *set, *clr, *dirout;
203 	int ret;
204 
205 	gb->base = regs + sg->data->bank_offsets[index];
206 	gb->sg = sg;
207 
208 	dat	= gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPLR];
209 	set	= gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPSR];
210 	clr	= gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPCR];
211 	dirout	= gb->base + to_spacemit_gpio_regs(gb)[SPACEMIT_GPDR];
212 
213 	config = (struct gpio_generic_chip_config) {
214 		.dev = dev,
215 		.sz = 4,
216 		.dat = dat,
217 		.set = set,
218 		.clr = clr,
219 		.dirout = dirout,
220 		.flags = GPIO_GENERIC_UNREADABLE_REG_SET,
221 	};
222 
223 	/* This registers 32 GPIO lines per bank */
224 	ret = gpio_generic_chip_init(&gb->chip, &config);
225 	if (ret)
226 		return dev_err_probe(dev, ret, "failed to init gpio chip\n");
227 
228 	gc->label		= dev_name(dev);
229 	gc->request		= gpiochip_generic_request;
230 	gc->free		= gpiochip_generic_free;
231 	gc->set_config		= gpiochip_generic_config;
232 	gc->ngpio		= SPACEMIT_NR_GPIOS_PER_BANK;
233 	gc->base		= -1;
234 	gc->of_gpio_n_cells	= 3;
235 	gc->of_node_instance_match = spacemit_of_node_instance_match;
236 
237 	girq			= &gc->irq;
238 	girq->threaded		= true;
239 	girq->handler		= handle_simple_irq;
240 
241 	gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip);
242 
243 	/* Disable Interrupt */
244 	spacemit_gpio_write(gb, SPACEMIT_GAPMASK, 0);
245 	/* Disable Edge Detection Settings */
246 	spacemit_gpio_write(gb, SPACEMIT_GRER, 0x0);
247 	spacemit_gpio_write(gb, SPACEMIT_GFER, 0x0);
248 	/* Clear Interrupt */
249 	spacemit_gpio_write(gb, SPACEMIT_GCRER, 0xffffffff);
250 	spacemit_gpio_write(gb, SPACEMIT_GCFER, 0xffffffff);
251 
252 	ret = devm_request_threaded_irq(dev, irq, NULL,
253 					spacemit_gpio_irq_handler,
254 					IRQF_ONESHOT | IRQF_SHARED,
255 					gb->chip.gc.label, gb);
256 	if (ret < 0)
257 		return dev_err_probe(dev, ret, "failed to register IRQ\n");
258 
259 	ret = devm_gpiochip_add_data(dev, gc, gb);
260 	if (ret)
261 		return ret;
262 
263 	/* Distuingish IRQ domain, for selecting threecells mode */
264 	irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED);
265 
266 	return 0;
267 }
268 
269 static int spacemit_gpio_probe(struct platform_device *pdev)
270 {
271 	struct device *dev = &pdev->dev;
272 	struct spacemit_gpio *sg;
273 	struct clk *core_clk, *bus_clk;
274 	void __iomem *regs;
275 	int i, irq, ret;
276 
277 	sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL);
278 	if (!sg)
279 		return -ENOMEM;
280 
281 	sg->data = of_device_get_match_data(dev);
282 	if (!sg->data)
283 		return dev_err_probe(dev, -EINVAL, "No available compatible data.");
284 
285 	regs = devm_platform_ioremap_resource(pdev, 0);
286 	if (IS_ERR(regs))
287 		return PTR_ERR(regs);
288 
289 	irq = platform_get_irq(pdev, 0);
290 	if (irq < 0)
291 		return irq;
292 
293 	sg->dev	= dev;
294 
295 	core_clk = devm_clk_get_enabled(dev, "core");
296 	if (IS_ERR(core_clk))
297 		return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n");
298 
299 	bus_clk = devm_clk_get_enabled(dev, "bus");
300 	if (IS_ERR(bus_clk))
301 		return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n");
302 
303 	for (i = 0; i < SPACEMIT_NR_BANKS; i++) {
304 		ret = spacemit_gpio_add_bank(sg, regs, i, irq);
305 		if (ret)
306 			return ret;
307 	}
308 
309 	return 0;
310 }
311 
312 static const unsigned int spacemit_gpio_k1_offsets[] = {
313 	[SPACEMIT_GPLR] = 0x00,
314 	[SPACEMIT_GPDR] = 0x0c,
315 	[SPACEMIT_GPSR] = 0x18,
316 	[SPACEMIT_GPCR] = 0x24,
317 	[SPACEMIT_GRER] = 0x30,
318 	[SPACEMIT_GFER] = 0x3c,
319 	[SPACEMIT_GEDR] = 0x48,
320 	[SPACEMIT_GSDR] = 0x54,
321 	[SPACEMIT_GCDR] = 0x60,
322 	[SPACEMIT_GSRER] = 0x6c,
323 	[SPACEMIT_GCRER] = 0x78,
324 	[SPACEMIT_GSFER] = 0x84,
325 	[SPACEMIT_GCFER] = 0x90,
326 	[SPACEMIT_GAPMASK] = 0x9c,
327 	[SPACEMIT_GCPMASK] = 0xA8,
328 };
329 
330 static const unsigned int spacemit_gpio_k3_offsets[] = {
331 	[SPACEMIT_GPLR] = 0x0,
332 	[SPACEMIT_GPDR] = 0x4,
333 	[SPACEMIT_GPSR] = 0x8,
334 	[SPACEMIT_GPCR] = 0xc,
335 	[SPACEMIT_GRER] = 0x10,
336 	[SPACEMIT_GFER] = 0x14,
337 	[SPACEMIT_GEDR] = 0x18,
338 	[SPACEMIT_GSDR] = 0x1c,
339 	[SPACEMIT_GCDR] = 0x20,
340 	[SPACEMIT_GSRER] = 0x24,
341 	[SPACEMIT_GCRER] = 0x28,
342 	[SPACEMIT_GSFER] = 0x2c,
343 	[SPACEMIT_GCFER] = 0x30,
344 	[SPACEMIT_GAPMASK] = 0x34,
345 	[SPACEMIT_GCPMASK] = 0x38,
346 };
347 
348 static const struct spacemit_gpio_data k1_gpio_data = {
349 	.offsets = spacemit_gpio_k1_offsets,
350 	.bank_offsets = { 0x0, 0x4, 0x8, 0x100 },
351 };
352 
353 static const struct spacemit_gpio_data k3_gpio_data = {
354 	.offsets = spacemit_gpio_k3_offsets,
355 	.bank_offsets = { 0x0, 0x40, 0x80, 0x100 },
356 };
357 
358 static const struct of_device_id spacemit_gpio_dt_ids[] = {
359 	{ .compatible = "spacemit,k1-gpio", .data = &k1_gpio_data },
360 	{ .compatible = "spacemit,k3-gpio", .data = &k3_gpio_data },
361 	{ /* sentinel */ }
362 };
363 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
364 
365 static struct platform_driver spacemit_gpio_driver = {
366 	.probe		= spacemit_gpio_probe,
367 	.driver		= {
368 		.name	= "spacemit-gpio",
369 		.of_match_table = spacemit_gpio_dt_ids,
370 	},
371 };
372 module_platform_driver(spacemit_gpio_driver);
373 
374 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>");
375 MODULE_DESCRIPTION("GPIO driver for SpacemiT K1/K3 SoC");
376 MODULE_LICENSE("GPL");
377