diff mbox series

[v10,1/2] gpio: loongson: add gpio driver support

Message ID 20221201123220.7893-1-zhuyinbo@loongson.cn
State New
Headers show
Series [v10,1/2] gpio: loongson: add gpio driver support | expand

Commit Message

Yinbo Zhu Dec. 1, 2022, 12:32 p.m. UTC
The Loongson platforms GPIO controller contains 60 GPIO pins in total,
4 of which are dedicated GPIO pins, and the remaining 56 are reused
with other functions. Each GPIO can set input/output and has the
interrupt capability.

This driver added support for Loongson GPIO controller and support to
use DTS or ACPI to descibe GPIO device resources.

Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn>
Signed-off-by: Liu Peibao <liupeibao@loongson.cn>
Signed-off-by: Juxin Gao <gaojuxin@loongson.cn>
Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
---
Change in v10:
		1. Used unsigned int to replace int in loongson_gpio_chip_data for
		   offset.
		2. Used chip_data replace p_data.
		3. Save a line by initializing the variable when declaring it.
		4. Add a static inline to_loongson_gpio_chip() helper that wraps the
		   container_of() expansion and only needs a single argument.
		5. Return devm_gpiochip_add_data directly in loongson_gpio_init. 
		6. Drop __maybe_unused in loongson_gpio_init.
Change in v9:
		1. Fixup the xxx_device_id data field naming method. 
		2. Remove the "offset >= chip->ngpio" judgement in loongson_gpio_to_irq.
Change in v8:
		1. Return the loongson_gpio_init function directly in probe.
Change in v7:
		1. Add the gpio irqchip support.
		2. Replace __set_direction with loongson_commit_direction.
		3. Replace __set_level with loongson_commit_level. 
		4. Remove the parser of gsi_idx_map. 
		5. Add the support_irq flag.
		6. Replace string platform_data with chip_data.
		7. Remove the loongson_gpio_request.
Change in v6:
		1. Remove the bit mode keep byte mode in all function except 
		   loongson_gpio_init.
		2. Use bgpio_init replace bit mode. 
		3. Implement the function loongson_gpio_get_direction for byte mode.
		4. Set ngpios after call bgpio_init. 
		5. Use gpio-loongson-64bit.c as driver filename.
		6. Ignore that loongson legacy drvier and remove the patch about 
		   "gpio: loongson2ef: move driver to original location". 
Change in v5:
		1. Move legacy gpio driver to proper location.
		2. Remove the "gpio_base".
		3. Add of_device_id and acpi_device_id data field for platform
		   data. 
		4. Remove the ACPI_PTR().
		5. Remove the gpio label judgement logic and use mode instead.  
		6. Drop platform_loongson_gpio_get_props.
		7. Using devres for all resource. 
		8. Remove the loongson_gpio_remove.
		9. Remove the unmatched print information.
		10. Remove the loongson_gpio_exit.
Change in v4:
		1. Fixup name spelling about Signed-off-by. 
		2. Drop "series" here and everywhere else.
		3. Fixup the copyright in driver.
		4. Drop the "else" in loongson_gpio_request.
		5. Use trinocular operation replace the related logic.
		6. Remove lable judgement in context about "lgpio->chip.to_irq = 
		   loongson_gpio_to_irq"
		7. Use dev_err replace pr_err in probe.
		8. Make legacy platform_data should be left out of this patch.
		9. Remove the mips config in gpio Kconfig.
Change in v3:
		1. Move the gpio platform data struct from arch/ into include/linux/
		   platform_data/.
		2. Replace platform_gpio_data with loongson_gpio_platform_data in .c.
		3. Add maintainer in MAINTAINERS file for include/linux/platform_data/
		   gpio-loongson.h and gpio-loongson.c
Change in v2:
		1. Fixup of_loongson_gpio_get_props and remove the parse logic about
	           "loongson,conf_offset", "loongson,out_offset", "loongson,in_offset",
		   "loongson,gpio_base", "loongson,support_irq" then kernel driver will
		   initial them that depend compatible except "loongson,gpio_base".

 MAINTAINERS                        |   6 +
 drivers/gpio/Kconfig               |  12 +
 drivers/gpio/Makefile              |   1 +
 drivers/gpio/gpio-loongson-64bit.c | 370 +++++++++++++++++++++++++++++
 4 files changed, 389 insertions(+)
 create mode 100644 drivers/gpio/gpio-loongson-64bit.c

