summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/74x164_gpio.c17
-rw-r--r--drivers/gpio/Kconfig61
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-aspeed-g7.c2
-rw-r--r--drivers/gpio/gpio-aspeed-sgpio.c6
-rw-r--r--drivers/gpio/gpio-aspeed.c2
-rw-r--r--drivers/gpio/gpio-fxl6408.c2
-rw-r--r--drivers/gpio/gpio-uclass.c13
-rw-r--r--drivers/gpio/imx_rgpio2p.c4
-rw-r--r--drivers/gpio/mpc8xxx_gpio.c54
-rw-r--r--drivers/gpio/nx_gpio.c7
-rw-r--r--drivers/gpio/pca953x_gpio.c2
-rw-r--r--drivers/gpio/qcom_pmic_gpio.c3
-rw-r--r--drivers/gpio/qcom_spmi_gpio.c4
-rw-r--r--drivers/gpio/sandbox.c4
-rw-r--r--drivers/gpio/spacemit_gpio.c253
16 files changed, 380 insertions, 55 deletions
diff --git a/drivers/gpio/74x164_gpio.c b/drivers/gpio/74x164_gpio.c
index 331428ccdb9..de2926894ec 100644
--- a/drivers/gpio/74x164_gpio.c
+++ b/drivers/gpio/74x164_gpio.c
@@ -10,17 +10,13 @@
#include <errno.h>
#include <dm.h>
-#include <fdtdec.h>
#include <malloc.h>
-#include <asm/global_data.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <dt-bindings/gpio/gpio.h>
#include <spi.h>
-DECLARE_GLOBAL_DATA_PTR;
-
/*
* struct gen_74x164_chip - Data for 74Hx164
*
@@ -127,11 +123,9 @@ static int gen_74x164_probe(struct udevice *dev)
{
struct gen_74x164_priv *priv = dev_get_priv(dev);
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const u8 *defaults;
char *str, name[32];
int ret;
- const void *fdt = gd->fdt_blob;
- int node = dev_of_offset(dev);
-
snprintf(name, sizeof(name), "%s_", dev->name);
str = strdup(name);
if (!str)
@@ -141,16 +135,17 @@ static int gen_74x164_probe(struct udevice *dev)
* See Linux kernel:
* Documentation/devicetree/bindings/gpio/gpio-74x164.txt
*/
- priv->nregs = fdtdec_get_int(fdt, node, "registers-number", 1);
+ priv->nregs = dev_read_u32_default(dev, "registers-number", 1);
priv->buffer = calloc(priv->nregs, sizeof(u8));
if (!priv->buffer) {
ret = -ENOMEM;
goto free_str;
}
- ret = fdtdec_get_byte_array(fdt, node, "registers-default",
- priv->buffer, priv->nregs);
- if (ret)
+ defaults = dev_read_u8_array_ptr(dev, "registers-default", priv->nregs);
+ if (defaults)
+ memcpy(priv->buffer, defaults, priv->nregs);
+ else
dev_dbg(dev, "No registers-default property\n");
ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe,
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0b5466b39b8..a11f3043670 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -294,9 +294,9 @@ config MAX7320_GPIO
bool "MAX7320 I2C GPIO Expander driver"
depends on DM_GPIO && DM_I2C
help
- Support for MAX7320 I2C 8/16-bit GPIO expander.
- original maxim device has 8 push/pull outputs,
- some clones offers 16bit.
+ Support for MAX7320 I2C 8/16-bit GPIO expander.
+ original maxim device has 8 push/pull outputs,
+ some clones offers 16bit.
config MAX77663_GPIO
bool "MAX77663 GPIO cell of PMIC driver"
@@ -309,27 +309,27 @@ config MAX77663_GPIO
config MCP230XX_GPIO
bool "MCP230XX GPIO driver"
- depends on DM
+ depends on DM && DM_I2C && DM_SPI
help
- Support for Microchip's MCP230XX I2C connected GPIO devices.
+ Support for Microchip's MCP230XX I2C and SPI connected GPIO devices.
The following chips are supported:
- - MCP23008
- - MCP23017
- - MCP23018
- - MCP23S08
- - MCP23S17
- - MCP23S18
+ - MCP23008
+ - MCP23017
+ - MCP23018
+ - MCP23S08
+ - MCP23S17
+ - MCP23S18
config MSCC_SGPIO
bool "Microsemi Serial GPIO driver"
depends on DM_GPIO && SOC_VCOREIII
help
Support for the VCoreIII SoC serial GPIO device. By using a
- serial interface, the SIO controller significantly extends
- the number of available GPIOs with a minimum number of
- additional pins on the device. The primary purpose of the
- SIO controller is to connect control signals from SFP
- modules and to act as an LED controller.
+ serial interface, the SIO controller significantly extends
+ the number of available GPIOs with a minimum number of
+ additional pins on the device. The primary purpose of the
+ SIO controller is to connect control signals from SFP
+ modules and to act as an LED controller.
config MSM_GPIO
bool "Qualcomm GPIO driver"
@@ -404,8 +404,8 @@ config PCF8575_GPIO
bool "PCF8575 I2C GPIO Expander driver"
depends on DM_GPIO && DM_I2C
help
- Support for PCF8575 I2C 16-bit GPIO expander. Most of these
- chips are from NXP and TI.
+ Support for PCF8575 I2C 16-bit GPIO expander. Most of these
+ chips are from NXP and TI.
config RCAR_GPIO
bool "Renesas R-Car GPIO driver"
@@ -449,6 +449,14 @@ config SANDBOX_GPIO_COUNT
of 'anonymous' GPIOs that do not belong to any device or bank.
Select a suitable value depending on your needs.
+config SPACEMIT_GPIO
+ bool "Spacemit K1 GPIO driver"
+ depends on DM_GPIO && TARGET_SPACEMIT_K1
+ help
+ Support the GPIO device in Spacemit SoCs. The GPIOs are arranged
+ into a number of banks (different for each SoC type) each with 32
+ GPIOs.
+
config SUNXI_GPIO
bool "Allwinner GPIO driver"
depends on ARCH_SUNXI
@@ -459,9 +467,9 @@ config SUNXI_GPIO
config SUNXI_NEW_PINCTRL
bool
depends on SUNXI_GPIO
- ---help---
- The Allwinner D1 and other new SoCs use a different register map
- for the GPIO block, which we need to know about in the SPL.
+ help
+ The Allwinner D1 and other new SoCs use a different register map
+ for the GPIO block, which we need to know about in the SPL.
config XILINX_GPIO
bool "Xilinx GPIO driver"
@@ -728,15 +736,15 @@ config SLG7XL45106_I2C_GPO
bool "slg7xl45106 i2c gpo expander"
depends on DM_GPIO && ARCH_ZYNQMP
help
- Support for slg7xl45106 i2c gpo expander. It is an i2c based
- 8-bit gpo expander, all gpo lines are controlled by writing
- value into data register.
+ Support for slg7xl45106 i2c gpo expander. It is an i2c based
+ 8-bit gpo expander, all gpo lines are controlled by writing
+ value into data register.
config GPIO_SCMI
bool "SCMI GPIO pinctrl driver"
depends on DM_GPIO && PINCTRL_SCMI
help
- Support pinctrl GPIO over the SCMI interface.
+ Support pinctrl GPIO over the SCMI interface.
config ADP5585_GPIO
bool "ADP5585 GPIO driver"
@@ -756,10 +764,11 @@ config SPL_ADP5585_GPIO
depends on SPL_DM_GPIO && SPL_I2C
help
Support ADP5585 GPIO expander in SPL.
+
config MPFS_GPIO
bool "Enable Polarfire SoC GPIO driver"
depends on DM_GPIO
help
- Enable to support the GPIO driver on Polarfire SoC
+ Enable to support the GPIO driver on Polarfire SoC
endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 863557e45ce..020e23c7252 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(PHASE_)PCF8575_GPIO) += pcf8575_gpio.o
obj-$(CONFIG_$(PHASE_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o qcom_spmi_gpio.o
+obj-$(CONFIG_$(PHASE_)SPACEMIT_GPIO) += spacemit_gpio.o
obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o
obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
diff --git a/drivers/gpio/gpio-aspeed-g7.c b/drivers/gpio/gpio-aspeed-g7.c
index 4607468ca05..ae330173f38 100644
--- a/drivers/gpio/gpio-aspeed-g7.c
+++ b/drivers/gpio/gpio-aspeed-g7.c
@@ -131,7 +131,7 @@ static int aspeed_gpio_probe(struct udevice *dev)
uc_priv->bank_name = dev->name;
ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count);
- priv->regs = devfdt_get_addr_ptr(dev);
+ priv->regs = dev_read_addr_ptr(dev);
return 0;
}
diff --git a/drivers/gpio/gpio-aspeed-sgpio.c b/drivers/gpio/gpio-aspeed-sgpio.c
index 4bbdec756f3..d6144d5706b 100644
--- a/drivers/gpio/gpio-aspeed-sgpio.c
+++ b/drivers/gpio/gpio-aspeed-sgpio.c
@@ -223,9 +223,9 @@ static int aspeed_sgpio_probe(struct udevice *dev)
ulong apb_freq;
int ret;
- priv->base = devfdt_get_addr_ptr(dev);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
priv->pdata = (const struct aspeed_sgpio_pdata *)dev_get_driver_data(dev);
if (!priv->pdata)
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index c5608f4a9df..54a786c4dc0 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -275,7 +275,7 @@ static int aspeed_gpio_probe(struct udevice *dev)
uc_priv->bank_name = dev->name;
ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count);
- priv->regs = devfdt_get_addr_ptr(dev);
+ priv->regs = dev_read_addr_ptr(dev);
return 0;
}
diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c
index c8d2dff5f7b..180799139b3 100644
--- a/drivers/gpio/gpio-fxl6408.c
+++ b/drivers/gpio/gpio-fxl6408.c
@@ -273,7 +273,7 @@ static int fxl6408_probe(struct udevice *dev)
u32 val32;
addr = dev_read_addr(dev);
- if (addr == 0)
+ if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
info->addr = addr;
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 7651d5360d6..4d40738e5aa 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -877,8 +877,19 @@ static int get_function(struct udevice *dev, int offset, bool skip_unused,
return -ENODEV;
if (offset < 0 || offset >= uc_priv->gpio_count)
return -EINVAL;
- if (namep)
+ if (namep) {
*namep = uc_priv->name[offset];
+ /* Fall back to DT "gpio-line-names" for unrequested pins. */
+ if (CONFIG_IS_ENABLED(DM_GPIO_LOOKUP_LINE_NAME) &&
+ (!*namep || !**namep)) {
+ const char *dt_name = NULL;
+
+ if (!dev_read_string_index(dev, "gpio-line-names",
+ offset, &dt_name) &&
+ dt_name && *dt_name)
+ *namep = dt_name;
+ }
+ }
if (skip_unused && !gpio_is_claimed(uc_priv, offset))
return GPIOF_UNUSED;
if (ops->get_function) {
diff --git a/drivers/gpio/imx_rgpio2p.c b/drivers/gpio/imx_rgpio2p.c
index 7cf178f8a48..ba3c5fcf25b 100644
--- a/drivers/gpio/imx_rgpio2p.c
+++ b/drivers/gpio/imx_rgpio2p.c
@@ -194,11 +194,11 @@ static int imx_rgpio2p_bind(struct udevice *dev)
dual_base = true;
if (dual_base) {
- addr = devfdt_get_addr_index(dev, 1);
+ addr = dev_read_addr_index(dev, 1);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
} else {
- addr = devfdt_get_addr_index(dev, 0);
+ addr = dev_read_addr_index(dev, 0);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
diff --git a/drivers/gpio/mpc8xxx_gpio.c b/drivers/gpio/mpc8xxx_gpio.c
index 709d04017d1..40646407369 100644
--- a/drivers/gpio/mpc8xxx_gpio.c
+++ b/drivers/gpio/mpc8xxx_gpio.c
@@ -171,6 +171,58 @@ static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio)
return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
}
+static int mpc8xxx_gpio_set_flags(struct udevice *dev, uint gpio,
+ ulong flags)
+{
+ u32 mask = gpio_mask(gpio);
+ int ret;
+
+ /* The QorIQ GPIO pad supports open-drain only; open-source has
+ * no silicon counterpart, so reject it rather than silently
+ * pretending.
+ */
+ if (flags & GPIOD_OPEN_SOURCE)
+ return -EOPNOTSUPP;
+
+ /* GPODR is per-pin and meaningful in both directions (it stays
+ * latched when the pin is re-purposed), so apply it before the
+ * direction change.
+ */
+ if (flags & GPIOD_OPEN_DRAIN)
+ mpc8xxx_gpio_open_drain_on(dev, mask);
+ else
+ mpc8xxx_gpio_open_drain_off(dev, mask);
+
+ if (flags & GPIOD_IS_OUT) {
+ ret = mpc8xxx_gpio_direction_output(dev, gpio,
+ !!(flags & GPIOD_IS_OUT_ACTIVE));
+ } else if (flags & GPIOD_IS_IN) {
+ ret = mpc8xxx_gpio_direction_input(dev, gpio);
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int mpc8xxx_gpio_get_flags(struct udevice *dev, uint gpio,
+ ulong *flagsp)
+{
+ u32 mask = gpio_mask(gpio);
+ ulong flags = 0;
+
+ if (mpc8xxx_gpio_get_dir(dev, mask))
+ flags |= GPIOD_IS_OUT;
+ else
+ flags |= GPIOD_IS_IN;
+
+ if (mpc8xxx_gpio_open_drain_val(dev, mask))
+ flags |= GPIOD_OPEN_DRAIN;
+
+ *flagsp = flags;
+ return 0;
+}
+
#if CONFIG_IS_ENABLED(OF_CONTROL)
static int mpc8xxx_gpio_of_to_plat(struct udevice *dev)
{
@@ -255,6 +307,8 @@ static const struct dm_gpio_ops gpio_mpc8xxx_ops = {
.get_value = mpc8xxx_gpio_get_value,
.set_value = mpc8xxx_gpio_set_value,
.get_function = mpc8xxx_gpio_get_function,
+ .set_flags = mpc8xxx_gpio_set_flags,
+ .get_flags = mpc8xxx_gpio_get_flags,
};
static const struct udevice_id mpc8xxx_gpio_ids[] = {
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c
index 5abbb34daea..1c3d27eb1cc 100644
--- a/drivers/gpio/nx_gpio.c
+++ b/drivers/gpio/nx_gpio.c
@@ -213,9 +213,10 @@ static int nx_gpio_of_to_plat(struct udevice *dev)
{
struct nx_gpio_plat *plat = dev_get_plat(dev);
- plat->regs = map_physmem(devfdt_get_addr(dev),
- sizeof(struct nx_gpio_regs),
- MAP_NOCACHE);
+ plat->regs = dev_remap_addr(dev);
+ if (!plat->regs)
+ return -EINVAL;
+
plat->gpio_count = dev_read_s32_default(dev, "nexell,gpio-bank-width",
32);
plat->bank_name = dev_read_string(dev, "gpio-bank-name");
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
index 523ca8473a8..965a5fcf30b 100644
--- a/drivers/gpio/pca953x_gpio.c
+++ b/drivers/gpio/pca953x_gpio.c
@@ -312,7 +312,7 @@ static int pca953x_probe(struct udevice *dev)
u8 val[MAX_BANK];
addr = dev_read_addr(dev);
- if (addr == 0)
+ if (addr == FDT_ADDR_T_NONE)
return -ENODEV;
info->addr = addr;
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c
index 4458c55cd3d..ad4a70dd49b 100644
--- a/drivers/gpio/qcom_pmic_gpio.c
+++ b/drivers/gpio/qcom_pmic_gpio.c
@@ -344,7 +344,6 @@ static int qcom_gpio_probe(struct udevice *dev)
static const struct udevice_id qcom_gpio_ids[] = {
{ .compatible = "qcom,pm8916-gpio" },
{ .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */
- { .compatible = "qcom,pm8998-gpio" },
{ .compatible = "qcom,pms405-gpio" },
{ .compatible = "qcom,pm6125-gpio" },
{ .compatible = "qcom,pm8150-gpio" },
@@ -410,7 +409,7 @@ static int qcom_pmic_pinctrl_generic_pinmux_set_mux(struct udevice *dev, unsigne
return 0;
}
-struct pinctrl_ops qcom_pmic_pinctrl_ops = {
+static const struct pinctrl_ops qcom_pmic_pinctrl_ops = {
.get_pins_count = qcom_pmic_pinctrl_get_pins_count,
.get_pin_name = qcom_pmic_pinctrl_get_pin_name,
.set_state = pinctrl_generic_set_state,
diff --git a/drivers/gpio/qcom_spmi_gpio.c b/drivers/gpio/qcom_spmi_gpio.c
index 1a7c7c48dfc..3efee206007 100644
--- a/drivers/gpio/qcom_spmi_gpio.c
+++ b/drivers/gpio/qcom_spmi_gpio.c
@@ -747,10 +747,12 @@ static const struct udevice_id qcom_spmi_pmic_gpio_ids[] = {
{ .compatible = "qcom,pm6350-gpio" },
{ .compatible = "qcom,pm660l-gpio" },
{ .compatible = "qcom,pm7325-gpio" },
+ { .compatible = "qcom,pm7550-gpio" },
{ .compatible = "qcom,pm8550-gpio" },
{ .compatible = "qcom,pm8550b-gpio" },
{ .compatible = "qcom,pm8550ve-gpio" },
{ .compatible = "qcom,pm8550vs-gpio" },
+ { .compatible = "qcom,pm8998-gpio" },
{ .compatible = "qcom,pmk8550-gpio" },
{ .compatible = "qcom,pmr735d-gpio" },
{ }
@@ -1019,7 +1021,7 @@ static int qcom_spmi_pmic_pinctrl_pinmux_set_mux(struct udevice *dev, unsigned i
return spmi_pmic_gpio_write(plat, pad, PMIC_GPIO_REG_EN_CTL, val);
}
-struct pinctrl_ops qcom_spmi_pmic_pinctrl_ops = {
+static const struct pinctrl_ops qcom_spmi_pmic_pinctrl_ops = {
.get_pins_count = qcom_spmi_pmic_pinctrl_get_pins_count,
.get_pin_name = qcom_spmi_pmic_pinctrl_get_pin_name,
.set_state = pinctrl_generic_set_state,
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index e8f50d815d7..76aff0ed5aa 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -306,7 +306,7 @@ static int sb_gpio_get_name(const struct udevice *dev, char *out_name)
return acpi_copy_name(out_name, "GPIO");
}
-struct acpi_ops gpio_sandbox_acpi_ops = {
+static const struct acpi_ops gpio_sandbox_acpi_ops = {
.get_name = sb_gpio_get_name,
};
#endif /* ACPIGEN */
@@ -568,7 +568,7 @@ static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
};
#if CONFIG_IS_ENABLED(ACPIGEN)
-struct acpi_ops pinctrl_sandbox_acpi_ops = {
+static const struct acpi_ops pinctrl_sandbox_acpi_ops = {
.get_name = sb_pinctrl_get_name,
};
#endif
diff --git a/drivers/gpio/spacemit_gpio.c b/drivers/gpio/spacemit_gpio.c
new file mode 100644
index 00000000000..de71880df15
--- /dev/null
+++ b/drivers/gpio/spacemit_gpio.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ */
+
+#include <asm/gpio.h>
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <log.h>
+
+#define GPIO_BANK_SIZE 32
+#define GPIO_TO_BANK(pin) ((pin) / GPIO_BANK_SIZE)
+#define GPIO_TO_BIT(pin) ((pin) % GPIO_BANK_SIZE)
+
+static inline int gpio_to_reg_offset(unsigned int pin)
+{
+ unsigned int bank = GPIO_TO_BANK(pin);
+
+ if (bank == 0)
+ return 0;
+ else if (bank == 1)
+ return 4;
+ else if (bank == 2)
+ return 8;
+ else if (bank == 3)
+ return 0x100;
+ log_warning("Use default GPIO bank for an invalid GPIO[%d].\n", pin);
+ return 0;
+}
+
+#define REG_PLR(pin) (0x00 + gpio_to_reg_offset(pin))
+#define REG_PDR(pin) (0x0c + gpio_to_reg_offset(pin))
+#define REG_PSR(pin) (0x18 + gpio_to_reg_offset(pin))
+#define REG_PCR(pin) (0x24 + gpio_to_reg_offset(pin))
+#define REG_SDR(pin) (0x54 + gpio_to_reg_offset(pin))
+#define REG_CDR(pin) (0x60 + gpio_to_reg_offset(pin))
+
+struct spacemit_gpio_data {
+ u16 gpio_base;
+ u16 gpio_count;
+ u8 num_banks;
+};
+
+struct spacemit_gpio_priv {
+ void __iomem *regs;
+};
+
+static int spacemit_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ struct spacemit_gpio_data *data;
+ u32 bank, offset, flags;
+
+ data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
+ if (args->args_count < 3) {
+ dev_err(dev, "Invalid args count: %d, expected 3\n",
+ args->args_count);
+ return -EINVAL;
+ }
+ bank = args->args[0];
+ offset = args->args[1];
+ flags = args->args[2];
+
+ if (bank >= data->num_banks) {
+ dev_err(dev, "Invalid gpio bank: %u (max %u)\n",
+ bank, data->num_banks - 1);
+ return -EINVAL;
+ }
+ if (offset >= GPIO_BANK_SIZE) {
+ dev_err(dev, "Invalid offset: %u (max 31)\n", offset);
+ return -EINVAL;
+ }
+ desc->offset = bank * GPIO_BANK_SIZE + offset;
+ desc->flags = gpio_flags_xlate(flags);
+ return 0;
+}
+
+static int spacemit_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ u32 value, mask;
+
+ addr = priv->regs + REG_PLR(offset);
+ value = readl(addr);
+ mask = 1 << GPIO_TO_BIT(offset);
+ return !!(value & mask);
+}
+
+static int spacemit_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ u32 value, mask;
+
+ addr = priv->regs + REG_PDR(offset);
+ value = readl(addr);
+ mask = 1 << GPIO_TO_BIT(offset);
+ if (value & mask)
+ return GPIOF_OUTPUT;
+ return GPIOF_INPUT;
+}
+
+static int spacemit_gpio_get_flags(struct udevice *dev, unsigned int offset,
+ ulong *flagsp)
+{
+ ulong flags = 0;
+ u32 dir;
+
+ dir = spacemit_gpio_get_function(dev, offset);
+ if (dir) {
+ flags |= GPIOD_IS_OUT;
+ if (spacemit_gpio_get_value(dev, offset))
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ } else {
+ flags |= GPIOD_IS_IN;
+ }
+ *flagsp = flags;
+ return 0;
+}
+
+static int spacemit_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ struct spacemit_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *addr;
+ int value;
+
+ value = (flags & GPIOD_IS_OUT_ACTIVE) ? 1 : 0;
+ if (flags & GPIOD_IS_IN) {
+ addr = priv->regs + REG_CDR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ if (flags & GPIOD_IS_OUT) {
+ if (value) {
+ addr = priv->regs + REG_PSR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ } else {
+ addr = priv->regs + REG_PCR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ addr = priv->regs + REG_SDR(offset);
+ writel(1 << GPIO_TO_BIT(offset), addr);
+ }
+ return 0;
+}
+
+static int spacemit_gpio_request(struct udevice *dev, unsigned int offset,
+ const char *label)
+{
+ const struct pinctrl_ops *ops;
+ struct udevice *pctldev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
+ if (ret)
+ return ret;
+
+ ops = pinctrl_get_ops(pctldev);
+ if (!ops->gpio_request_enable)
+ return -ENOSYS;
+
+ return ops->gpio_request_enable(pctldev, offset);
+}
+
+static int spacemit_gpio_rfree(struct udevice *dev, unsigned int offset)
+{
+ const struct pinctrl_ops *ops;
+ struct udevice *pctldev;
+ int ret;
+
+ ret = uclass_first_device_err(UCLASS_PINCTRL, &pctldev);
+ if (ret)
+ return ret;
+
+ ops = pinctrl_get_ops(pctldev);
+ if (!ops->gpio_disable_free)
+ return -ENOSYS;
+
+ return ops->gpio_disable_free(pctldev, offset);
+}
+
+static const struct dm_gpio_ops spacemit_gpio_ops = {
+ .request = spacemit_gpio_request,
+ .rfree = spacemit_gpio_rfree,
+ .xlate = spacemit_gpio_xlate,
+ .get_value = spacemit_gpio_get_value,
+ .get_function = spacemit_gpio_get_function,
+ .get_flags = spacemit_gpio_get_flags,
+ .set_flags = spacemit_gpio_set_flags,
+};
+
+static int spacemit_gpio_probe(struct udevice *dev)
+{
+ struct spacemit_gpio_priv *priv;
+ struct spacemit_gpio_data *data;
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk_bulk clks;
+ int ret;
+
+ data = (struct spacemit_gpio_data *)dev_get_driver_data(dev);
+ priv = dev_get_priv(dev);
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs) {
+ dev_err(dev, "Fail to get base address\n");
+ return -EINVAL;
+ }
+ uc_priv->bank_name = "GPIO";
+ uc_priv->gpio_count = data->gpio_count;
+ uc_priv->gpio_base = data->gpio_base;
+
+ ret = clk_get_bulk(dev, &clks);
+ if (ret) {
+ dev_err(dev, "Fail to get bulk clks\n");
+ return ret;
+ }
+ ret = clk_enable_bulk(&clks);
+ if (ret) {
+ dev_err(dev, "Fail to enable bulk clks\n");
+ goto out;
+ }
+ return 0;
+out:
+ clk_release_bulk(&clks);
+ return ret;
+}
+
+static const struct spacemit_gpio_data k1_gpio_data = {
+ .num_banks = 4,
+ .gpio_count = 128,
+ .gpio_base = 0,
+};
+
+static const struct udevice_id spacemit_gpio_ids[] = {
+ { .compatible = "spacemit,k1-gpio", .data = (uintptr_t)&k1_gpio_data, },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(k1_gpio) = {
+ .name = "spacemit_k1_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = spacemit_gpio_ids,
+ .ops = &spacemit_gpio_ops,
+ .priv_auto = sizeof(struct spacemit_gpio_priv),
+ .probe = spacemit_gpio_probe,
+};