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