Skip to content

Commit 03c146c

Browse files
ziyao233Bartosz Golaszewski
authored andcommitted
gpio: loongson-64bit: Add support for Loongson-2K0300 SoC
This controller's input and output logic is similar to previous generations of SoCs. Additionally, it's capable of interrupt masking, and could be configured to detect levels and edges, and is supplied with a distinct reset signal. The interrupt functionality is implemented through an irqchip, whose operations are written with previous generation SoCs in mind and could be reused. Since all Loongson SoCs with similar interrupt capability (LS2K1500, LS2K2000) support byte-control mode, these operations are for byte-control mode only for simplicity. Signed-off-by: Yao Zi <ziyao@disroot.org> Reviewed-by: Huacai Chen <chenhuacai@loongson.cn> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Link: https://lore.kernel.org/r/20250904013438.2405-3-ziyao@disroot.org Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
1 parent 084d01a commit 03c146c

2 files changed

Lines changed: 183 additions & 7 deletions

File tree

drivers/gpio/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ config GPIO_LOONGSON_64BIT
436436
depends on LOONGARCH || COMPILE_TEST
437437
depends on OF_GPIO
438438
select GPIO_GENERIC
439+
select GPIOLIB_IRQCHIP
439440
help
440441
Say yes here to support the GPIO functionality of a number of
441442
Loongson series of chips. The Loongson GPIO controller supports

drivers/gpio/gpio-loongson-64bit.c

Lines changed: 182 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77

88
#include <linux/kernel.h>
99
#include <linux/init.h>
10+
#include <linux/irq.h>
11+
#include <linux/irqdesc.h>
1012
#include <linux/module.h>
1113
#include <linux/spinlock.h>
1214
#include <linux/err.h>
1315
#include <linux/gpio/driver.h>
1416
#include <linux/gpio/generic.h>
1517
#include <linux/platform_device.h>
1618
#include <linux/bitops.h>
19+
#include <linux/reset.h>
1720
#include <asm/types.h>
1821

