xref: /linux/drivers/gpio/gpio-spacemit-k1.c (revision 68a052239fc4b351e961f698b824f7654a346091)
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 /* register offset */
19 #define SPACEMIT_GPLR		0x00 /* port level - R */
20 #define SPACEMIT_GPDR		0x0c /* port direction - R/W */
21 #define SPACEMIT_GPSR		0x18 /* port set - W */
22 #define SPACEMIT_GPCR		0x24 /* port clear - W */
23 #define SPACEMIT_GRER		0x30 /* port rising edge R/W */
24 #define SPACEMIT_GFER		0x3c /* port falling edge R/W */
25 #define SPACEMIT_GEDR		0x48 /* edge detect status - R/W1C */
26 #define SPACEMIT_GSDR		0x54 /* (set) direction - W */
27 #define SPACEMIT_GCDR		0x60 /* (clear) direction - W */
28 #define SPACEMIT_GSRER		0x6c /* (set) rising edge detect enable - W */
29 #define SPACEMIT_GCRER		0x78 /* (clear) rising edge detect enable - W */
30 #define SPACEMIT_GSFER		0x84 /* (set) falling edge detect enable - W */
31 #define SPACEMIT_GCFER		0x90 /* (clear) falling edge detect enable - W */
32 #define SPACEMIT_GAPMASK	0x9c /* interrupt mask , 0 disable, 1 enable - R/W */
33 
34 #define SPACEMIT_NR_BANKS		4
35 #define SPACEMIT_NR_GPIOS_PER_BANK	32
36 
37 #define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc)
38 
39 struct spacemit_gpio;
40 
41 struct spacemit_gpio_bank {
42 	struct gpio_generic_chip chip;
43 	struct spacemit_gpio *sg;
44 	void __iomem *base;
45 	u32 irq_mask;
46 	u32 irq_rising_edge;
47 	u32 irq_falling_edge;
48 };
49 
50 struct spacemit_gpio {
51 	struct device *dev;
52 	struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS];
53 };
54 
55 static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb)
56 {
57 	return (u32)(gb - gb->sg->sgb);
58 }
59 
60 static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id)
61 {
62 	struct spacemit_gpio_bank *gb = dev_id;
63 	unsigned long pending;
64 	u32 n, gedr;
65 
66 	gedr = readl(gb->base + SPACEMIT_GEDR);
67 	if (!gedr)
68 		return IRQ_NONE;
69 	writel(gedr, gb->base + SPACEMIT_GEDR);
70 
71 	pending = gedr & gb->irq_mask;
72 	if (!pending)
73 		return IRQ_NONE;
74 
75 	for_each_set_bit(n, &pending, BITS_PER_LONG)
76 		handle_nested_irq(irq_find_mapping(gb->chip.gc.irq.domain, n));
77 
78 	return IRQ_HANDLED;
79 }
80 
81 static void spacemit_gpio_irq_ack(struct irq_data *d)
82 {
83 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
84 
85 	writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR);
86 }
87 
88 static void spacemit_gpio_irq_mask(struct irq_data *d)
89 {
90 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
91 	u32 bit = BIT(irqd_to_hwirq(d));
92 
93 	gb->irq_mask &= ~bit;
94 	writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
95 
96 	if (bit & gb->irq_rising_edge)
97 		writel(bit, gb->base + SPACEMIT_GCRER);
98 
99 	if (bit & gb->irq_falling_edge)
100 		writel(bit, gb->base + SPACEMIT_GCFER);
101 }
102 
103 static void spacemit_gpio_irq_unmask(struct irq_data *d)
104 {
105 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
106 	u32 bit = BIT(irqd_to_hwirq(d));
107 
108 	gb->irq_mask |= bit;
109 
110 	if (bit & gb->irq_rising_edge)
111 		writel(bit, gb->base + SPACEMIT_GSRER);
112 
113 	if (bit & gb->irq_falling_edge)
114 		writel(bit, gb->base + SPACEMIT_GSFER);
115 
116 	writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK);
117 }
118 
119 static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type)
120 {
121 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d);
122 	u32 bit = BIT(irqd_to_hwirq(d));
123 
124 	if (type & IRQ_TYPE_EDGE_RISING) {
125 		gb->irq_rising_edge |= bit;
126 		writel(bit, gb->base + SPACEMIT_GSRER);
127 	} else {
128 		gb->irq_rising_edge &= ~bit;
129 		writel(bit, gb->base + SPACEMIT_GCRER);
130 	}
131 
132 	if (type & IRQ_TYPE_EDGE_FALLING) {
133 		gb->irq_falling_edge |= bit;
134 		writel(bit, gb->base + SPACEMIT_GSFER);
135 	} else {
136 		gb->irq_falling_edge &= ~bit;
137 		writel(bit, gb->base + SPACEMIT_GCFER);
138 	}
139 
140 	return 0;
141 }
142 
143 static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p)
144 {
145 	struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data);
146 
147 	seq_printf(p, "%s-%d", dev_name(gb->chip.gc.parent), spacemit_gpio_bank_index(gb));
148 }
149 
150 static struct irq_chip spacemit_gpio_chip = {
151 	.name		= "k1-gpio-irqchip",
152 	.irq_ack	= spacemit_gpio_irq_ack,
153 	.irq_mask	= spacemit_gpio_irq_mask,
154 	.irq_unmask	= spacemit_gpio_irq_unmask,
155 	.irq_set_type	= spacemit_gpio_irq_set_type,
156 	.irq_print_chip	= spacemit_gpio_irq_print_chip,
157 	.flags		= IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
158 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
159 };
160 
161 static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i)
162 {
163 	struct spacemit_gpio_bank *gb = gpiochip_get_data(gc);
164 	struct spacemit_gpio *sg = gb->sg;
165 
166 	if (i >= SPACEMIT_NR_BANKS)
167 		return false;
168 
169 	return (gc == &sg->sgb[i].chip.gc);
170 }
171 
172 static int spacemit_gpio_add_bank(struct spacemit_gpio *sg,
173 				  void __iomem *regs,
174 				  int index, int irq)
175 {
176 	struct spacemit_gpio_bank *gb = &sg->sgb[index];
177 	struct gpio_generic_chip_config config;
178 	struct gpio_chip *gc = &gb->chip.gc;
179 	struct device *dev = sg->dev;
180 	struct gpio_irq_chip *girq;
181 	void __iomem *dat, *set, *clr, *dirin, *dirout;
182 	int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 };
183 
184 	gb->base = regs + bank_base[index];
185 
186 	dat	= gb->base + SPACEMIT_GPLR;
187 	set	= gb->base + SPACEMIT_GPSR;
188 	clr	= gb->base + SPACEMIT_GPCR;
189 	dirin	= gb->base + SPACEMIT_GCDR;
190 	dirout	= gb->base + SPACEMIT_GSDR;
191 
192 	config = (struct gpio_generic_chip_config) {
193 		.dev = dev,
194 		.sz = 4,
195 		.dat = dat,
196 		.set = set,
197 		.clr = clr,
198 		.dirout = dirout,
199 		.dirin = dirin,
200 		.flags = GPIO_GENERIC_UNREADABLE_REG_SET |
201 			 GPIO_GENERIC_UNREADABLE_REG_DIR,
202 	};
203 
204 	/* This registers 32 GPIO lines per bank */
205 	ret = gpio_generic_chip_init(&gb->chip, &config);
206 	if (ret)
207 		return dev_err_probe(dev, ret, "failed to init gpio chip\n");
208 
209 	gb->sg = sg;
210 
211 	gc->label		= dev_name(dev);
212 	gc->request		= gpiochip_generic_request;
213 	gc->free		= gpiochip_generic_free;
214 	gc->ngpio		= SPACEMIT_NR_GPIOS_PER_BANK;
215 	gc->base		= -1;
216 	gc->of_gpio_n_cells	= 3;
217 	gc->of_node_instance_match = spacemit_of_node_instance_match;
218 
219 	girq			= &gc->irq;
220 	girq->threaded		= true;
221 	girq->handler		= handle_simple_irq;
222 
223 	gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip);
224 
225 	/* Disable Interrupt */
226 	writel(0, gb->base + SPACEMIT_GAPMASK);
227 	/* Disable Edge Detection Settings */
228 	writel(0x0, gb->base + SPACEMIT_GRER);
229 	writel(0x0, gb->base + SPACEMIT_GFER);
230 	/* Clear Interrupt */
231 	writel(0xffffffff, gb->base + SPACEMIT_GCRER);
232 	writel(0xffffffff, gb->base + SPACEMIT_GCFER);
233 
234 	ret = devm_request_threaded_irq(dev, irq, NULL,
235 					spacemit_gpio_irq_handler,
236 					IRQF_ONESHOT | IRQF_SHARED,
237 					gb->chip.gc.label, gb);
238 	if (ret < 0)
239 		return dev_err_probe(dev, ret, "failed to register IRQ\n");
240 
241 	ret = devm_gpiochip_add_data(dev, gc, gb);
242 	if (ret)
243 		return ret;
244 
245 	/* Distuingish IRQ domain, for selecting threecells mode */
246 	irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED);
247 
248 	return 0;
249 }
250 
251 static int spacemit_gpio_probe(struct platform_device *pdev)
252 {
253 	struct device *dev = &pdev->dev;
254 	struct spacemit_gpio *sg;
255 	struct clk *core_clk, *bus_clk;
256 	void __iomem *regs;
257 	int i, irq, ret;
258 
259 	sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL);
260 	if (!sg)
261 		return -ENOMEM;
262 
263 	regs = devm_platform_ioremap_resource(pdev, 0);
264 	if (IS_ERR(regs))
265 		return PTR_ERR(regs);
266 
267 	irq = platform_get_irq(pdev, 0);
268 	if (irq < 0)
269 		return irq;
270 
271 	sg->dev	= dev;
272 
273 	core_clk = devm_clk_get_enabled(dev, "core");
274 	if (IS_ERR(core_clk))
275 		return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n");
276 
277 	bus_clk = devm_clk_get_enabled(dev, "bus");
278 	if (IS_ERR(bus_clk))
279 		return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n");
280 
281 	for (i = 0; i < SPACEMIT_NR_BANKS; i++) {
282 		ret = spacemit_gpio_add_bank(sg, regs, i, irq);
283 		if (ret)
284 			return ret;
285 	}
286 
287 	return 0;
288 }
289 
290 static const struct of_device_id spacemit_gpio_dt_ids[] = {
291 	{ .compatible = "spacemit,k1-gpio" },
292 	{ /* sentinel */ }
293 };
294 MODULE_DEVICE_TABLE(of, spacemit_gpio_dt_ids);
295 
296 static struct platform_driver spacemit_gpio_driver = {
297 	.probe		= spacemit_gpio_probe,
298 	.driver		= {
299 		.name	= "k1-gpio",
300 		.of_match_table = spacemit_gpio_dt_ids,
301 	},
302 };
303 module_platform_driver(spacemit_gpio_driver);
304 
305 MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>");
306 MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC");
307 MODULE_LICENSE("GPL");
308