From d024e482630acfb513027b5e4549a7b792ea8abb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 16:36:29 +0200 Subject: [PATCH 01/16] gpio: blzp1600: drop dependency on OF headers Use the generic boolean device property getter instead of the OF-specific variant. This allows us to stop pulling in linux/of.h. While at it: drop the of_irq.h inclusion as none of its symbols are used in this driver. Signed-off-by: Bartosz Golaszewski Reviewed-by: Nikolaos Pasaloukos Link: https://lore.kernel.org/r/20250408143629.125576-1-brgl@bgdev.pl Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-blzp1600.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-blzp1600.c b/drivers/gpio/gpio-blzp1600.c index 77ad0e596f3e..2760a13c0801 100644 --- a/drivers/gpio/gpio-blzp1600.c +++ b/drivers/gpio/gpio-blzp1600.c @@ -10,9 +10,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -217,7 +216,6 @@ static int blzp1600_gpio_set_config(struct gpio_chip *gc, unsigned int offset, u static int blzp1600_gpio_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct blzp1600_gpio *chip; struct gpio_chip *gc; int ret; @@ -240,7 +238,7 @@ static int blzp1600_gpio_probe(struct platform_device *pdev) gc = &chip->gc; gc->set_config = blzp1600_gpio_set_config; - if (of_property_read_bool(node, "interrupt-controller")) { + if (device_property_present(&pdev->dev, "interrupt-controller")) { struct gpio_irq_chip *girq; chip->irq = platform_get_irq(pdev, 0); -- 2.51.0 From 6273ef3560728f111c480e580970ba25cdee85b1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 14:59:01 +0200 Subject: [PATCH 02/16] gpio: zynq: enable building the modules with COMPILE_TEST=y Extend the build coverage by allowing to build the modules with COMPILE_TEST enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250408-gpio-compile-test-v1-1-140e108e9392@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c4f7cc024efb..592bb1f624c0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -841,14 +841,14 @@ config GPIO_ZEVIO config GPIO_ZYNQ tristate "Xilinx Zynq GPIO support" - depends on ARCH_ZYNQ || ARCH_ZYNQMP + depends on ARCH_ZYNQ || ARCH_ZYNQMP || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support Xilinx Zynq GPIO controller. config GPIO_ZYNQMP_MODEPIN tristate "ZynqMP ps-mode pin GPIO configuration driver" - depends on ZYNQMP_FIRMWARE + depends on ZYNQMP_FIRMWARE || COMPILE_TEST default ZYNQMP_FIRMWARE help Say yes here to support the ZynqMP ps-mode pin GPIO configuration -- 2.51.0 From 4675b78c88a4397adf20eb17e8dcf216d29cb09b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 14:59:02 +0200 Subject: [PATCH 03/16] gpio: msc313: enable building the module with COMPILE_TEST=y Extend the build coverage by allowing to build the module with COMPILE_TEST enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250408-gpio-compile-test-v1-2-140e108e9392@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 592bb1f624c0..dcb8128d06c0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -877,7 +877,7 @@ config GPIO_AMD_FCH config GPIO_MSC313 bool "MStar MSC313 GPIO support" - depends on ARCH_MSTARV7 + depends on ARCH_MSTARV7 || COMPILE_TEST default ARCH_MSTARV7 select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY -- 2.51.0 From bcb4be090783f0ce1acdff86242e6f55a2137b64 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 14:59:03 +0200 Subject: [PATCH 04/16] gpio: pl061: enable building the module with COMPILE_TEST=y Extend the build coverage by allowing to build the module with COMPILE_TEST enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250408-gpio-compile-test-v1-3-140e108e9392@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index dcb8128d06c0..f0de782c04cc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -551,7 +551,7 @@ config GPIO_OMAP config GPIO_PL061 tristate "PrimeCell PL061 GPIO support" - depends on ARM_AMBA + depends on ARM_AMBA || COMPILE_TEST select IRQ_DOMAIN select GPIOLIB_IRQCHIP help -- 2.51.0 From 3d1c5b622141418c739291d945686137fd92cd3a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 14:59:04 +0200 Subject: [PATCH 05/16] gpio: rtd: enable building the module with COMPILE_TEST=y Extend the build coverage by allowing to build the module with COMPILE_TEST enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250408-gpio-compile-test-v1-4-140e108e9392@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f0de782c04cc..169158089608 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -615,7 +615,7 @@ config GPIO_ROCKCHIP config GPIO_RTD tristate "Realtek DHC GPIO support" - depends on ARCH_REALTEK + depends on ARCH_REALTEK || COMPILE_TEST default y select GPIOLIB_IRQCHIP help -- 2.51.0 From 4e8a72f9b7120c5775e051b3c41fd7cc46e1ae8b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 14:59:06 +0200 Subject: [PATCH 06/16] gpio: tn48m: enable building the module with COMPILE_TEST=y Extend the build coverage by allowing to build the module with COMPILE_TEST enabled. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250408-gpio-compile-test-v1-6-140e108e9392@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 169158089608..e08e2161c1eb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1536,7 +1536,7 @@ config GPIO_TIMBERDALE config GPIO_TN48M_CPLD tristate "Delta Networks TN48M switch CPLD GPIO driver" - depends on MFD_TN48M_CPLD + depends on MFD_TN48M_CPLD || COMPILE_TEST select GPIO_REGMAP help This enables support for the GPIOs found on the Delta -- 2.51.0 From 26bb7614fa04278abbf0902d516b884677c12e86 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sat, 12 Apr 2025 07:31:28 +0800 Subject: [PATCH 07/16] dt-bindings: gpio: spacemit: add support for K1 SoC The GPIO controller of K1 support basic functions as input/output, all pins can be used as interrupt which route to one IRQ line, trigger type can be select between rising edge, falling edge, or both. There are four GPIO banks, each consisting of 32 pins. Reviewed-by: Linus Walleij Reviewed-by: Rob Herring (Arm) Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20250412-03-k1-gpio-v8-1-1c6862d272ec@gentoo.org Signed-off-by: Bartosz Golaszewski --- .../bindings/gpio/spacemit,k1-gpio.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/spacemit,k1-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/spacemit,k1-gpio.yaml b/Documentation/devicetree/bindings/gpio/spacemit,k1-gpio.yaml new file mode 100644 index 000000000000..ec0232e72c71 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/spacemit,k1-gpio.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/spacemit,k1-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K1 GPIO controller + +maintainers: + - Yixun Lan + +description: + The controller's registers are organized as sets of eight 32-bit + registers with each set of port controlling 32 pins. A single + interrupt line is shared for all of the pins by the controller. + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + const: spacemit,k1-gpio + + reg: + maxItems: 1 + + clocks: + items: + - description: GPIO Core Clock + - description: GPIO Bus Clock + + clock-names: + items: + - const: core + - const: bus + + resets: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 3 + description: + The first two cells are the GPIO bank index and offset inside the bank, + the third cell should specify GPIO flag. + + gpio-ranges: true + + interrupts: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 3 + description: + The first two cells are the GPIO bank index and offset inside the bank, + the third cell should specify interrupt flag. The controller does not + support level interrupts, so flags of IRQ_TYPE_LEVEL_HIGH, + IRQ_TYPE_LEVEL_LOW should not be used. + Refer for valid flags. + +required: + - compatible + - reg + - clocks + - clock-names + - gpio-controller + - "#gpio-cells" + - interrupts + - interrupt-controller + - "#interrupt-cells" + - gpio-ranges + +additionalProperties: false + +examples: + - | + gpio@d4019000 { + compatible = "spacemit,k1-gpio"; + reg = <0xd4019000 0x800>; + clocks =<&ccu 9>, <&ccu 61>; + clock-names = "core", "bus"; + gpio-controller; + #gpio-cells = <3>; + interrupts = <58>; + interrupt-controller; + interrupt-parent = <&plic>; + #interrupt-cells = <3>; + gpio-ranges = <&pinctrl 0 0 0 32>, + <&pinctrl 1 0 32 32>, + <&pinctrl 2 0 64 32>, + <&pinctrl 3 0 96 32>; + }; +... -- 2.51.0 From d00553240ef89542661b9088dc50c8ebf480a23e Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sat, 12 Apr 2025 07:31:29 +0800 Subject: [PATCH 08/16] gpio: spacemit: add support for K1 SoC Implement GPIO functionality which capable of setting pin as input, output. Also, each pin can be used as interrupt which support rising, falling, or both edge type trigger. Reviewed-by: Alex Elder Reviewed-by: Linus Walleij Signed-off-by: Yixun Lan Link: https://lore.kernel.org/r/20250412-03-k1-gpio-v8-2-1c6862d272ec@gentoo.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-spacemit-k1.c | 293 ++++++++++++++++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 drivers/gpio/gpio-spacemit-k1.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index e08e2161c1eb..bbbb550cac93 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -667,6 +667,15 @@ config GPIO_SNPS_CREG where only several fields in register belong to GPIO lines and each GPIO line owns a field with different length and on/off value. +config GPIO_SPACEMIT_K1 + tristate "SPACEMIT K1 GPIO support" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say yes here to support the SpacemiT's K1 GPIO device. + config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" depends on PLAT_SPEAR diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8661cfd8fd8c..9aabbb9cb4c6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -160,6 +160,7 @@ obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o obj-$(CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER) += gpio-sloppy-logic-analyzer.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o +obj-$(CONFIG_GPIO_SPACEMIT_K1) += gpio-spacemit-k1.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c new file mode 100644 index 000000000000..f027066365ff --- /dev/null +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (C) 2025 Yixun Lan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offset */ +#define SPACEMIT_GPLR 0x00 /* port level - R */ +#define SPACEMIT_GPDR 0x0c /* port direction - R/W */ +#define SPACEMIT_GPSR 0x18 /* port set - W */ +#define SPACEMIT_GPCR 0x24 /* port clear - W */ +#define SPACEMIT_GRER 0x30 /* port rising edge R/W */ +#define SPACEMIT_GFER 0x3c /* port falling edge R/W */ +#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */ +#define SPACEMIT_GSDR 0x54 /* (set) direction - W */ +#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */ +#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */ +#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */ +#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */ +#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */ +#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */ + +#define SPACEMIT_NR_BANKS 4 +#define SPACEMIT_NR_GPIOS_PER_BANK 32 + +#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) + +struct spacemit_gpio; + +struct spacemit_gpio_bank { + struct gpio_chip gc; + struct spacemit_gpio *sg; + void __iomem *base; + u32 irq_mask; + u32 irq_rising_edge; + u32 irq_falling_edge; +}; + +struct spacemit_gpio { + struct device *dev; + struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; +}; + +static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) +{ + return (u32)(gb - gb->sg->sgb); +} + +static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) +{ + struct spacemit_gpio_bank *gb = dev_id; + unsigned long pending; + u32 n, gedr; + + gedr = readl(gb->base + SPACEMIT_GEDR); + if (!gedr) + return IRQ_NONE; + writel(gedr, gb->base + SPACEMIT_GEDR); + + pending = gedr & gb->irq_mask; + if (!pending) + return IRQ_NONE; + + for_each_set_bit(n, &pending, BITS_PER_LONG) + handle_nested_irq(irq_find_mapping(gb->gc.irq.domain, n)); + + return IRQ_HANDLED; +} + +static void spacemit_gpio_irq_ack(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + + writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); +} + +static void spacemit_gpio_irq_mask(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + gb->irq_mask &= ~bit; + writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); + + if (bit & gb->irq_rising_edge) + writel(bit, gb->base + SPACEMIT_GCRER); + + if (bit & gb->irq_falling_edge) + writel(bit, gb->base + SPACEMIT_GCFER); +} + +static void spacemit_gpio_irq_unmask(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + gb->irq_mask |= bit; + + if (bit & gb->irq_rising_edge) + writel(bit, gb->base + SPACEMIT_GSRER); + + if (bit & gb->irq_falling_edge) + writel(bit, gb->base + SPACEMIT_GSFER); + + writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); +} + +static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + if (type & IRQ_TYPE_EDGE_RISING) { + gb->irq_rising_edge |= bit; + writel(bit, gb->base + SPACEMIT_GSRER); + } else { + gb->irq_rising_edge &= ~bit; + writel(bit, gb->base + SPACEMIT_GCRER); + } + + if (type & IRQ_TYPE_EDGE_FALLING) { + gb->irq_falling_edge |= bit; + writel(bit, gb->base + SPACEMIT_GSFER); + } else { + gb->irq_falling_edge &= ~bit; + writel(bit, gb->base + SPACEMIT_GCFER); + } + + return 0; +} + +static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data); + + seq_printf(p, "%s-%d", dev_name(gb->gc.parent), spacemit_gpio_bank_index(gb)); +} + +static struct irq_chip spacemit_gpio_chip = { + .name = "k1-gpio-irqchip", + .irq_ack = spacemit_gpio_irq_ack, + .irq_mask = spacemit_gpio_irq_mask, + .irq_unmask = spacemit_gpio_irq_unmask, + .irq_set_type = spacemit_gpio_irq_set_type, + .irq_print_chip = spacemit_gpio_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i) +{ + struct spacemit_gpio_bank *gb = gpiochip_get_data(gc); + struct spacemit_gpio *sg = gb->sg; + + if (i >= SPACEMIT_NR_BANKS) + return false; + + return (gc == &sg->sgb[i].gc); +} + +static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, + void __iomem *regs, + int index, int irq) +{ + struct spacemit_gpio_bank *gb = &sg->sgb[index]; + struct gpio_chip *gc = &gb->gc; + struct device *dev = sg->dev; + struct gpio_irq_chip *girq; + void __iomem *dat, *set, *clr, *dirin, *dirout; + int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; + + gb->base = regs + bank_base[index]; + + dat = gb->base + SPACEMIT_GPLR; + set = gb->base + SPACEMIT_GPSR; + clr = gb->base + SPACEMIT_GPCR; + dirin = gb->base + SPACEMIT_GCDR; + dirout = gb->base + SPACEMIT_GSDR; + + /* This registers 32 GPIO lines per bank */ + ret = bgpio_init(gc, dev, 4, dat, set, clr, dirout, dirin, + BGPIOF_UNREADABLE_REG_SET | BGPIOF_UNREADABLE_REG_DIR); + if (ret) + return dev_err_probe(dev, ret, "failed to init gpio chip\n"); + + gb->sg = sg; + + gc->label = dev_name(dev); + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; + gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; + gc->base = -1; + gc->of_gpio_n_cells = 3; + gc->of_node_instance_match = spacemit_of_node_instance_match; + + girq = &gc->irq; + girq->threaded = true; + girq->handler = handle_simple_irq; + + gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); + + /* Disable Interrupt */ + writel(0, gb->base + SPACEMIT_GAPMASK); + /* Disable Edge Detection Settings */ + writel(0x0, gb->base + SPACEMIT_GRER); + writel(0x0, gb->base + SPACEMIT_GFER); + /* Clear Interrupt */ + writel(0xffffffff, gb->base + SPACEMIT_GCRER); + writel(0xffffffff, gb->base + SPACEMIT_GCFER); + + ret = devm_request_threaded_irq(dev, irq, NULL, + spacemit_gpio_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + gb->gc.label, gb); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to register IRQ\n"); + + ret = devm_gpiochip_add_data(dev, gc, gb); + if (ret) + return ret; + + /* Distuingish IRQ domain, for selecting threecells mode */ + irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); + + return 0; +} + +static int spacemit_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spacemit_gpio *sg; + struct clk *core_clk, *bus_clk; + void __iomem *regs; + int i, irq, ret; + + sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); + if (!sg) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + sg->dev = dev; + + core_clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(core_clk)) + return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); + + bus_clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); + + for (i = 0; i < SPACEMIT_NR_BANKS; i++) { + ret = spacemit_gpio_add_bank(sg, regs, i, irq); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id spacemit_gpio_dt_ids[] = { + { .compatible = "spacemit,k1-gpio" }, + { /* sentinel */ } +}; + +static struct platform_driver spacemit_gpio_driver = { + .probe = spacemit_gpio_probe, + .driver = { + .name = "k1-gpio", + .of_match_table = spacemit_gpio_dt_ids, + }, +}; +module_platform_driver(spacemit_gpio_driver); + +MODULE_AUTHOR("Yixun Lan "); +MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); +MODULE_LICENSE("GPL"); -- 2.51.0 From f1d6cd0774bc211429ebf24187dc3d271feb5ed6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:09 +0300 Subject: [PATCH 09/16] gpiolib: Make taking gpio_lookup_lock consistent There are two ways to take a lock: plain call to the mutex_lock() or using guard()() / scoped_guard(). The driver inconsistently uses both. Make taking gpio_lookup_lock consistent. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-2-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f208901ba478..40ae0e09fb9f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4363,12 +4363,10 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); for (i = 0; i < n; i++) list_add_tail(&tables[i]->list, &gpio_lookup_list); - - mutex_unlock(&gpio_lookup_lock); } /** @@ -4427,11 +4425,9 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) if (!table) return; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); list_del(&table->list); - - mutex_unlock(&gpio_lookup_lock); } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); -- 2.51.0 From d4fe58c8ea17db3e76cf80ad0fcdc655bba0153c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:10 +0300 Subject: [PATCH 10/16] gpiolib: Convert to use guard()() for gpio_machine_hogs_mutex The driver uses guard()()/scoped_guard() for the rest of the synchronisation calls. Convert to use the same for gpio_machine_hogs_mutex. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-3-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 40ae0e09fb9f..e2716e20b0e4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -888,14 +888,12 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { if (!strcmp(gc->label, hog->chip_label)) gpiochip_machine_hog(gc, hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } static void gpiochip_setup_devs(void) @@ -4439,7 +4437,7 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { list_add_tail(&hog->list, &gpio_machine_hogs); @@ -4453,8 +4451,6 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) if (gdev) gpiochip_machine_hog(gpio_device_get_chip(gdev), hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_add_hogs); @@ -4462,10 +4458,10 @@ void gpiod_remove_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); + for (hog = &hogs[0]; hog->chip_label; hog++) list_del(&hog->list); - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_remove_hogs); -- 2.51.0 From b709d676c1029458261121da6ccf65ecbb56f73c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:11 +0300 Subject: [PATCH 11/16] gpiolib: Print actual error when descriptor contains an error pointer Print the actual error when descriptor contains an error pointer. This might help debugging those rare cases. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-4-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e2716e20b0e4..e8e75da7b177 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2443,7 +2443,7 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) return 0; if (IS_ERR(desc)) { - pr_warn("%s: invalid GPIO (errorpointer)\n", func); + pr_warn("%s: invalid GPIO (errorpointer: %pe)\n", func, desc); return PTR_ERR(desc); } -- 2.51.0 From df6dccb4aeab5f19c8451d960259fd1d911f3129 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:12 +0300 Subject: [PATCH 12/16] gpiolib: Revert "Don't WARN on gpiod_put() for optional GPIO" No need to double check the pointer for NULL since gpiod_free() is using VALIDATE_DESC_VOID() which simply returns in that case. This reverts commit 1d7765ba15aca68f3bc52f59434c1c34855bbb54. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-5-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e8e75da7b177..7ab408b44893 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5140,8 +5140,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { - if (desc) - gpiod_free(desc); + gpiod_free(desc); } EXPORT_SYMBOL_GPL(gpiod_put); -- 2.51.0 From 35d9bb5f799a4d8cf6cada0e7dc93a87ca8f6755 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:13 +0300 Subject: [PATCH 13/16] gpiolib: Move validate_desc() and Co upper in the code Move validate_desc() and Co upper in the code to be able to use in the further changes. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-6-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 62 +++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7ab408b44893..297a124a5e44 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -356,6 +356,37 @@ static int gpiochip_find_base_unlocked(u16 ngpio) } } +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. If the desc is NULL it is an + * optional GPIO and calls should just bail out. + */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer: %pe)\n", func, desc); + return PTR_ERR(desc); + } + + return 1; +} + +#define VALIDATE_DESC(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return; \ + } while (0) + static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) { int ret; @@ -2431,37 +2462,6 @@ out_clear_bit: return ret; } -/* - * This descriptor validation needs to be inserted verbatim into each - * function taking a descriptor, so we need to use a preprocessor - * macro to avoid endless duplication. If the desc is NULL it is an - * optional GPIO and calls should just bail out. - */ -static int validate_desc(const struct gpio_desc *desc, const char *func) -{ - if (!desc) - return 0; - - if (IS_ERR(desc)) { - pr_warn("%s: invalid GPIO (errorpointer: %pe)\n", func, desc); - return PTR_ERR(desc); - } - - return 1; -} - -#define VALIDATE_DESC(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return __valid; \ - } while (0) - -#define VALIDATE_DESC_VOID(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return; \ - } while (0) - int gpiod_request(struct gpio_desc *desc, const char *label) { int ret = -EPROBE_DEFER; -- 2.51.0 From 8c13e6a3d588320327b67727c1b9a0dea843ca74 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:14 +0300 Subject: [PATCH 14/16] gpiolib: Call validate_desc() when VALIDATE_DESC() can't be used Call validate_desc() directly when VALIDATE_DESC() can't be used. It will print additional information useful for debugging. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-7-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 297a124a5e44..8a47d861d836 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -421,11 +421,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned int offset; int ret; - /* - * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL - * descriptor like we usually do. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; CLASS(gpio_chip_guard, guard)(desc); @@ -3982,13 +3979,10 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_device *gdev; struct gpio_chip *gc; int offset; + int ret; - /* - * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics - * requires this function to not return zero on an invalid descriptor - * but rather a negative error number. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; gdev = desc->gdev; -- 2.51.0 From 7cc868831b98eb10e8ae50a8e5a9f04674d1a2ab Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:15 +0300 Subject: [PATCH 15/16] gpiolib: Reuse return variable in gpiod_to_irq() There are two variables for the same used in the gpiod_to_irq(). Replace the second by reusing the function top-level one. While at it, refactor the branch to have less lines of code. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-8-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8a47d861d836..3243ef3fc7d8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3994,13 +3994,12 @@ int gpiod_to_irq(const struct gpio_desc *desc) offset = gpio_chip_hwgpio(desc); if (gc->to_irq) { - int retirq = gc->to_irq(gc, offset); + ret = gc->to_irq(gc, offset); + if (ret) + return ret; /* Zero means NO_IRQ */ - if (!retirq) - return -ENXIO; - - return retirq; + return -ENXIO; } #ifdef CONFIG_GPIOLIB_IRQCHIP if (gc->irq.chip) { -- 2.51.0 From 550300b9a295a591e0721a31f8c964a4bc08d51c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Apr 2025 12:55:16 +0300 Subject: [PATCH 16/16] gpiolib: Remove redundant assignment of return variable In some functions the returned variable is assigned to 0 and then reassigned to the actual value. Remove redundant assignments. In one case make it more clear that the assignment is not needed. Reviewed-by: Linus Walleij Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250416095645.2027695-9-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3243ef3fc7d8..87903847c3d7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1015,7 +1015,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct gpio_device *gdev; unsigned int desc_index; int base = 0; - int ret = 0; + int ret; /* Only allow one set() and one set_multiple(). */ if ((gc->set && gc->set_rv) || @@ -1040,11 +1040,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); - gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); - if (gdev->id < 0) { - ret = gdev->id; + ret = ida_alloc(&gpio_ida, GFP_KERNEL); + if (ret < 0) goto err_free_gdev; - } + gdev->id = ret; ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); if (ret) @@ -3068,7 +3067,7 @@ set_output_flag: */ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); @@ -3101,7 +3100,7 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns); */ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); -- 2.51.0