1922
enum loongson_gpio_mode {
@@ -28,6 +31,14 @@ struct loongson_gpio_chip_data {
2831
unsigned int out_offset;
2932
unsigned int in_offset;
3033
unsigned int inten_offset;
34+
unsigned int intpol_offset;
35+
unsigned int intedge_offset;
36+
unsigned int intclr_offset;
37+
unsigned int intsts_offset;
38+
unsigned int intdual_offset;
39+
unsigned int intr_num;
40+
irq_flow_handler_t irq_handler;
41+
const struct irq_chip *girqchip;
3142
};
3243

3344
struct loongson_gpio_chip {
@@ -137,7 +148,140 @@ static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
137148
return platform_get_irq(pdev, offset);
138149
}
139150

140-
static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgpio,
151+
static void loongson_gpio_irq_ack(struct irq_data *data)
152+
{
153+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
154+
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
155+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
156+
157+
writeb(0x1, lgpio->reg_base + lgpio->chip_data->intclr_offset + hwirq);
158+
}
159+
160+
static void loongson_gpio_irq_mask(struct irq_data *data)
161+
{
162+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
163+
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
164+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
165+
166+
writeb(0x0, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
167+
}
168+
169+
static void loongson_gpio_irq_unmask(struct irq_data *data)
170+
{
171+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
172+
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
173+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
174+
175+
writeb(0x1, lgpio->reg_base + lgpio->chip_data->inten_offset + hwirq);
176+
}
177+
178+
static int loongson_gpio_irq_set_type(struct irq_data *data, unsigned int type)
179+
{
180+
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
181+
struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
182+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
183+
u8 pol = 0, edge = 0, dual = 0;
184+
185+
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
186+
edge = 1;
187+
dual = 1;
188+
irq_set_handler_locked(data, handle_edge_irq);
189+
} else {
190+
switch (type) {
191+
case IRQ_TYPE_LEVEL_HIGH:
192+
pol = 1;
193+
fallthrough;
194+
case IRQ_TYPE_LEVEL_LOW:
195+
irq_set_handler_locked(data, handle_level_irq);
196+
break;
197+
198+
case IRQ_TYPE_EDGE_RISING:
199+
pol = 1;
200+
fallthrough;
201+
case IRQ_TYPE_EDGE_FALLING:
202+
edge = 1;
203+
irq_set_handler_locked(data, handle_edge_irq);
204+
break;
205+
206+
default:
207+
return -EINVAL;
208+
};
209+
}
210+
211+
writeb(pol, lgpio->reg_base + lgpio->chip_data->intpol_offset + hwirq);
212+
writeb(edge, lgpio->reg_base + lgpio->chip_data->intedge_offset + hwirq);
213+
writeb(dual, lgpio->reg_base + lgpio->chip_data->intdual_offset + hwirq);
214+
215+
return 0;
216+
}
217+
218+
static void loongson_gpio_ls2k0300_irq_handler(struct irq_desc *desc)
219+
{
220+
struct loongson_gpio_chip *lgpio = irq_desc_get_handler_data(desc);
221+
struct irq_chip *girqchip = irq_desc_get_chip(desc);
222+
int i;
223+
224+
chained_irq_enter(girqchip, desc);
225+
226+
for (i = 0; i < lgpio->chip.gc.ngpio; i++) {
227+
/*
228+
* For the GPIO controller of LS2K0300, interrupts status bits
229+
* may be wrongly set even if the corresponding interrupt is
230+
* disabled. Thus interrupt enable bits are checked along with
231+
* status bits to detect interrupts reliably.
232+
*/
233+
if (readb(lgpio->reg_base + lgpio->chip_data->intsts_offset + i) &&
234+
readb(lgpio->reg_base + lgpio->chip_data->inten_offset + i))
235+
generic_handle_domain_irq(lgpio->chip.gc.irq.domain, i);
236+
}
237+
238+
chained_irq_exit(girqchip, desc);
239+
}
240+
241+
static const struct irq_chip loongson_gpio_ls2k0300_irqchip = {
242+
.irq_ack = loongson_gpio_irq_ack,
243+
.irq_mask = loongson_gpio_irq_mask,
244+
.irq_unmask = loongson_gpio_irq_unmask,
245+
.irq_set_type = loongson_gpio_irq_set_type,
246+
.flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE,
247+
GPIOCHIP_IRQ_RESOURCE_HELPERS,
248+
};
249+
250+
static int loongson_gpio_init_irqchip(struct platform_device *pdev,
251+
struct loongson_gpio_chip *lgpio)
252+
{
253+
const struct loongson_gpio_chip_data *data = lgpio->chip_data;
254+
struct gpio_chip *chip = &lgpio->chip.gc;
255+
int i;
256+
257+
chip->irq.default_type = IRQ_TYPE_NONE;
258+
chip->irq.handler = handle_bad_irq;
259+
chip->irq.parent_handler = data->irq_handler;
260+
chip->irq.parent_handler_data = lgpio;
261+
gpio_irq_chip_set_chip(&chip->irq, data->girqchip);
262+
263+
chip->irq.num_parents = data->intr_num;
264+
chip->irq.parents = devm_kcalloc(&pdev->dev, data->intr_num,
265+
sizeof(*chip->irq.parents), GFP_KERNEL);
266+
if (!chip->parent)
267+
return -ENOMEM;
268+
269+
for (i = 0; i < data->intr_num; i++) {
270+
chip->irq.parents[i] = platform_get_irq(pdev, i);
271+
if (chip->irq.parents[i] < 0)
272+
return dev_err_probe(&pdev->dev, chip->irq.parents[i],
273+
"failed to get IRQ %d\n", i);
274+
}
275+
276+
for (i = 0; i < data->intr_num; i++) {
277+
writeb(0x0, lgpio->reg_base + data->inten_offset + i);
278+
writeb(0x1, lgpio->reg_base + data->intclr_offset + i);
279+
}
280+
281+
return 0;
282+
}
283+
284+
static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio_chip *lgpio,
141285
void __iomem *reg_base)
142286
{
143287
struct gpio_generic_chip_config config;
@@ -146,7 +290,7 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp
146290
lgpio->reg_base = reg_base;
147291
if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
148292
config = (typeof(config)){
149-
.dev = dev,
293+
.dev = &pdev->dev,
150294
.sz = 8,
151295
.dat = lgpio->reg_base + lgpio->chip_data->in_offset,
152296
.set = lgpio->reg_base + lgpio->chip_data->out_offset,
@@ -155,7 +299,7 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp
155299

156300
ret = gpio_generic_chip_init(&lgpio->chip, &config);
157301
if (ret) {
158-
dev_err(dev, "unable to init generic GPIO\n");
302+
dev_err(&pdev->dev, "unable to init generic GPIO\n");
159303
return ret;
160304
}
161305
} else {
@@ -164,23 +308,29 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp
164308
lgpio->chip.gc.get_direction = loongson_gpio_get_direction;
165309
lgpio->chip.gc.direction_output = loongson_gpio_direction_output;
166310
lgpio->chip.gc.set = loongson_gpio_set;
167-
lgpio->chip.gc.parent = dev;
311+
lgpio->chip.gc.parent = &pdev->dev;
168312
spin_lock_init(&lgpio->lock);
169313
}
170314

171315
lgpio->chip.gc.label = lgpio->chip_data->label;
172316
lgpio->chip.gc.can_sleep = false;
173-
if (lgpio->chip_data->inten_offset)
317+
if (lgpio->chip_data->girqchip) {
318+
ret = loongson_gpio_init_irqchip(pdev, lgpio);
319+
if (ret)
320+
return dev_err_probe(&pdev->dev, ret, "failed to initialize irqchip\n");
321+
} else if (lgpio->chip_data->inten_offset) {
174322
lgpio->chip.gc.to_irq = loongson_gpio_to_irq;
323+
}
175324

176-
return devm_gpiochip_add_data(dev, &lgpio->chip.gc, lgpio);
325+
return devm_gpiochip_add_data(&pdev->dev, &lgpio->chip.gc, lgpio);
177326
}
178327

