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