Comments

Bartosz Golaszewski Dec. 2, 2022, 4:11 p.m. UTC | #1
On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
>
> The Loongson platforms GPIO controller contains 60 GPIO pins in total,
> 4 of which are dedicated GPIO pins, and the remaining 56 are reused
> with other functions. Each GPIO can set input/output and has the
> interrupt capability.
>
> This driver added support for Loongson GPIO controller and support to
> use DTS or ACPI to descibe GPIO device resources.
>
> Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
> Signed-off-by: Hongchen Zhang <zhanghongchen@loongson.cn>
> Signed-off-by: Liu Peibao <liupeibao@loongson.cn>
> Signed-off-by: Juxin Gao <gaojuxin@loongson.cn>
> Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
> ---

Both patches applied.

For the future: please reverse the order of patches and send dt
bindings before the driver code.

Bartosz
Linus Walleij Dec. 3, 2022, 10:03 a.m. UTC | #2
On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
(...)
> +config GPIO_LOONGSON_64BIT
> +       tristate "Loongson 64 bit GPIO support"
> +       depends on LOONGARCH || COMPILE_TEST
> +       select GPIO_GENERIC
> +       select GPIOLIB_IRQCHIP

If the kernelbots start complaining you might need to add a:

select IRQ_DOMAIN_HIERARCHY

here.

Yours,
Linus Walleij
Yinbo Zhu Dec. 3, 2022, 10:53 a.m. UTC | #3
在 2022/12/3 18:03, Linus Walleij 写道:
> On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
> (...)
>> +config GPIO_LOONGSON_64BIT
>> +       tristate "Loongson 64 bit GPIO support"
>> +       depends on LOONGARCH || COMPILE_TEST
>> +       select GPIO_GENERIC
>> +       select GPIOLIB_IRQCHIP
> If the kernelbots start complaining you might need to add a:
>
> select IRQ_DOMAIN_HIERARCHY
>
> here.
>
> Yours,
> Linus Walleij

Bartosz had merged my patch and I will add a change on top of the 
existing series.

Thanks.
Andy Shevchenko Dec. 3, 2022, 3:27 p.m. UTC | #4
On Sat, Dec 03, 2022 at 06:53:20PM +0800, Yinbo Zhu wrote:
> 在 2022/12/3 18:03, Linus Walleij 写道:
> > On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
> > > +config GPIO_LOONGSON_64BIT
> > > +       tristate "Loongson 64 bit GPIO support"
> > > +       depends on LOONGARCH || COMPILE_TEST
> > > +       select GPIO_GENERIC
> > > +       select GPIOLIB_IRQCHIP
> > If the kernelbots start complaining you might need to add a:
> > 
> > select IRQ_DOMAIN_HIERARCHY
> > 
> > here.
> 
> Bartosz had merged my patch and I will add a change on top of the existing
> series.

I don;'t know why he had done that, the driver requires a lot of cleanups,
e.g. why it uses acpi.h, what that "nice" container of the platform device for.
Andy Shevchenko Dec. 3, 2022, 3:41 p.m. UTC | #5
On Sat, Dec 03, 2022 at 05:27:31PM +0200, Andy Shevchenko wrote:
> On Sat, Dec 03, 2022 at 06:53:20PM +0800, Yinbo Zhu wrote:
> > 在 2022/12/3 18:03, Linus Walleij 写道:
> > > On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
> > > > +config GPIO_LOONGSON_64BIT
> > > > +       tristate "Loongson 64 bit GPIO support"
> > > > +       depends on LOONGARCH || COMPILE_TEST
> > > > +       select GPIO_GENERIC
> > > > +       select GPIOLIB_IRQCHIP
> > > If the kernelbots start complaining you might need to add a:
> > > 
> > > select IRQ_DOMAIN_HIERARCHY
> > > 
> > > here.
> > 
> > Bartosz had merged my patch and I will add a change on top of the existing
> > series.
> 
> I don;'t know why he had done that, the driver requires a lot of cleanups,
> e.g. why it uses acpi.h, what that "nice" container of the platform device for.

