diff options
Diffstat (limited to 'drivers/gpio')
| -rw-r--r-- | drivers/gpio/74x164_gpio.c | 17 | ||||
| -rw-r--r-- | drivers/gpio/Kconfig | 61 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aspeed-g7.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aspeed-sgpio.c | 6 | ||||
| -rw-r--r-- | drivers/gpio/gpio-aspeed.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-fxl6408.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-uclass.c | 13 | ||||
| -rw-r--r-- | drivers/gpio/imx_rgpio2p.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/mpc8xxx_gpio.c | 54 | ||||
| -rw-r--r-- | drivers/gpio/nx_gpio.c | 7 | ||||
| -rw-r--r-- | drivers/gpio/pca953x_gpio.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/qcom_pmic_gpio.c | 3 | ||||
| -rw-r--r-- | drivers/gpio/qcom_spmi_gpio.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/sandbox.c | 4 | ||||
| -rw-r--r-- | drivers/gpio/spacemit_gpio.c | 253 |
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, +}; |