179328
static int loongson_gpio_probe(struct platform_device *pdev)
180329
{
181330
void __iomem *reg_base;
182331
struct loongson_gpio_chip *lgpio;
183332
struct device *dev = &pdev->dev;
333+
struct reset_control *rst;
184334

185335
lgpio = devm_kzalloc(dev, sizeof(*lgpio), GFP_KERNEL);
186336
if (!lgpio)
@@ -192,7 +342,11 @@ static int loongson_gpio_probe(struct platform_device *pdev)
192342
if (IS_ERR(reg_base))
193343
return PTR_ERR(reg_base);
194344

195-
return loongson_gpio_init(dev, lgpio, reg_base);
345+
rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
346+
if (IS_ERR(rst))
347+
return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get reset control\n");
348+
349+
return loongson_gpio_init(pdev, lgpio, reg_base);
196350
}
197351

198352
static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
@@ -204,6 +358,23 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
204358
.inten_offset = 0x30,
205359
};
206360

361+
static const struct loongson_gpio_chip_data loongson_gpio_ls2k0300_data = {
362+
.label = "ls2k0300_gpio",
363+
.mode = BYTE_CTRL_MODE,
364+
.conf_offset = 0x800,
365+
.in_offset = 0xa00,
366+
.out_offset = 0x900,
367+
.inten_offset = 0xb00,
368+
.intpol_offset = 0xc00,
369+
.intedge_offset = 0xd00,
370+
.intclr_offset = 0xe00,
371+
.intsts_offset = 0xf00,
372+
.intdual_offset = 0xf80,
373+
.intr_num = 7,
374+
.irq_handler = loongson_gpio_ls2k0300_irq_handler,
375+
.girqchip = &loongson_gpio_ls2k0300_irqchip,
376+
};
377+
207378
static const struct loongson_gpio_chip_data loongson_gpio_ls2k0500_data0 = {
208379
.label = "ls2k0500_gpio",
209380
.mode = BIT_CTRL_MODE,
@@ -300,6 +471,10 @@ static const struct of_device_id loongson_gpio_of_match[] = {
300471
.compatible = "loongson,ls2k-gpio",
301472
.data = &loongson_gpio_ls2k_data,
302473
},
474+
{
475+
.compatible = "loongson,ls2k0300-gpio",
476+
.data = &loongson_gpio_ls2k0300_data,
477+
},
303478
{
304479
.compatible = "loongson,ls2k0500-gpio0",
305480
.data = &loongson_gpio_ls2k0500_data0,

0 commit comments

Comments
 (0)