+ we have macros for get_direction() returned values and so on.
Bartosz Golaszewski Dec. 3, 2022, 5:45 p.m. UTC | #6
On Sat, Dec 3, 2022 at 4:41 PM Andy Shevchenko
<andriy.shevchenko@intel.com> wrote:
>
> On Sat, Dec 03, 2022 at 05:27:31PM +0200, Andy Shevchenko wrote:
> > On Sat, Dec 03, 2022 at 06:53:20PM +0800, Yinbo Zhu wrote:
> > > 在 2022/12/3 18:03, Linus Walleij 写道:
> > > > On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
> > > > > +config GPIO_LOONGSON_64BIT
> > > > > +       tristate "Loongson 64 bit GPIO support"
> > > > > +       depends on LOONGARCH || COMPILE_TEST
> > > > > +       select GPIO_GENERIC
> > > > > +       select GPIOLIB_IRQCHIP
> > > > If the kernelbots start complaining you might need to add a:
> > > >
> > > > select IRQ_DOMAIN_HIERARCHY
> > > >
> > > > here.
> > >
> > > Bartosz had merged my patch and I will add a change on top of the existing
> > > series.
> >
> > I don;'t know why he had done that, the driver requires a lot of cleanups,
> > e.g. why it uses acpi.h, what that "nice" container of the platform device for.
>
> + we have macros for get_direction() returned values and so on.
>

You'll have the chance to review Yinbo's patches Andy because the
driver fails to build with current next. I didn't notice the code
references of_nodes even though they're gone now from struct
gpio_chip. I need to back it out and let's get back to it for the next
release.

Bartosz
Yinbo Zhu Dec. 5, 2022, 6:04 a.m. UTC | #7
在 2022/12/3 23:41, Andy Shevchenko 写道:
> On Sat, Dec 03, 2022 at 05:27:31PM +0200, Andy Shevchenko wrote:
>> On Sat, Dec 03, 2022 at 06:53:20PM +0800, Yinbo Zhu wrote:
>>> 在 2022/12/3 18:03, Linus Walleij 写道:
>>>> On Thu, Dec 1, 2022 at 1:33 PM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
>>>>> +config GPIO_LOONGSON_64BIT
>>>>> +       tristate "Loongson 64 bit GPIO support"
>>>>> +       depends on LOONGARCH || COMPILE_TEST
>>>>> +       select GPIO_GENERIC
>>>>> +       select GPIOLIB_IRQCHIP
>>>> If the kernelbots start complaining you might need to add a:
>>>>
>>>> select IRQ_DOMAIN_HIERARCHY
>>>>
>>>> here.
>>> Bartosz had merged my patch and I will add a change on top of the existing
>>> series.
>> I don;'t know why he had done that, the driver requires a lot of cleanups,
>> e.g. why it uses acpi.h, what that "nice" container of the platform device for.

I just compile it that it is still okay when remove acpi.h, so I will 
remove it in v11.

and, I'm afraid I didn't catch your meaning about "

what that "nice" container of the platform device for."


you said is for following code ?
144         struct platform_device *pdev =
145                 container_of(chip->parent, struct platform_device, dev);

> + we have macros for get_direction() returned values and so on.
okay, I will use that macros in v11.
Andy Shevchenko Dec. 5, 2022, 12:53 p.m. UTC | #8
On Mon, Dec 05, 2022 at 02:04:09PM +0800, Yinbo Zhu wrote:
> 在 2022/12/3 23:41, Andy Shevchenko 写道:

...

> I just compile it that it is still okay when remove acpi.h, so I will remove
> it in v11.

You need to address all review comments. I don't remember if I reviewed this,
but there are a lot of contribution from your side to the different subsystems
where I have commented on your code and you can check those reviews because
some of the remarks can be applied to this contribution as well.

Nevertheless, please Cc me in your v11 when you consider it will be ready.
Note, you have approx. month now to make it better. Of course you can
send a version to review before that.

...

> and, I'm afraid I didn't catch your meaning about "
> 
> what that "nice" container of the platform device for."
> 
> you said is for following code ?
> 144         struct platform_device *pdev =
> 145                 container_of(chip->parent, struct platform_device, dev);

Have you seen to_platform_device() macro?
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 8bea25ac6196..2203d90bb2cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12165,6 +12165,12 @@  S:	Maintained
 F:	Documentation/devicetree/bindings/pinctrl/loongson,ls2k-pinctrl.yaml
 F:	drivers/pinctrl/pinctrl-loongson2.c
 
+LOONGSON GPIO DRIVER
+M:	Yinbo Zhu <zhuyinbo@loongson.cn>
+L:	linux-gpio@vger.kernel.org
+S:	Maintained
+F:	drivers/gpio/gpio-loongson-64bit.c
+
 LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
 M:	Sathya Prakash <sathya.prakash@broadcom.com>
 M:	Sreekanth Reddy <sreekanth.reddy@broadcom.com>
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ec7cfd4f52b1..55b7c5bae4aa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -390,6 +390,18 @@  config GPIO_LOONGSON
 	help
 	  Driver for GPIO functionality on Loongson-2F/3A/3B processors.
 
+config GPIO_LOONGSON_64BIT
+	tristate "Loongson 64 bit GPIO support"
+	depends on LOONGARCH || COMPILE_TEST
+	select GPIO_GENERIC
+	select GPIOLIB_IRQCHIP
+	help
+	  Say yes here to support the GPIO functionality of a number of
+	  Loongson series of chips. The Loongson GPIO controller supports
+	  up to 60 GPIOS in total, 4 of which are dedicated GPIO pins, and
+	  the remaining 56 are reused with other functions, with edge or
+	  level triggered interrupts.
+
 config GPIO_LPC18XX
 	tristate "NXP LPC18XX/43XX GPIO support"
 	default y if ARCH_LPC18XX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 010587025fc8..8223d2f330c5 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -81,6 +81,7 @@  obj-$(CONFIG_GPIO_LATCH)		+= gpio-latch.o
 obj-$(CONFIG_GPIO_LOGICVC)		+= gpio-logicvc.o
 obj-$(CONFIG_GPIO_LOONGSON1)		+= gpio-loongson1.o
 obj-$(CONFIG_GPIO_LOONGSON)		+= gpio-loongson.o
+obj-$(CONFIG_GPIO_LOONGSON_64BIT)	+= gpio-loongson-64bit.o
 obj-$(CONFIG_GPIO_LP3943)		+= gpio-lp3943.o
 obj-$(CONFIG_GPIO_LP873X)		+= gpio-lp873x.o
 obj-$(CONFIG_GPIO_LP87565)		+= gpio-lp87565.o
diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c
new file mode 100644
index 000000000000..1e966411c677
--- /dev/null
+++ b/drivers/gpio/gpio-loongson-64bit.c
@@ -0,0 +1,370 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Loongson GPIO Support
+ *
+ * Copyright (C) 2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/of_irq.h>
+#include <asm/types.h>
+
+#define LOONGSON_GPIO_IN(x)		(x->reg_base +\
+					x->chip_data->in_offset)
+#define LOONGSON_GPIO_OUT(x)		(x->reg_base +\
+					x->chip_data->out_offset)
+#define LOONGSON_GPIO_OEN(x)		(x->reg_base +\
+					x->chip_data->conf_offset)
+#define LOONGSON_GPIO_IN_BYTE(x, gpio)	(x->reg_base +\
+					x->chip_data->in_offset + gpio)
+#define LOONGSON_GPIO_OUT_BYTE(x, gpio)	(x->reg_base +\
+					x->chip_data->out_offset + gpio)
+#define LOONGSON_GPIO_OEN_BYTE(x, gpio)	(x->reg_base +\
+					x->chip_data->conf_offset + gpio)
+
+enum loongson_gpio_mode {
+	BIT_CTRL_MODE,
+	BYTE_CTRL_MODE,
+};
+
+struct loongson_gpio_chip_data {
+	const char		*label;
+	enum loongson_gpio_mode	mode;
+	unsigned int		conf_offset;
+	unsigned int		out_offset;
+	unsigned int		in_offset;
+	bool			support_irq;
+};
+
+struct loongson_gpio_chip {
+	struct gpio_chip	chip;
+	struct fwnode_handle	*fwnode;
+	spinlock_t		lock;
+	void __iomem		*reg_base;
+	const struct loongson_gpio_chip_data *chip_data;
+};
+
+static inline struct loongson_gpio_chip *to_loongson_gpio_chip(
+				struct gpio_chip *chip)
+{
+	return container_of(chip, struct loongson_gpio_chip, chip);
+}
+
+static inline void loongson_commit_direction(
+				struct loongson_gpio_chip *lgpio,
+				unsigned int pin, int input)
+{
+	u8 bval = input ? 1 : 0;
+
+	writeb(bval, LOONGSON_GPIO_OEN_BYTE(lgpio, pin));
+}
+
+static void loongson_commit_level(struct loongson_gpio_chip *lgpio,
+				unsigned int pin, int high)
+{
+	u8 bval = high ? 1 : 0;
+
+	writeb(bval, LOONGSON_GPIO_OUT_BYTE(lgpio, pin));
+}
+
+static int loongson_gpio_direction_input(
+				struct gpio_chip *chip, unsigned int pin)
+{
+	unsigned long flags;
+	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+	spin_lock_irqsave(&lgpio->lock, flags);
+	loongson_commit_direction(lgpio, pin, 1);
+	spin_unlock_irqrestore(&lgpio->lock, flags);
+
+	return 0;
+}
+
+static int loongson_gpio_direction_output(
+				struct gpio_chip *chip, unsigned int pin,
+				int value)
+{
+	unsigned long flags;
+	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+	spin_lock_irqsave(&lgpio->lock, flags);
+	loongson_commit_level(lgpio, pin, value);
+	loongson_commit_direction(lgpio, pin, 0);
+	spin_unlock_irqrestore(&lgpio->lock, flags);
+
+	return 0;
+}
+
+static int loongson_gpio_get(struct gpio_chip *chip, unsigned int pin)
+{
+	u8  bval;
+	int val;
+	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+	bval = readb(LOONGSON_GPIO_IN_BYTE(lgpio, pin));
+	val = bval & 1;
+
+	return val;
+}
+
+static int loongson_gpio_get_direction(
+				struct gpio_chip *chip, unsigned int pin)
+{
+	u8  bval;
+	int val;
+	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+	bval = readb(LOONGSON_GPIO_OEN_BYTE(lgpio, pin));
+	val = bval & 1;
+
+	return val;
+}
+
+static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin,
+			int value)
+{
+	unsigned long flags;
+	struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+	spin_lock_irqsave(&lgpio->lock, flags);
+	loongson_commit_level(lgpio, pin, value);
+	spin_unlock_irqrestore(&lgpio->lock, flags);
+}
+
+static int loongson_gpio_to_irq(
+			struct gpio_chip *chip, unsigned int offset)
+{
+	struct platform_device *pdev =
+		container_of(chip->parent, struct platform_device, dev);
+
+	return platform_get_irq(pdev, offset);
+}
+
+static void loongson_gpio_irq_ack(struct irq_data *d)
+{
+	irq_chip_ack_parent(d);
+}
+
+static void loongson_gpio_mask_irq(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	irq_chip_mask_parent(d);
+	gpiochip_disable_irq(gc, d->hwirq);
+}
+
+static void loongson_gpio_irq_unmask(struct irq_data *d)
+{
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+
+	gpiochip_enable_irq(gc, d->hwirq);
+	irq_chip_unmask_parent(d);
+}
+
+static int loongson_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		irq_set_handler_locked(d, handle_edge_irq);
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		irq_set_handler_locked(d, handle_edge_irq);
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		irq_set_handler_locked(d, handle_level_irq);
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		irq_set_handler_locked(d, handle_level_irq);
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		irq_set_handler_locked(d, handle_edge_irq);
+		break;
+	case IRQ_TYPE_NONE:
+	default:
+		return -EINVAL;
+	}
+
+	return irq_chip_set_type_parent(d, type);
+}
+
+static const struct irq_chip loongson_gpio_irqchip = {
+	.name = "loongson-gpio-irqchip",
+	.irq_ack = loongson_gpio_irq_ack,
+	.irq_mask = loongson_gpio_mask_irq,
+	.irq_unmask = loongson_gpio_irq_unmask,
+	.irq_set_type = loongson_gpio_irq_set_type,
+	.flags = IRQCHIP_IMMUTABLE,
+	GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
+static int loongson_gpio_child_to_parent_hwirq(struct gpio_chip *gc,
+					     unsigned int child,
+					     unsigned int child_type,
+					     unsigned int *parent,
+					     unsigned int *parent_type)
+{
+	*parent_type = child_type;
+
+	/* GPIO lines 0..3 have dedicated IRQs */
+	if (child >= 0 && child <= 3) {
+		*parent = child + 60;
+		return 0;
+	}
+
+	if (child >= 4 && child <= 31) {
+		*parent = 58;
+		return 0;
+	}
+
+	if (child >= 32 && child <= 63) {
+		*parent = 59;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int loongson_gpio_init(
+			struct device *dev, struct loongson_gpio_chip *lgpio,
+			struct device_node *np, void __iomem *reg_base)
+{
+	int ret;
+	u32 ngpios;
+	struct irq_domain *parent;
+	struct gpio_irq_chip *girq;
+	struct device_node *irq_parent;
+
+	lgpio->reg_base = reg_base;
+
+	if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
+		ret = bgpio_init(&lgpio->chip, dev, 8,
+				LOONGSON_GPIO_IN(lgpio),
+				LOONGSON_GPIO_OUT(lgpio), 0,
+				LOONGSON_GPIO_OEN(lgpio), NULL, 0);
+		if (ret) {
+			dev_err(dev, "unable to init generic GPIO\n");
+			return ret;
+		}
+	} else {
+		lgpio->chip.direction_input = loongson_gpio_direction_input;
+		lgpio->chip.get = loongson_gpio_get;
+		lgpio->chip.get_direction = loongson_gpio_get_direction;
+		lgpio->chip.direction_output = loongson_gpio_direction_output;
+		lgpio->chip.set = loongson_gpio_set;
+		lgpio->chip.can_sleep = 0;
+		lgpio->chip.of_node = np;
+		lgpio->chip.parent = dev;
+		spin_lock_init(&lgpio->lock);
+	}
+
+	device_property_read_u32(dev, "ngpios", &ngpios);
+
+	lgpio->chip.ngpio = ngpios;
+	lgpio->chip.label = lgpio->chip_data->label;
+
+	if (lgpio->chip_data->support_irq == true) {
+		lgpio->chip.to_irq = loongson_gpio_to_irq;
+		irq_parent = of_irq_find_parent(np);
+		if (irq_parent)
+			parent = irq_find_host(irq_parent);
+
+		lgpio->fwnode = of_node_to_fwnode(np);
+
+		girq = &lgpio->chip.irq;
+		gpio_irq_chip_set_chip(girq, &loongson_gpio_irqchip);
+		girq->fwnode = lgpio->fwnode;
+		girq->parent_domain = parent;
+		girq->child_to_parent_hwirq = loongson_gpio_child_to_parent_hwirq;
+		girq->handler = handle_bad_irq;
+		girq->default_type = IRQ_TYPE_NONE;
+	}
+
+	return devm_gpiochip_add_data(dev, &lgpio->chip, lgpio);
+}
+
+static int loongson_gpio_probe(struct platform_device *pdev)
+{
+	void __iomem *reg_base;
+	struct loongson_gpio_chip *lgpio;
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+
+	lgpio = devm_kzalloc(dev, sizeof(*lgpio), GFP_KERNEL);
+	if (!lgpio)
+		return -ENOMEM;
+
+	lgpio->chip_data = device_get_match_data(dev);
+
+	reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(reg_base))
+		return PTR_ERR(reg_base);
+
+	return loongson_gpio_init(dev, lgpio, np, reg_base);
+}
+
+static const struct loongson_gpio_chip_data loongson_gpio_ls2k_data = {
+	.label = "ls2k_gpio",
+	.mode = BIT_CTRL_MODE,
+	.conf_offset = 0x0,
+	.in_offset = 0x10,
+	.out_offset = 0x20,
+	.support_irq = true,
+};
+
+static const struct loongson_gpio_chip_data loongson_gpio_ls7a_data = {
+	.label = "ls7a_gpio",
+	.mode = BYTE_CTRL_MODE,
+	.conf_offset = 0x800,
+	.in_offset = 0x900,
+	.out_offset = 0xa00,
+	.support_irq = false,
+};
+
+static const struct of_device_id loongson_gpio_of_match[] = {
+	{
+		.compatible = "loongson,ls2k-gpio",
+		.data = &loongson_gpio_ls2k_data,
+	},
+	{
+		.compatible = "loongson,ls7a-gpio",
+		.data = &loongson_gpio_ls7a_data,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, loongson_gpio_of_match);
+
+static const struct acpi_device_id loongson_gpio_acpi_match[] = {
+	{
+		.id = "LOON0002",
+		.driver_data = (kernel_ulong_t)&loongson_gpio_ls7a_data,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match);
+
+static struct platform_driver loongson_gpio_driver = {
+	.driver = {
+		.name = "loongson-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = loongson_gpio_of_match,
+		.acpi_match_table = loongson_gpio_acpi_match,
+	},
+	.probe = loongson_gpio_probe,
+};
+
+static int __init loongson_gpio_setup(void)
+{
+	return platform_driver_register(&loongson_gpio_driver);
+}
+postcore_initcall(loongson_gpio_setup);
+
+MODULE_DESCRIPTION("Loongson gpio driver");
+MODULE_LICENSE("GPL");