summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/74x164_gpio.c193
-rw-r--r--drivers/gpio/Kconfig691
-rw-r--r--drivers/gpio/Makefile79
-rw-r--r--drivers/gpio/adp5585_gpio.c238
-rw-r--r--drivers/gpio/altera_pio.c123
-rw-r--r--drivers/gpio/at91_gpio.c636
-rw-r--r--drivers/gpio/atmel_pio4.c368
-rw-r--r--drivers/gpio/axp_gpio.c166
-rw-r--r--drivers/gpio/bcm2835_gpio.c142
-rw-r--r--drivers/gpio/bcm6345_gpio.c119
-rw-r--r--drivers/gpio/cortina_gpio.c113
-rw-r--r--drivers/gpio/da8xx_gpio.c577
-rw-r--r--drivers/gpio/da8xx_gpio.h41
-rw-r--r--drivers/gpio/dwapb_gpio.c235
-rw-r--r--drivers/gpio/ftgpio010.c111
-rw-r--r--drivers/gpio/gpio-aspeed.c298
-rw-r--r--drivers/gpio/gpio-fxl6408.c380
-rw-r--r--drivers/gpio/gpio-rcar.c197
-rw-r--r--drivers/gpio/gpio-rza1.c136
-rw-r--r--drivers/gpio/gpio-uclass.c1558
-rw-r--r--drivers/gpio/gpio-uniphier.c171
-rw-r--r--drivers/gpio/gpio_slg7xl45106.c117
-rw-r--r--drivers/gpio/hi6220_gpio.c91
-rw-r--r--drivers/gpio/hsdk-creg-gpio.c169
-rw-r--r--drivers/gpio/imx_rgpio2p.c239
-rw-r--r--drivers/gpio/intel_broadwell_gpio.c190
-rw-r--r--drivers/gpio/intel_gpio.c221
-rw-r--r--drivers/gpio/intel_ich6_gpio.c243
-rw-r--r--drivers/gpio/iproc_gpio.c290
-rw-r--r--drivers/gpio/kw_gpio.c149
-rw-r--r--drivers/gpio/lpc32xx_gpio.c321
-rw-r--r--drivers/gpio/max7320_gpio.c113
-rw-r--r--drivers/gpio/max77663_gpio.c178
-rw-r--r--drivers/gpio/mcp230xx_gpio.c235
-rw-r--r--drivers/gpio/mpc83xx_spisel_boot.c149
-rw-r--r--drivers/gpio/mpc8xx_gpio.c347
-rw-r--r--drivers/gpio/mpc8xxx_gpio.c273
-rw-r--r--drivers/gpio/mscc_sgpio.c279
-rw-r--r--drivers/gpio/msm_gpio.c133
-rw-r--r--drivers/gpio/mt7620_gpio.c146
-rw-r--r--drivers/gpio/mt7621_gpio.c183
-rw-r--r--drivers/gpio/mvebu_gpio.c126
-rw-r--r--drivers/gpio/mxc_gpio.c399
-rw-r--r--drivers/gpio/mxs_gpio.c311
-rw-r--r--drivers/gpio/nmk_gpio.c125
-rw-r--r--drivers/gpio/npcm_gpio.c123
-rw-r--r--drivers/gpio/nx_gpio.c251
-rw-r--r--drivers/gpio/octeon_gpio.c242
-rw-r--r--drivers/gpio/omap_gpio.c381
-rw-r--r--drivers/gpio/palmas_gpio.c132
-rw-r--r--drivers/gpio/pca953x.c295
-rw-r--r--drivers/gpio/pca953x_gpio.c425
-rw-r--r--drivers/gpio/pcf8575_gpio.c177
-rw-r--r--drivers/gpio/pic32_gpio.c166
-rw-r--r--drivers/gpio/qcom_pmic_gpio.c299
-rw-r--r--drivers/gpio/qe_gpio.c170
-rw-r--r--drivers/gpio/rk_gpio.c236
-rw-r--r--drivers/gpio/rzg2l-gpio.c169
-rw-r--r--drivers/gpio/s5p_gpio.c373
-rw-r--r--drivers/gpio/sandbox.c592
-rw-r--r--drivers/gpio/sandbox_test.c21
-rw-r--r--drivers/gpio/sh_pfc.c639
-rw-r--r--drivers/gpio/sifive-gpio.c176
-rw-r--r--drivers/gpio/sl28cpld-gpio.c165
-rw-r--r--drivers/gpio/stm32_gpio.c319
-rw-r--r--drivers/gpio/stm32_gpio_priv.h84
-rw-r--r--drivers/gpio/sunxi_gpio.c350
-rw-r--r--drivers/gpio/tca642x.c356
-rw-r--r--drivers/gpio/tegra186_gpio.c283
-rw-r--r--drivers/gpio/tegra186_gpio_priv.h61
-rw-r--r--drivers/gpio/tegra_gpio.c382
-rw-r--r--drivers/gpio/turris_omnia_mcu.c316
-rw-r--r--drivers/gpio/vybrid_gpio.c138
-rw-r--r--drivers/gpio/xilinx_gpio.c308
-rw-r--r--drivers/gpio/zynq_gpio.c411
-rw-r--r--drivers/gpio/zynqmp_gpio_modepin.c156
76 files changed, 19795 insertions, 0 deletions
diff --git a/drivers/gpio/74x164_gpio.c b/drivers/gpio/74x164_gpio.c
new file mode 100644
index 00000000000..7a7cfe86114
--- /dev/null
+++ b/drivers/gpio/74x164_gpio.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Take drivers/gpio/gpio-74x164.c as reference.
+ *
+ * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
+ *
+ * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
+ *
+ */
+
+#include <common.h>
+#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
+ *
+ * @oe: OE pin
+ * @nregs: number of registers
+ * @buffer: buffer for chained chips
+ */
+#define GEN_74X164_NUMBER_GPIOS 8
+
+struct gen_74x164_priv {
+ struct gpio_desc oe;
+ u32 nregs;
+ /*
+ * Since the nregs are chained, every byte sent will make
+ * the previous byte shift to the next register in the
+ * chain. Thus, the first byte sent will end up in the last
+ * register at the end of the transfer. So, to have a logical
+ * numbering, store the bytes in reverse order.
+ */
+ u8 *buffer;
+};
+
+static int gen_74x164_write_conf(struct udevice *dev)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_spi_claim_bus(dev);
+ if (ret)
+ return ret;
+
+ ret = dm_spi_xfer(dev, priv->nregs * 8, priv->buffer, NULL,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+
+ dm_spi_release_bus(dev);
+
+ return ret;
+}
+
+static int gen_74x164_get_value(struct udevice *dev, unsigned offset)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ uint bank = priv->nregs - 1 - offset / 8;
+ uint pin = offset % 8;
+
+ return (priv->buffer[bank] >> pin) & 0x1;
+}
+
+static int gen_74x164_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ uint bank = priv->nregs - 1 - offset / 8;
+ uint pin = offset % 8;
+ int ret;
+
+ if (value)
+ priv->buffer[bank] |= 1 << pin;
+ else
+ priv->buffer[bank] &= ~(1 << pin);
+
+ ret = gen_74x164_write_conf(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int gen_74x164_direction_input(struct udevice *dev, unsigned offset)
+{
+ return -ENOSYS;
+}
+
+static int gen_74x164_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return gen_74x164_set_value(dev, offset, value);
+}
+
+static int gen_74x164_get_function(struct udevice *dev, unsigned offset)
+{
+ return GPIOF_OUTPUT;
+}
+
+static int gen_74x164_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gen_74x164_ops = {
+ .direction_input = gen_74x164_direction_input,
+ .direction_output = gen_74x164_direction_output,
+ .get_value = gen_74x164_get_value,
+ .set_value = gen_74x164_set_value,
+ .get_function = gen_74x164_get_function,
+ .xlate = gen_74x164_xlate,
+};
+
+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);
+ 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)
+ return -ENOMEM;
+
+ /*
+ * See Linux kernel:
+ * Documentation/devicetree/bindings/gpio/gpio-74x164.txt
+ */
+ priv->nregs = fdtdec_get_int(fdt, node, "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)
+ dev_dbg(dev, "No registers-default property\n");
+
+ ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ dev_dbg(dev, "No oe-pins property\n");
+ }
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = priv->nregs * 8;
+
+ ret = gen_74x164_write_conf(dev);
+ if (ret)
+ goto free_buf;
+
+ dev_dbg(dev, "%s is ready\n", dev->name);
+
+ return 0;
+
+free_buf:
+ free(priv->buffer);
+free_str:
+ free(str);
+ return ret;
+}
+
+static const struct udevice_id gen_74x164_ids[] = {
+ { .compatible = "fairchild,74hc595" },
+ { }
+};
+
+U_BOOT_DRIVER(74x164) = {
+ .name = "74x164",
+ .id = UCLASS_GPIO,
+ .ops = &gen_74x164_ops,
+ .probe = gen_74x164_probe,
+ .priv_auto = sizeof(struct gen_74x164_priv),
+ .of_match = gen_74x164_ids,
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
new file mode 100644
index 00000000000..27df5d88d40
--- /dev/null
+++ b/drivers/gpio/Kconfig
@@ -0,0 +1,691 @@
+#
+# GPIO infrastructure and drivers
+#
+
+menuconfig GPIO
+ bool "GPIO support"
+ default y
+ help
+ Enable support for GPIOs (General-purpose Input/Output) in U-Boot.
+ GPIOs allow U-Boot to read the state of an input line (high or
+ low) and set the state of an output line. This can be used to
+ drive LEDs, control power to various system parts and read user
+ input. GPIOs can be useful to enable a 'sign-of-life' LED,
+ for example. Enable this option to build the drivers in
+ drivers/gpio as part of an U-Boot build.
+
+if GPIO
+
+config DM_GPIO
+ bool "Enable Driver Model for GPIO drivers"
+ depends on DM
+ help
+ Enable driver model for GPIO access. The standard GPIO
+ interface (gpio_get_value(), etc.) is then implemented by
+ the GPIO uclass. Drivers provide methods to query the
+ particular GPIOs that they provide. The uclass interface
+ is defined in include/asm-generic/gpio.h.
+
+config SPL_DM_GPIO
+ bool "Enable Driver Model for GPIO drivers in SPL"
+ depends on DM_GPIO && SPL_DM && SPL_GPIO
+ default y
+ help
+ Enable driver model for GPIO access in SPL. The standard GPIO
+ interface (gpio_get_value(), etc.) is then implemented by
+ the GPIO uclass. Drivers provide methods to query the
+ particular GPIOs that they provide. The uclass interface
+ is defined in include/asm-generic/gpio.h.
+
+config TPL_DM_GPIO
+ bool "Enable Driver Model for GPIO drivers in TPL"
+ depends on DM_GPIO && TPL_DM && TPL_GPIO
+ default y
+ help
+ Enable driver model for GPIO access in TPL. The standard GPIO
+ interface (gpio_get_value(), etc.) is then implemented by
+ the GPIO uclass. Drivers provide methods to query the
+ particular GPIOs that they provide. The uclass interface
+ is defined in include/asm-generic/gpio.h.
+
+config VPL_DM_GPIO
+ bool "Enable Driver Model for GPIO drivers in VPL"
+ depends on DM_GPIO && VPL_DM && VPL_GPIO
+ default y
+ help
+ Enable driver model for GPIO access in VPL. The standard GPIO
+ interface (gpio_get_value(), etc.) is then implemented by
+ the GPIO uclass. Drivers provide methods to query the
+ particular GPIOs that they provide. The uclass interface
+ is defined in include/asm-generic/gpio.h.
+
+config GPIO_HOG
+ bool "Enable GPIO hog support"
+ depends on DM_GPIO
+ help
+ Enable gpio hog support
+ The GPIO chip may contain GPIO hog definitions. GPIO hogging
+ is a mechanism providing automatic GPIO request and config-
+ uration as part of the gpio-controller's driver probe function.
+
+config SPL_GPIO_HOG
+ bool "Enable GPIO hog support in SPL"
+ depends on SPL_GPIO
+ help
+ Enable gpio hog support in SPL
+ The GPIO chip may contain GPIO hog definitions. GPIO hogging
+ is a mechanism providing automatic GPIO request and config-
+ uration as part of the gpio-controller's driver probe function.
+
+config DM_GPIO_LOOKUP_LABEL
+ bool "Enable searching for gpio labelnames"
+ depends on DM_GPIO
+ help
+ This option enables searching for gpio names in
+ the defined gpio labels, if the search for the
+ gpio bank name failed. This makes sense if you use
+ different gpios on different hardware versions
+ for the same functionality in board code.
+
+config SPL_DM_GPIO_LOOKUP_LABEL
+ bool "Enable searching for gpio labelnames"
+ depends on SPL_DM_GPIO
+ help
+ This option enables searching for gpio names in
+ the defined gpio labels, if the search for the
+ gpio bank name failed. This makes sense if you use
+ different gpios on different hardware versions
+ for the same functionality in board code.
+
+config ALTERA_PIO
+ bool "Altera PIO driver"
+ depends on DM_GPIO
+ help
+ Select this to enable PIO for Altera devices. Please find
+ details on the "Embedded Peripherals IP User Guide" of Altera.
+
+config BCM2835_GPIO
+ bool "BCM2835 GPIO driver"
+ depends on DM_GPIO
+
+config BCM6345_GPIO
+ bool "BCM6345 GPIO driver"
+ depends on DM_GPIO && (ARCH_BMIPS || BCM6856 || \
+ BCM6858 || BCM63158 || BCM6855)
+ help
+ This driver supports the GPIO banks on BCM6345 SoCs.
+
+config CORTINA_GPIO
+ bool "Cortina-Access GPIO driver"
+ depends on DM_GPIO && CORTINA_PLATFORM
+ help
+ Enable support for the GPIO controller in Cortina CAxxxx SoCs.
+ This driver supports all CPU ISA variants supported by Cortina
+ Access CAxxxx SoCs.
+
+config DWAPB_GPIO
+ bool "DWAPB GPIO driver"
+ depends on DM && DM_GPIO
+ help
+ Support for the Designware APB GPIO driver.
+
+config AT91_GPIO
+ bool "AT91 PIO GPIO driver"
+ help
+ Say yes here to select AT91 PIO GPIO driver. AT91 PIO
+ controller manages up to 32 fully programmable input/output
+ lines. Each I/O line may be dedicated as a general-purpose
+ I/O or be assigned to a function of an embedded peripheral.
+ The assignment to a function of an embedded peripheral is
+ the responsibility of AT91 Pinctrl driver. This driver is
+ responsible for the general-purpose I/O.
+
+config ATMEL_PIO4
+ bool "ATMEL PIO4 driver"
+ depends on DM_GPIO
+ help
+ Say yes here to support the Atmel PIO4 driver.
+ The PIO4 is new version of Atmel PIO controller, which manages
+ up to 128 fully programmable input/output lines. Each I/O line
+ may be dedicated as a general purpose I/O or be assigned to
+ a function of an embedded peripheral.
+
+config ASPEED_GPIO
+ bool "Aspeed GPIO Driver"
+ help
+ Say yes here to support the Aspeed GPIO driver. The controller
+ is found in the AST2400, AST2500 and AST2600 BMC SoCs and
+ provides access to over 200 GPIOs on each chip.
+
+config DA8XX_GPIO
+ bool "DA8xx GPIO Driver"
+ help
+ This driver supports the DA8xx GPIO controller
+
+config FXL6408_GPIO
+ bool "FXL6408 I2C GPIO expander driver"
+ depends on DM_GPIO && DM_I2C
+ help
+ This driver supports the Fairchild FXL6408 device. FXL6408 is a
+ fully configurable 8-bit I2C-controlled GPIO expander.
+
+config HIKEY_GPIO
+ bool "HI6220 GPIO driver"
+ depends on DM_GPIO
+
+config INTEL_BROADWELL_GPIO
+ bool "Intel Broadwell GPIO driver"
+ depends on DM
+ help
+ This driver supports Broadwell U devices which have an expanded
+ GPIO feature set. The difference is large enough to merit a separate
+ driver from the common Intel ICH6 driver. It supports a total of
+ 95 GPIOs which can be configured from the device tree.
+
+config INTEL_GPIO
+ bool "Intel generic GPIO driver"
+ depends on DM_GPIO
+ help
+ Say yes here to select Intel generic GPIO driver. This controller
+ supports recent chips (e.g. Apollo Lake). It permits basic GPIO
+ control including setting pins to input/output. It makes use of its
+ parent pinctrl driver to actually effect changes.
+
+config INTEL_ICH6_GPIO
+ bool "Intel ICH6 compatible legacy GPIO driver"
+ depends on DM_GPIO
+ help
+ Say yes here to select Intel ICH6 compatible legacy GPIO driver.
+
+config IMX_RGPIO2P
+ bool "i.MX7ULP RGPIO2P driver"
+ depends on DM
+ help
+ This driver supports i.MX7ULP Rapid GPIO2P controller.
+
+config IPROC_GPIO
+ bool "Broadcom iProc GPIO driver(without pinconf)"
+ help
+ The Broadcom iProc based SoCs- Cygnus, NS2, NS3, NSP and Stingray,
+ use the same GPIO Controller IP hence this driver could be used
+ for all.
+
+ The Broadcom iProc based SoCs have multiple GPIO controllers and only
+ the always-ON GPIO controller (CRMU/AON) is supported by this driver.
+
+config HSDK_CREG_GPIO
+ bool "HSDK CREG GPIO griver"
+ depends on DM_GPIO
+ help
+ This driver supports CREG GPIOs on Synopsys HSDK SOC.
+
+config KIRKWOOD_GPIO
+ bool "Kirkwood GPIO driver"
+ help
+ This drdiver supports GPIOs on Kirkwood platforms
+
+config LPC32XX_GPIO
+ bool "LPC32XX GPIO driver"
+ depends on DM
+ help
+ Support for the LPC32XX GPIO driver.
+
+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.
+
+config MAX77663_GPIO
+ bool "MAX77663 GPIO cell of PMIC driver"
+ depends on DM_GPIO && DM_PMIC_MAX77663
+ help
+ GPIO driver for MAX77663 PMIC from Maxim Semiconductor.
+ MAX77663 PMIC has 8 pins that can be configured as GPIOs
+ and 3 GPIO-like pins dedicated for power/reset buttons
+ and LID sensor.
+
+config MCP230XX_GPIO
+ bool "MCP230XX GPIO driver"
+ depends on DM
+ help
+ Support for Microchip's MCP230XX I2C connected GPIO devices.
+ The following chips are supported:
+ - MCP23008
+ - MCP23017
+ - MCP23018
+
+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.
+
+config MSM_GPIO
+ bool "Qualcomm GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO controllers on Qualcomm Snapdragon family of SoCs.
+ This controller have single bank (default name "soc"), every
+ gpio has it's own set of registers.
+ Only simple GPIO operations are supported (get/set, change of
+ direction and checking pin function).
+ Supported devices:
+ - APQ8016
+ - MSM8916
+
+config MXC_GPIO
+ bool "Freescale/NXP MXC GPIO driver"
+ help
+ Support GPIO controllers on various i.MX platforms
+
+config MXS_GPIO
+ bool "Freescale/NXP MXS GPIO driver"
+ help
+ Support GPIO controllers on i.MX23 and i.MX28 platforms
+
+config NPCM_GPIO
+ bool "Nuvoton NPCM GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO controllers on Nuvovon NPCM SoCs.
+ NPCM7xx/NPCM8xx contain 8 GPIO banks, each bank contains 32 pins.
+
+config OMAP_GPIO
+ bool "TI OMAP GPIO driver"
+ depends on ARCH_OMAP2PLUS
+ default y
+ help
+ Support GPIO controllers on the TI OMAP3/4/5 and related (such as
+ AM335x/AM43xx/AM57xx/DRA7xx/etc) families of SoCs.
+
+config CMD_PCA953X
+ bool "Enable the pca953x command"
+ help
+ Deprecated: This should be converted to driver model.
+
+ This command provides access to a pca953x GPIO device using the
+ legacy GPIO interface. Several subcommands are provided which mirror
+ the standard 'gpio' command. It should use that instead.
+
+config QCOM_PMIC_GPIO
+ bool "Qualcomm generic PMIC GPIO/keypad driver"
+ depends on DM_GPIO && PMIC_QCOM
+ select BUTTON
+ help
+ Support for GPIO pins and power/reset buttons found on
+ Qualcomm SoCs PMIC.
+ The GPIO bank is called "pmic"
+
+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.
+
+config RCAR_GPIO
+ bool "Renesas RCar GPIO driver"
+ depends on DM_GPIO && ARCH_RMOBILE
+ help
+ This driver supports the GPIO banks on Renesas RCar SoCs.
+
+config RZA1_GPIO
+ bool "Renesas RZ/A1 GPIO driver"
+ depends on DM_GPIO && RZA1
+ help
+ This driver supports the GPIO banks on Renesas RZ/A1 R7S72100 SoCs.
+
+config ROCKCHIP_GPIO
+ bool "Rockchip GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO access on Rockchip SoCs. The GPIOs are arranged into
+ a number of banks (different for each SoC type) each with 32 GPIOs.
+ The GPIOs for a device are defined in the device tree with one node
+ for each bank.
+
+config SANDBOX_GPIO
+ bool "Enable sandbox GPIO driver"
+ depends on SANDBOX && DM && DM_GPIO
+ help
+ This driver supports some simulated GPIOs which can be adjusted
+ using 'back door' functions like sandbox_gpio_set_value(). Then the
+ GPIOs can be inspected through the normal get_get_value()
+ interface. The purpose of this is to allow GPIOs to be used as
+ normal in sandbox, perhaps with test code actually driving the
+ behaviour of those GPIOs.
+
+config SANDBOX_GPIO_COUNT
+ int "Number of sandbox GPIOs"
+ depends on SANDBOX_GPIO
+ default 128
+ help
+ The sandbox driver can support any number of GPIOs. Generally these
+ are specified using the device tree. But you can also have a number
+ of 'anonymous' GPIOs that do not belong to any device or bank.
+ Select a suitable value depending on your needs.
+
+config SUNXI_GPIO
+ bool "Allwinner GPIO driver"
+ depends on ARCH_SUNXI
+ select SPL_STRTO if SPL
+ help
+ Support the GPIO device in Allwinner SoCs.
+
+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.
+
+config XILINX_GPIO
+ bool "Xilinx GPIO driver"
+ depends on DM_GPIO
+ help
+ This config enable the Xilinx GPIO driver for Microblaze.
+
+config TCA642X
+ bool "TCA642x legacy GPIO driver"
+
+config CMD_TCA642X
+ bool "tca642x - Command to access tca642x state"
+ depends on TCA642X
+ default y
+ help
+ DEPRECATED - This needs conversion to driver model
+
+ This provides a way to looking at the pin state of this device.
+ This mirrors the 'gpio' command and that should be used in preference
+ to custom code.
+
+config TEGRA_GPIO
+ bool "Tegra20..210 GPIO driver"
+ depends on DM_GPIO
+ help
+ Support for the GPIO controller contained in NVIDIA Tegra20 through
+ Tegra210.
+
+config TEGRA186_GPIO
+ bool "Tegra186 GPIO driver"
+ depends on DM_GPIO
+ help
+ Support for the GPIO controller contained in NVIDIA Tegra186. This
+ covers both the "main" and "AON" controller instances, even though
+ they have slightly different register layout.
+
+config GPIO_UNIPHIER
+ bool "UniPhier GPIO"
+ depends on ARCH_UNIPHIER
+ help
+ Say yes here to support UniPhier GPIOs.
+
+config VYBRID_GPIO
+ bool "Vybrid GPIO driver"
+ depends on DM
+ help
+ Say yes here to support Vybrid vf610 GPIOs.
+
+config PALMAS_GPIO
+ bool "TI PALMAS series PMICs GPIO"
+ depends on DM_GPIO && PMIC_PALMAS
+ help
+ Select this option to enable GPIO driver for the TI PALMAS
+ series chip family.
+
+config PIC32_GPIO
+ bool "Microchip PIC32 GPIO driver"
+ depends on DM_GPIO && MACH_PIC32
+ default y
+ help
+ Say yes here to support Microchip PIC32 GPIOs.
+
+config OCTEON_GPIO
+ bool "Octeon II/III/TX/TX2 GPIO driver"
+ depends on DM_GPIO && PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
+ default y
+ help
+ Add support for the Marvell Octeon GPIO driver. This is used with
+ various Octeon parts such as Octeon II/III and OcteonTX/TX2.
+ Octeon II/III has 32 GPIOs (count defined via DT) and OcteonTX/TX2
+ has 64 GPIOs (count defined via internal register).
+
+config STM32_GPIO
+ bool "ST STM32 GPIO driver"
+ depends on DM_GPIO && (ARCH_STM32 || ARCH_STM32MP)
+ default y
+ help
+ Device model driver support for STM32 GPIO controller. It should be
+ usable on many stm32 families like stm32f4/f7/h7 and stm32mp1.
+ Tested on STM32F7.
+
+config SIFIVE_GPIO
+ bool "SiFive GPIO driver"
+ depends on DM_GPIO
+ help
+ Device model driver for GPIO controller present in SiFive FU540 SoC. This
+ driver enables GPIO interface on HiFive Unleashed A00 board.
+
+config MVEBU_GPIO
+ bool "Marvell MVEBU GPIO driver"
+ depends on DM_GPIO && (ARCH_MVEBU || ARCH_KIRKWOOD)
+ default y
+ help
+ Say yes here to support Marvell MVEBU (Armada XP/38x) GPIOs.
+
+config ZYNQ_GPIO
+ bool "Zynq GPIO driver"
+ depends on DM_GPIO
+ default y if ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_VERSAL
+ help
+ Supports GPIO access on Zynq SoC.
+
+config DM_74X164
+ bool "74x164 serial-in/parallel-out 8-bits shift register"
+ depends on DM_GPIO
+ help
+ Driver for 74x164 compatible serial-in/parallel-out 8-outputs
+ shift registers, such as 74lv165, 74hc595.
+ This driver can be used to provide access to more gpio outputs.
+
+config DM_PCA953X
+ bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
+ depends on DM_GPIO && DM_I2C
+ help
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
+ pca9556, pca9557, pca9574, tca6408, xra1202
+
+ 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
+ tca6416
+
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
+ Now, max 24 bits chips and PCA953X compatible chips are
+ supported
+
+config SPL_DM_PCA953X
+ bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL"
+ depends on SPL_DM_GPIO
+ help
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
+ pca9556, pca9557, pca9574, tca6408, xra1202
+
+ 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
+ tca6416
+
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
+ Now, max 24 bits chips and PCA953X compatible chips are
+ supported
+
+config PCA953X
+ bool "NXP's PCA953X series I2C GPIO (legacy driver)"
+ depends on !DM_PCA953X
+
+config MPC8XXX_GPIO
+ bool "Freescale MPC8XXX GPIO driver"
+ depends on DM_GPIO
+ help
+ This driver supports the built-in GPIO controller of MPC8XXX CPUs.
+ Each GPIO bank is identified by its own entry in the device tree,
+ i.e.
+
+ gpio-controller@fc00 {
+ #gpio-cells = <2>;
+ compatible = "fsl,pq3-gpio";
+ reg = <0xfc00 0x100>
+ }
+
+ By default, each bank is assumed to have 32 GPIOs, but the ngpios
+ setting is honored, so the number of GPIOs for each bank is
+ configurable to match the actual GPIO count of the SoC (e.g. the
+ 32/32/23 banks of the P1022 SoC).
+
+ Aside from the standard functions of input/output mode, and output
+ value setting, the open-drain feature, which can configure individual
+ GPIOs to work as open-drain outputs, is supported.
+
+config QE_GPIO
+ bool "Freescale QUICC ENGINE GPIO driver"
+ depends on DM_GPIO
+ depends on QE
+ help
+ This driver supports the QUICC Engine GPIOs of MPC83XX CPUs.
+ Each GPIO bank is identified by its own entry in the device tree,
+ i.e.
+
+ qe_pio_a: gpio-controller@1400 {
+ compatible = "fsl,mpc8323-qe-pario-bank";
+ reg = <0x1400 0x18>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ Each bank has 32 GPIOs.
+
+config MPC8XX_GPIO
+ bool "Freescale MPC8XX GPIO driver"
+ depends on DM_GPIO
+ help
+ This driver supports parallel IO ports from MPC8XX CPUs.
+ Each GPIO bank is identified by its own entry in the device tree.
+
+config MPC83XX_SPISEL_BOOT
+ bool "Freescale MPC83XX SPISEL_BOOT driver"
+ depends on DM_GPIO && ARCH_MPC830X
+ help
+ GPIO driver to set/clear dedicated SPISEL_BOOT output on MPC83XX.
+
+ This pin is typically used as spi chip select to a spi nor flash.
+
+config MT7620_GPIO
+ bool "MediaTek MT7620 GPIO driver"
+ depends on DM_GPIO && SOC_MT7620
+ default y
+ help
+ Device model driver for GPIO controller present in MediaTek MT7620
+ and earlier SoCs.
+
+config MT7621_GPIO
+ bool "MediaTek MT7621 GPIO driver"
+ depends on DM_GPIO && (SOC_MT7621 || SOC_MT7628)
+ default y
+ help
+ Say yes here to support MediaTek MT7621 compatible GPIOs.
+
+config NX_GPIO
+ bool "Nexell GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO access on Nexell SoCs. The GPIOs are arranged into
+ a number of banks (different for each SoC type) each with 32 GPIOs.
+ The GPIOs for a device are defined in the device tree with one node
+ for each bank.
+
+config NOMADIK_GPIO
+ bool "Nomadik GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO access on ST-Ericsson Ux500 SoCs. The GPIOs are arranged
+ into a number of banks each with 32 GPIOs. The GPIOs for a device are
+ defined in the device tree with one node for each bank.
+
+config ZYNQMP_GPIO_MODEPIN
+ bool "ZynqMP gpio modepin"
+ depends on DM_GPIO
+ help
+ This config enables the ZynqMP gpio modepin driver. ZynqMP modepin
+ driver will set and get the status of PS_MODE pins. These modepins
+ are accessed using xilinx firmware. In modepin register, [3:0] bits
+ set direction, [7:4] bits read IO, [11:8] bits set/clear IO.
+
+config SH_GPIO_PFC
+ bool "Pinmuxed GPIO support for SuperH"
+ depends on RCAR_GEN2 && !PINCTRL_PFC
+ default y
+
+config SL28CPLD_GPIO
+ bool "Kontron sl28cpld GPIO driver"
+ depends on DM_GPIO && SL28CPLD
+ help
+ Support GPIO access on Kontron sl28cpld board management controllers.
+
+config SLG7XL45106_I2C_GPO
+ bool "slg7xl45106 i2c gpo expander"
+ depends on DM_GPIO
+ 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.
+
+config TURRIS_OMNIA_MCU
+ bool "Turris Omnia MCU GPIO driver"
+ depends on DM_GPIO
+ default y if TARGET_TURRIS_OMNIA
+ help
+ Support for GPIOs on MCU connected to Turris Omnia via i2c.
+
+config FTGPIO010
+ bool "Faraday Technology FTGPIO010 driver"
+ depends on DM_GPIO
+ help
+ Support for GPIOs on Faraday Technology's FTGPIO010 controller.
+
+config ADP5585_GPIO
+ bool "ADP5585 GPIO driver"
+ depends on DM_GPIO && DM_I2C
+ help
+ Support ADP5585 GPIO expander.
+
+config RZG2L_GPIO
+ bool "Renesas RZ/G2L family GPIO driver"
+ depends on DM_GPIO && PINCTRL_RZG2L
+ help
+ Support the gpio functionality of the pin function controller (PFC)
+ on the Renesas RZ/G2L SoC family.
+
+endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
new file mode 100644
index 00000000000..da3da5da2b3
--- /dev/null
+++ b/drivers/gpio/Makefile
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2000-2008
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_DWAPB_GPIO) += dwapb_gpio.o
+obj-$(CONFIG_AXP_GPIO) += axp_gpio.o
+obj-$(CONFIG_DM_74X164) += 74x164_gpio.o
+endif
+obj-$(CONFIG_$(SPL_TPL_)DM_GPIO) += gpio-uclass.o
+
+obj-$(CONFIG_$(SPL_)DM_PCA953X) += pca953x_gpio.o
+
+obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o
+obj-$(CONFIG_AT91_GPIO) += at91_gpio.o
+obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o
+obj-$(CONFIG_BCM6345_GPIO) += bcm6345_gpio.o
+obj-$(CONFIG_CORTINA_GPIO) += cortina_gpio.o
+obj-$(CONFIG_FXL6408_GPIO) += gpio-fxl6408.o
+obj-$(CONFIG_INTEL_GPIO) += intel_gpio.o
+obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o
+obj-$(CONFIG_INTEL_BROADWELL_GPIO) += intel_broadwell_gpio.o
+obj-$(CONFIG_IPROC_GPIO) += iproc_gpio.o
+obj-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
+obj-$(CONFIG_MCP230XX_GPIO) += mcp230xx_gpio.o
+obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o
+obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o
+obj-$(CONFIG_NPCM_GPIO) += npcm_gpio.o
+obj-$(CONFIG_PCA953X) += pca953x.o
+obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o
+obj-$(CONFIG_RCAR_GPIO) += gpio-rcar.o
+obj-$(CONFIG_RZA1_GPIO) += gpio-rza1.o
+obj-$(CONFIG_S5P) += s5p_gpio.o
+obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o sandbox_test.o
+obj-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o
+obj-$(CONFIG_TEGRA186_GPIO) += tegra186_gpio.o
+obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o
+obj-$(CONFIG_ALTERA_PIO) += altera_pio.o
+obj-$(CONFIG_MPC8XXX_GPIO) += mpc8xxx_gpio.o
+obj-$(CONFIG_QE_GPIO) += qe_gpio.o
+obj-$(CONFIG_MPC8XX_GPIO) += mpc8xx_gpio.o
+obj-$(CONFIG_MPC83XX_SPISEL_BOOT) += mpc83xx_spisel_boot.o
+obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o
+obj-$(CONFIG_OMAP_GPIO) += omap_gpio.o
+obj-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o
+obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o
+obj-$(CONFIG_TCA642X) += tca642x.o
+obj-$(CONFIG_SUNXI_GPIO) += sunxi_gpio.o
+obj-$(CONFIG_LPC32XX_GPIO) += lpc32xx_gpio.o
+obj-$(CONFIG_STM32_GPIO) += stm32_gpio.o
+obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
+obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o
+obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o
+obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o
+obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o
+obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o
+obj-$(CONFIG_$(SPL_)PALMAS_GPIO) += palmas_gpio.o
+obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o
+obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o
+obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
+obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
+obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o
+obj-$(CONFIG_$(SPL_TPL_)QCOM_PMIC_GPIO) += qcom_pmic_gpio.o
+obj-$(CONFIG_MT7620_GPIO) += mt7620_gpio.o
+obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o
+obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
+obj-$(CONFIG_NX_GPIO) += nx_gpio.o
+obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o
+obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o
+obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o
+obj-$(CONFIG_$(SPL_)MAX77663_GPIO) += max77663_gpio.o
+obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
+obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
+obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
+obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU) += turris_omnia_mcu.o
+obj-$(CONFIG_FTGPIO010) += ftgpio010.o
+obj-$(CONFIG_ADP5585_GPIO) += adp5585_gpio.o
+obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o
diff --git a/drivers/gpio/adp5585_gpio.c b/drivers/gpio/adp5585_gpio.c
new file mode 100644
index 00000000000..ea0cb75459b
--- /dev/null
+++ b/drivers/gpio/adp5585_gpio.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 NXP
+ *
+ * ADP5585 I/O Expander Controller
+ *
+ * Author: Alice Guo <alice.guo@nxp.com>
+ */
+
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <i2c.h>
+
+#define ADP5585_ID 0x00
+#define ADP5585_INT_STATUS 0x01
+#define ADP5585_STATUS 0x02
+#define ADP5585_FIFO_1 0x03
+#define ADP5585_FIFO_2 0x04
+#define ADP5585_FIFO_3 0x05
+#define ADP5585_FIFO_4 0x06
+#define ADP5585_FIFO_5 0x07
+#define ADP5585_FIFO_6 0x08
+#define ADP5585_FIFO_7 0x09
+#define ADP5585_FIFO_8 0x0A
+#define ADP5585_FIFO_9 0x0B
+#define ADP5585_FIFO_10 0x0C
+#define ADP5585_FIFO_11 0x0D
+#define ADP5585_FIFO_12 0x0E
+#define ADP5585_FIFO_13 0x0F
+#define ADP5585_FIFO_14 0x10
+#define ADP5585_FIFO_15 0x11
+#define ADP5585_FIFO_16 0x12
+#define ADP5585_GPI_INT_STAT_A 0x13
+#define ADP5585_GPI_INT_STAT_B 0x14
+#define ADP5585_GPI_STATUS_A 0x15
+#define ADP5585_GPI_STATUS_B 0x16
+#define ADP5585_RPULL_CONFIG_A 0x17
+#define ADP5585_RPULL_CONFIG_B 0x18
+#define ADP5585_RPULL_CONFIG_C 0x19
+#define ADP5585_RPULL_CONFIG_D 0x1A
+#define ADP5585_GPI_INT_LEVEL_A 0x1B
+#define ADP5585_GPI_INT_LEVEL_B 0x1C
+#define ADP5585_GPI_EVENT_EN_A 0x1D
+#define ADP5585_GPI_EVENT_EN_B 0x1E
+#define ADP5585_GPI_INTERRUPT_EN_A 0x1F
+#define ADP5585_GPI_INTERRUPT_EN_B 0x20
+#define ADP5585_DEBOUNCE_DIS_A 0x21
+#define ADP5585_DEBOUNCE_DIS_B 0x22
+#define ADP5585_GPO_DATA_OUT_A 0x23
+#define ADP5585_GPO_DATA_OUT_B 0x24
+#define ADP5585_GPO_OUT_MODE_A 0x25
+#define ADP5585_GPO_OUT_MODE_B 0x26
+#define ADP5585_GPIO_DIRECTION_A 0x27
+#define ADP5585_GPIO_DIRECTION_B 0x28
+#define ADP5585_RESET1_EVENT_A 0x29
+#define ADP5585_RESET1_EVENT_B 0x2A
+#define ADP5585_RESET1_EVENT_C 0x2B
+#define ADP5585_RESET2_EVENT_A 0x2C
+#define ADP5585_RESET2_EVENT_B 0x2D
+#define ADP5585_RESET_CFG 0x2E
+#define ADP5585_PWM_OFFT_LOW 0x2F
+#define ADP5585_PWM_OFFT_HIGH 0x30
+#define ADP5585_PWM_ONT_LOW 0x31
+#define ADP5585_PWM_ONT_HIGH 0x32
+#define ADP5585_PWM_CFG 0x33
+#define ADP5585_LOGIC_CFG 0x34
+#define ADP5585_LOGIC_FF_CFG 0x35
+#define ADP5585_LOGIC_INT_EVENT_EN 0x36
+#define ADP5585_POLL_PTIME_CFG 0x37
+#define ADP5585_PIN_CONFIG_A 0x38
+#define ADP5585_PIN_CONFIG_B 0x39
+#define ADP5585_PIN_CONFIG_D 0x3A
+#define ADP5585_GENERAL_CFG 0x3B
+#define ADP5585_INT_EN 0x3C
+
+#define ADP5585_MAXGPIO 10
+#define ADP5585_BANK(offs) ((offs) > 4)
+#define ADP5585_BIT(offs) ((offs) > 4 ? \
+ 1u << ((offs) - 5) : 1u << (offs))
+
+struct adp5585_plat {
+ fdt_addr_t addr;
+ u8 id;
+ u8 dat_out[2];
+ u8 dir[2];
+};
+
+static int adp5585_direction_input(struct udevice *dev, unsigned int offset)
+{
+ int ret;
+ unsigned int bank;
+ struct adp5585_plat *plat = dev_get_plat(dev);
+
+ bank = ADP5585_BANK(offset);
+
+ plat->dir[bank] &= ~ADP5585_BIT(offset);
+ ret = dm_i2c_write(dev, ADP5585_GPIO_DIRECTION_A + bank, &plat->dir[bank], 1);
+
+ return ret;
+}
+
+static int adp5585_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ int ret;
+ unsigned int bank, bit;
+ struct adp5585_plat *plat = dev_get_plat(dev);
+
+ bank = ADP5585_BANK(offset);
+ bit = ADP5585_BIT(offset);
+
+ plat->dir[bank] |= bit;
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = dm_i2c_write(dev, ADP5585_GPO_DATA_OUT_A + bank, &plat->dat_out[bank], 1);
+ ret |= dm_i2c_write(dev, ADP5585_GPIO_DIRECTION_A + bank, &plat->dir[bank], 1);
+
+ return ret;
+}
+
+static int adp5585_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct adp5585_plat *plat = dev_get_plat(dev);
+ unsigned int bank = ADP5585_BANK(offset);
+ unsigned int bit = ADP5585_BIT(offset);
+ u8 val;
+
+ if (plat->dir[bank] & bit)
+ val = plat->dat_out[bank];
+ else
+ dm_i2c_read(dev, ADP5585_GPI_STATUS_A + bank, &val, 1);
+
+ return !!(val & bit);
+}
+
+static int adp5585_set_value(struct udevice *dev, unsigned int offset, int value)
+{
+ int ret;
+ unsigned int bank, bit;
+ struct adp5585_plat *plat = dev_get_plat(dev);
+
+ bank = ADP5585_BANK(offset);
+ bit = ADP5585_BIT(offset);
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = dm_i2c_write(dev, ADP5585_GPO_DATA_OUT_A + bank, &plat->dat_out[bank], 1);
+
+ return ret;
+}
+
+static int adp5585_get_function(struct udevice *dev, unsigned int offset)
+{
+ unsigned int bank, bit, dir;
+ struct adp5585_plat *plat = dev_get_plat(dev);
+
+ bank = ADP5585_BANK(offset);
+ bit = ADP5585_BIT(offset);
+ dir = plat->dir[bank] & bit;
+
+ if (!dir)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static int adp5585_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops adp5585_ops = {
+ .direction_input = adp5585_direction_input,
+ .direction_output = adp5585_direction_output,
+ .get_value = adp5585_get_value,
+ .set_value = adp5585_set_value,
+ .get_function = adp5585_get_function,
+ .xlate = adp5585_xlate,
+};
+
+static int adp5585_probe(struct udevice *dev)
+{
+ struct adp5585_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ if (!plat)
+ return 0;
+
+ plat->addr = dev_read_addr(dev);
+ if (plat->addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ ret = dm_i2c_read(dev, ADP5585_ID, &plat->id, 1);
+ if (ret < 0)
+ return ret;
+
+ uc_priv->gpio_count = ADP5585_MAXGPIO;
+ uc_priv->bank_name = "adp5585-gpio";
+
+ for (int i = 0; i < 2; i++) {
+ ret = dm_i2c_read(dev, ADP5585_GPO_DATA_OUT_A + i, &plat->dat_out[i], 1);
+ if (ret)
+ return ret;
+
+ ret = dm_i2c_read(dev, ADP5585_GPIO_DIRECTION_A + i, &plat->dir[i], 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id adp5585_ids[] = {
+ { .compatible = "adp5585" },
+ { }
+};
+
+U_BOOT_DRIVER(adp5585) = {
+ .name = "adp5585",
+ .id = UCLASS_GPIO,
+ .of_match = adp5585_ids,
+ .probe = adp5585_probe,
+ .ops = &adp5585_ops,
+ .plat_auto = sizeof(struct adp5585_plat),
+};
diff --git a/drivers/gpio/altera_pio.c b/drivers/gpio/altera_pio.c
new file mode 100644
index 00000000000..edc5a8093b0
--- /dev/null
+++ b/drivers/gpio/altera_pio.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
+ * Copyright (C) 2011 Missing Link Electronics
+ * Joachim Foerster <joachim@missinglinkelectronics.com>
+ */
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fdtdec.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct altera_pio_regs {
+ u32 data; /* Data register */
+ u32 direction; /* Direction register */
+};
+
+struct altera_pio_plat {
+ struct altera_pio_regs *regs;
+ int gpio_count;
+ const char *bank_name;
+};
+
+static int altera_pio_direction_input(struct udevice *dev, unsigned pin)
+{
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+ struct altera_pio_regs *const regs = plat->regs;
+
+ clrbits_le32(&regs->direction, 1 << pin);
+
+ return 0;
+}
+
+static int altera_pio_direction_output(struct udevice *dev, unsigned pin,
+ int val)
+{
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+ struct altera_pio_regs *const regs = plat->regs;
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ clrbits_le32(&regs->data, 1 << pin);
+ /* change the data first, then the direction. to avoid glitch */
+ setbits_le32(&regs->direction, 1 << pin);
+
+ return 0;
+}
+
+static int altera_pio_get_value(struct udevice *dev, unsigned pin)
+{
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+ struct altera_pio_regs *const regs = plat->regs;
+
+ return !!(readl(&regs->data) & (1 << pin));
+}
+
+
+static int altera_pio_set_value(struct udevice *dev, unsigned pin, int val)
+{
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+ struct altera_pio_regs *const regs = plat->regs;
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ clrbits_le32(&regs->data, 1 << pin);
+
+ return 0;
+}
+
+static int altera_pio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+
+ uc_priv->gpio_count = plat->gpio_count;
+ uc_priv->bank_name = plat->bank_name;
+
+ return 0;
+}
+
+static int altera_pio_of_to_plat(struct udevice *dev)
+{
+ struct altera_pio_plat *plat = dev_get_plat(dev);
+
+ plat->regs = map_physmem(dev_read_addr(dev),
+ sizeof(struct altera_pio_regs),
+ MAP_NOCACHE);
+ plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "altr,gpio-bank-width", 32);
+ plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "gpio-bank-name", NULL);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops altera_pio_ops = {
+ .direction_input = altera_pio_direction_input,
+ .direction_output = altera_pio_direction_output,
+ .get_value = altera_pio_get_value,
+ .set_value = altera_pio_set_value,
+};
+
+static const struct udevice_id altera_pio_ids[] = {
+ { .compatible = "altr,pio-1.0" },
+ { }
+};
+
+U_BOOT_DRIVER(altera_pio) = {
+ .name = "altera_pio",
+ .id = UCLASS_GPIO,
+ .of_match = altera_pio_ids,
+ .ops = &altera_pio_ops,
+ .of_to_plat = altera_pio_of_to_plat,
+ .plat_auto = sizeof(struct altera_pio_plat),
+ .probe = altera_pio_probe,
+};
diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c
new file mode 100644
index 00000000000..f80f4afd24f
--- /dev/null
+++ b/drivers/gpio/at91_gpio.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Bo Shen <voice.shen@atmel.com>
+ *
+ * Copyright (C) 2009 Jens Scharsig (js_at_ng@scharsoft.de)
+ *
+ * Copyright (C) 2005 HP Labs
+ */
+
+#include <config.h>
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/sizes.h>
+#include <asm/gpio.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/at91_pio.h>
+
+#define GPIO_PER_BANK 32
+
+static struct at91_port *at91_pio_get_port(unsigned port)
+{
+ switch (port) {
+ case AT91_PIO_PORTA:
+ return (struct at91_port *)ATMEL_BASE_PIOA;
+ case AT91_PIO_PORTB:
+ return (struct at91_port *)ATMEL_BASE_PIOB;
+ case AT91_PIO_PORTC:
+ return (struct at91_port *)ATMEL_BASE_PIOC;
+#if (ATMEL_PIO_PORTS > 3)
+ case AT91_PIO_PORTD:
+ return (struct at91_port *)ATMEL_BASE_PIOD;
+#if (ATMEL_PIO_PORTS > 4)
+ case AT91_PIO_PORTE:
+ return (struct at91_port *)ATMEL_BASE_PIOE;
+#endif
+#endif
+ default:
+ printf("Error: at91_gpio: Fail to get PIO base!\n");
+ return NULL;
+ }
+}
+
+static void at91_set_port_pullup(struct at91_port *at91_port, unsigned offset,
+ int use_pullup)
+{
+ u32 mask;
+
+ mask = 1 << offset;
+ if (use_pullup)
+ writel(mask, &at91_port->puer);
+ else
+ writel(mask, &at91_port->pudr);
+ writel(mask, &at91_port->per);
+}
+
+int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ at91_set_port_pullup(at91_port, pin, use_pullup);
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "GPIO" peripheral role.
+ */
+int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &at91_port->per);
+ }
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "A" internal peripheral role.
+ */
+int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &at91_port->mux.pio2.asr);
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "B" internal peripheral role.
+ */
+int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &at91_port->mux.pio2.bsr);
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "A" internal peripheral role.
+ */
+int at91_pio3_set_a_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(readl(&at91_port->mux.pio3.abcdsr1) & ~mask,
+ &at91_port->mux.pio3.abcdsr1);
+ writel(readl(&at91_port->mux.pio3.abcdsr2) & ~mask,
+ &at91_port->mux.pio3.abcdsr2);
+
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "B" internal peripheral role.
+ */
+int at91_pio3_set_b_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(readl(&at91_port->mux.pio3.abcdsr1) | mask,
+ &at91_port->mux.pio3.abcdsr1);
+ writel(readl(&at91_port->mux.pio3.abcdsr2) & ~mask,
+ &at91_port->mux.pio3.abcdsr2);
+
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+/*
+ * mux the pin to the "C" internal peripheral role.
+ */
+int at91_pio3_set_c_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(readl(&at91_port->mux.pio3.abcdsr1) & ~mask,
+ &at91_port->mux.pio3.abcdsr1);
+ writel(readl(&at91_port->mux.pio3.abcdsr2) | mask,
+ &at91_port->mux.pio3.abcdsr2);
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+
+/*
+ * mux the pin to the "D" internal peripheral role.
+ */
+int at91_pio3_set_d_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(mask, &at91_port->idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(readl(&at91_port->mux.pio3.abcdsr1) | mask,
+ &at91_port->mux.pio3.abcdsr1);
+ writel(readl(&at91_port->mux.pio3.abcdsr2) | mask,
+ &at91_port->mux.pio3.abcdsr2);
+ writel(mask, &at91_port->pdr);
+ }
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+static bool at91_get_port_output(struct at91_port *at91_port, int offset)
+{
+ u32 mask, val;
+
+ mask = 1 << offset;
+ val = readl(&at91_port->osr);
+ return val & mask;
+}
+#endif
+
+static void at91_set_port_input(struct at91_port *at91_port, int offset,
+ int use_pullup)
+{
+ u32 mask;
+
+ mask = 1 << offset;
+ writel(mask, &at91_port->idr);
+ at91_set_port_pullup(at91_port, offset, use_pullup);
+ writel(mask, &at91_port->odr);
+ writel(mask, &at91_port->per);
+}
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
+ * configure it for an input.
+ */
+int at91_set_pio_input(unsigned port, u32 pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ at91_set_port_input(at91_port, pin, use_pullup);
+
+ return 0;
+}
+
+static void at91_set_port_output(struct at91_port *at91_port, int offset,
+ int value)
+{
+ u32 mask;
+
+ mask = 1 << offset;
+ writel(mask, &at91_port->idr);
+ writel(mask, &at91_port->pudr);
+ if (value)
+ writel(mask, &at91_port->sodr);
+ else
+ writel(mask, &at91_port->codr);
+ writel(mask, &at91_port->oer);
+ writel(mask, &at91_port->per);
+}
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral),
+ * and configure it for an output.
+ */
+int at91_set_pio_output(unsigned port, u32 pin, int value)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ at91_set_port_output(at91_port, pin, value);
+
+ return 0;
+}
+
+/*
+ * enable/disable the glitch filter. mostly used with IRQ handling.
+ */
+int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ if (is_on)
+ writel(mask, &at91_port->ifer);
+ else
+ writel(mask, &at91_port->ifdr);
+ }
+
+ return 0;
+}
+
+/*
+ * enable/disable the glitch filter. mostly used with IRQ handling.
+ */
+int at91_pio3_set_pio_deglitch(unsigned port, unsigned pin, int is_on)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ if (is_on) {
+ writel(mask, &at91_port->mux.pio3.ifscdr);
+ writel(mask, &at91_port->ifer);
+ } else {
+ writel(mask, &at91_port->ifdr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * enable/disable the debounce filter.
+ */
+int at91_pio3_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ if (is_on) {
+ writel(mask, &at91_port->mux.pio3.ifscer);
+ writel(div & PIO_SCDR_DIV, &at91_port->mux.pio3.scdr);
+ writel(mask, &at91_port->ifer);
+ } else {
+ writel(mask, &at91_port->ifdr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * enable/disable the pull-down.
+ * If pull-up already enabled while calling the function, we disable it.
+ */
+int at91_pio3_set_pio_pulldown(unsigned port, unsigned pin, int is_on)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ if (is_on) {
+ at91_set_pio_pullup(port, pin, 0);
+ writel(mask, &at91_port->mux.pio3.ppder);
+ } else
+ writel(mask, &at91_port->mux.pio3.ppddr);
+ }
+
+ return 0;
+}
+
+int at91_pio3_set_pio_pullup(unsigned port, unsigned pin, int use_pullup)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (use_pullup)
+ at91_pio3_set_pio_pulldown(port, pin, 0);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ at91_set_port_pullup(at91_port, pin, use_pullup);
+
+ return 0;
+}
+
+/*
+ * disable Schmitt trigger
+ */
+int at91_pio3_set_pio_disable_schmitt_trig(unsigned port, unsigned pin)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ writel(readl(&at91_port->schmitt) | mask,
+ &at91_port->schmitt);
+ }
+
+ return 0;
+}
+
+/*
+ * enable/disable the multi-driver. This is only valid for output and
+ * allows the output pin to run as an open collector output.
+ */
+int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+ u32 mask;
+
+ if (at91_port && (pin < GPIO_PER_BANK)) {
+ mask = 1 << pin;
+ if (is_on)
+ writel(mask, &at91_port->mder);
+ else
+ writel(mask, &at91_port->mddr);
+ }
+
+ return 0;
+}
+
+static void at91_set_port_value(struct at91_port *at91_port, int offset,
+ int value)
+{
+ u32 mask;
+
+ mask = 1 << offset;
+ if (value)
+ writel(mask, &at91_port->sodr);
+ else
+ writel(mask, &at91_port->codr);
+}
+
+/*
+ * assuming the pin is muxed as a gpio output, set its value.
+ */
+int at91_set_pio_value(unsigned port, unsigned pin, int value)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ at91_set_port_value(at91_port, pin, value);
+
+ return 0;
+}
+
+static int at91_get_port_value(struct at91_port *at91_port, int offset)
+{
+ u32 pdsr = 0, mask;
+
+ mask = 1 << offset;
+ pdsr = readl(&at91_port->pdsr) & mask;
+
+ return pdsr != 0;
+}
+/*
+ * read the pin's value (works even if it's not muxed as a gpio).
+ */
+int at91_get_pio_value(unsigned port, unsigned pin)
+{
+ struct at91_port *at91_port = at91_pio_get_port(port);
+
+ if (at91_port && (pin < GPIO_PER_BANK))
+ return at91_get_port_value(at91_port, pin);
+
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+/* Common GPIO API */
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ at91_set_pio_input(at91_gpio_to_port(gpio),
+ at91_gpio_to_pin(gpio), 0);
+ return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ at91_set_pio_output(at91_gpio_to_port(gpio),
+ at91_gpio_to_pin(gpio), value);
+ return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+ return at91_get_pio_value(at91_gpio_to_port(gpio),
+ at91_gpio_to_pin(gpio));
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+ at91_set_pio_value(at91_gpio_to_port(gpio),
+ at91_gpio_to_pin(gpio), value);
+
+ return 0;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+
+struct at91_port_priv {
+ struct at91_port *regs;
+};
+
+/* set GPIO pin 'gpio' as an input */
+static int at91_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+
+ at91_set_port_input(port->regs, offset, 0);
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int at91_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+
+ at91_set_port_output(port->regs, offset, value);
+
+ return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int at91_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+
+ return at91_get_port_value(port->regs, offset);
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int at91_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+
+ at91_set_port_value(port->regs, offset, value);
+
+ return 0;
+}
+
+static int at91_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+
+ /* GPIOF_FUNC is not implemented yet */
+ if (at91_get_port_output(port->regs, offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const char *at91_get_bank_name(uint32_t base_addr)
+{
+ switch (base_addr) {
+ case ATMEL_BASE_PIOA:
+ return "PIOA";
+ case ATMEL_BASE_PIOB:
+ return "PIOB";
+ case ATMEL_BASE_PIOC:
+ return "PIOC";
+#if (ATMEL_PIO_PORTS > 3)
+ case ATMEL_BASE_PIOD:
+ return "PIOD";
+#if (ATMEL_PIO_PORTS > 4)
+ case ATMEL_BASE_PIOE:
+ return "PIOE";
+#endif
+#endif
+ }
+
+ return "undefined";
+}
+
+static const struct dm_gpio_ops gpio_at91_ops = {
+ .direction_input = at91_gpio_direction_input,
+ .direction_output = at91_gpio_direction_output,
+ .get_value = at91_gpio_get_value,
+ .set_value = at91_gpio_set_value,
+ .get_function = at91_gpio_get_function,
+};
+
+static int at91_gpio_probe(struct udevice *dev)
+{
+ struct at91_port_priv *port = dev_get_priv(dev);
+ struct at91_port_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct clk clk;
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ plat->base_addr = dev_read_addr(dev);
+#endif
+ plat->bank_name = at91_get_bank_name(plat->base_addr);
+ port->regs = (struct at91_port *)plat->base_addr;
+
+ uc_priv->bank_name = plat->bank_name;
+ uc_priv->gpio_count = GPIO_PER_BANK;
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static const struct udevice_id at91_gpio_ids[] = {
+ { .compatible = "atmel,at91rm9200-gpio" },
+ { }
+};
+#endif
+
+U_BOOT_DRIVER(atmel_at91rm9200_gpio) = {
+ .name = "atmel_at91rm9200_gpio",
+ .id = UCLASS_GPIO,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ .of_match = at91_gpio_ids,
+ .plat_auto = sizeof(struct at91_port_plat),
+#endif
+ .ops = &gpio_at91_ops,
+ .probe = at91_gpio_probe,
+ .priv_auto = sizeof(struct at91_port_priv),
+};
+#endif
diff --git a/drivers/gpio/atmel_pio4.c b/drivers/gpio/atmel_pio4.c
new file mode 100644
index 00000000000..be1dd752bf7
--- /dev/null
+++ b/drivers/gpio/atmel_pio4.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Atmel PIO4 device driver
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ * Wenyou.Yang <wenyou.yang@atmel.com>
+ */
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/arch/hardware.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+#include <mach/gpio.h>
+#include <mach/atmel_pio4.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct atmel_pio4_port *atmel_pio4_port_base(u32 port)
+{
+ struct atmel_pio4_port *base = NULL;
+
+ switch (port) {
+ case AT91_PIO_PORTA:
+ base = (struct atmel_pio4_port *)ATMEL_BASE_PIOA;
+ break;
+ case AT91_PIO_PORTB:
+ base = (struct atmel_pio4_port *)ATMEL_BASE_PIOB;
+ break;
+ case AT91_PIO_PORTC:
+ base = (struct atmel_pio4_port *)ATMEL_BASE_PIOC;
+ break;
+ case AT91_PIO_PORTD:
+ base = (struct atmel_pio4_port *)ATMEL_BASE_PIOD;
+ break;
+#if (ATMEL_PIO_PORTS > 4)
+ case AT91_PIO_PORTE:
+ base = (struct atmel_pio4_port *)ATMEL_BASE_PIOE;
+ break;
+#endif
+ default:
+ printf("Error: Atmel PIO4: Failed to get PIO base of port#%d!\n",
+ port);
+ break;
+ }
+
+ return base;
+}
+
+static int atmel_pio4_config_io_func(u32 port, u32 pin,
+ u32 func, u32 config)
+{
+ struct atmel_pio4_port *port_base;
+ u32 reg, mask;
+
+ if (pin >= ATMEL_PIO_NPINS_PER_BANK)
+ return -EINVAL;
+
+ port_base = atmel_pio4_port_base(port);
+ if (!port_base)
+ return -EINVAL;
+
+ mask = 1 << pin;
+ reg = func;
+ reg |= config;
+
+ writel(mask, &port_base->mskr);
+ writel(reg, &port_base->cfgr);
+
+ return 0;
+}
+
+int atmel_pio4_set_gpio(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_GPIO,
+ config);
+}
+
+int atmel_pio4_set_a_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_A,
+ config);
+}
+
+int atmel_pio4_set_b_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_B,
+ config);
+}
+
+int atmel_pio4_set_c_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_C,
+ config);
+}
+
+int atmel_pio4_set_d_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_D,
+ config);
+}
+
+int atmel_pio4_set_e_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_E,
+ config);
+}
+
+int atmel_pio4_set_f_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_F,
+ config);
+}
+
+int atmel_pio4_set_g_periph(u32 port, u32 pin, u32 config)
+{
+ return atmel_pio4_config_io_func(port, pin,
+ ATMEL_PIO_CFGR_FUNC_PERIPH_G,
+ config);
+}
+
+int atmel_pio4_set_pio_output(u32 port, u32 pin, u32 value)
+{
+ struct atmel_pio4_port *port_base;
+ u32 reg, mask;
+
+ if (pin >= ATMEL_PIO_NPINS_PER_BANK)
+ return -EINVAL;
+
+ port_base = atmel_pio4_port_base(port);
+ if (!port_base)
+ return -EINVAL;
+
+ mask = 0x01 << pin;
+ reg = ATMEL_PIO_CFGR_FUNC_GPIO | ATMEL_PIO_DIR_MASK;
+
+ writel(mask, &port_base->mskr);
+ writel(reg, &port_base->cfgr);
+
+ if (value)
+ writel(mask, &port_base->sodr);
+ else
+ writel(mask, &port_base->codr);
+
+ return 0;
+}
+
+int atmel_pio4_get_pio_input(u32 port, u32 pin)
+{
+ struct atmel_pio4_port *port_base;
+ u32 reg, mask;
+
+ if (pin >= ATMEL_PIO_NPINS_PER_BANK)
+ return -EINVAL;
+
+ port_base = atmel_pio4_port_base(port);
+ if (!port_base)
+ return -EINVAL;
+
+ mask = 0x01 << pin;
+ reg = ATMEL_PIO_CFGR_FUNC_GPIO;
+
+ writel(mask, &port_base->mskr);
+ writel(reg, &port_base->cfgr);
+
+ return (readl(&port_base->pdsr) & mask) ? 1 : 0;
+}
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+
+/**
+ * struct atmel_pioctrl_data - Atmel PIO controller (pinmux + gpio) data struct
+ * @nbanks: number of PIO banks
+ * @last_bank_count: number of lines in the last bank (can be less than
+ * the rest of the banks).
+ */
+struct atmel_pioctrl_data {
+ u32 nbanks;
+ u32 last_bank_count;
+};
+
+struct atmel_pio4_plat {
+ struct atmel_pio4_port *reg_base;
+};
+
+static struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev,
+ u32 bank)
+{
+ struct atmel_pio4_plat *plat = dev_get_plat(dev);
+ struct atmel_pio4_port *port_base =
+ (struct atmel_pio4_port *)((u32)plat->reg_base +
+ ATMEL_PIO_BANK_OFFSET * bank);
+
+ return port_base;
+}
+
+static int atmel_pio4_direction_input(struct udevice *dev, unsigned offset)
+{
+ u32 bank = ATMEL_PIO_BANK(offset);
+ u32 line = ATMEL_PIO_LINE(offset);
+ struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
+ u32 mask = BIT(line);
+
+ writel(mask, &port_base->mskr);
+
+ clrbits_le32(&port_base->cfgr,
+ ATMEL_PIO_CFGR_FUNC_MASK | ATMEL_PIO_DIR_MASK);
+
+ return 0;
+}
+
+static int atmel_pio4_direction_output(struct udevice *dev,
+ unsigned offset, int value)
+{
+ u32 bank = ATMEL_PIO_BANK(offset);
+ u32 line = ATMEL_PIO_LINE(offset);
+ struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
+ u32 mask = BIT(line);
+
+ writel(mask, &port_base->mskr);
+
+ clrsetbits_le32(&port_base->cfgr,
+ ATMEL_PIO_CFGR_FUNC_MASK, ATMEL_PIO_DIR_MASK);
+
+ if (value)
+ writel(mask, &port_base->sodr);
+ else
+ writel(mask, &port_base->codr);
+
+ return 0;
+}
+
+static int atmel_pio4_get_value(struct udevice *dev, unsigned offset)
+{
+ u32 bank = ATMEL_PIO_BANK(offset);
+ u32 line = ATMEL_PIO_LINE(offset);
+ struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
+ u32 mask = BIT(line);
+
+ return (readl(&port_base->pdsr) & mask) ? 1 : 0;
+}
+
+static int atmel_pio4_set_value(struct udevice *dev,
+ unsigned offset, int value)
+{
+ u32 bank = ATMEL_PIO_BANK(offset);
+ u32 line = ATMEL_PIO_LINE(offset);
+ struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
+ u32 mask = BIT(line);
+
+ if (value)
+ writel(mask, &port_base->sodr);
+ else
+ writel(mask, &port_base->codr);
+
+ return 0;
+}
+
+static int atmel_pio4_get_function(struct udevice *dev, unsigned offset)
+{
+ u32 bank = ATMEL_PIO_BANK(offset);
+ u32 line = ATMEL_PIO_LINE(offset);
+ struct atmel_pio4_port *port_base = atmel_pio4_bank_base(dev, bank);
+ u32 mask = BIT(line);
+
+ writel(mask, &port_base->mskr);
+
+ return (readl(&port_base->cfgr) &
+ ATMEL_PIO_DIR_MASK) ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops atmel_pio4_ops = {
+ .direction_input = atmel_pio4_direction_input,
+ .direction_output = atmel_pio4_direction_output,
+ .get_value = atmel_pio4_get_value,
+ .set_value = atmel_pio4_set_value,
+ .get_function = atmel_pio4_get_function,
+};
+
+static int atmel_pio4_bind(struct udevice *dev)
+{
+ return dm_scan_fdt_dev(dev);
+}
+
+static int atmel_pio4_probe(struct udevice *dev)
+{
+ struct atmel_pio4_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct atmel_pioctrl_data *pioctrl_data;
+ struct clk clk;
+ fdt_addr_t addr_base;
+ u32 nbanks;
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return ret;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ addr_base = dev_read_addr(dev);
+ if (addr_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->reg_base = (struct atmel_pio4_port *)addr_base;
+
+ pioctrl_data = (struct atmel_pioctrl_data *)dev_get_driver_data(dev);
+ nbanks = pioctrl_data->nbanks;
+
+ uc_priv->bank_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev),
+ NULL);
+ uc_priv->gpio_count = nbanks * ATMEL_PIO_NPINS_PER_BANK;
+
+ /* if last bank has limited number of pins, adjust accordingly */
+ if (pioctrl_data->last_bank_count != ATMEL_PIO_NPINS_PER_BANK) {
+ uc_priv->gpio_count -= ATMEL_PIO_NPINS_PER_BANK;
+ uc_priv->gpio_count += pioctrl_data->last_bank_count;
+ }
+
+ return 0;
+}
+
+/*
+ * The number of banks can be different from a SoC to another one.
+ * We can have up to 16 banks.
+ */
+static const struct atmel_pioctrl_data atmel_sama5d2_pioctrl_data = {
+ .nbanks = 4,
+ .last_bank_count = ATMEL_PIO_NPINS_PER_BANK,
+};
+
+static const struct atmel_pioctrl_data microchip_sama7g5_pioctrl_data = {
+ .nbanks = 5,
+ .last_bank_count = 8, /* 5th bank has only 8 lines on sama7g5 */
+};
+
+static const struct udevice_id atmel_pio4_ids[] = {
+ {
+ .data = (ulong)&atmel_sama5d2_pioctrl_data,
+ }, {
+ .data = (ulong)&microchip_sama7g5_pioctrl_data,
+ },
+ {}
+};
+
+U_BOOT_DRIVER(gpio_atmel_pio4) = {
+ .name = "gpio_atmel_pio4",
+ .id = UCLASS_GPIO,
+ .ops = &atmel_pio4_ops,
+ .probe = atmel_pio4_probe,
+ .bind = atmel_pio4_bind,
+ .of_match = atmel_pio4_ids,
+ .plat_auto = sizeof(struct atmel_pio4_plat),
+};
+
+#endif
diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c
new file mode 100644
index 00000000000..af6631697f5
--- /dev/null
+++ b/drivers/gpio/axp_gpio.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Hans de Goede <hdegoede@redhat.com>
+ *
+ * X-Powers AXP Power Management ICs gpio driver
+ */
+
+#include <common.h>
+#include <asm/arch/pmic_bus.h>
+#include <asm/gpio.h>
+#include <axp_pmic.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/root.h>
+#include <errno.h>
+#include <sunxi_gpio.h>
+
+static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val);
+
+static u8 axp_get_gpio_ctrl_reg(unsigned pin)
+{
+ switch (pin) {
+ case 0: return AXP_GPIO0_CTRL;
+ case 1: return AXP_GPIO1_CTRL;
+#ifdef AXP_GPIO2_CTRL
+ case 2: return AXP_GPIO2_CTRL;
+#endif
+#ifdef AXP_GPIO3_CTRL
+ case 3: return AXP_GPIO3_CTRL;
+#endif
+ }
+ return 0;
+}
+
+static int axp_gpio_direction_input(struct udevice *dev, unsigned pin)
+{
+ u8 reg;
+
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
+
+ return pmic_bus_write(reg, AXP_GPIO_CTRL_INPUT);
+}
+
+static int axp_gpio_direction_output(struct udevice *dev, unsigned pin,
+ int val)
+{
+ __maybe_unused int ret;
+ u8 reg;
+
+ switch (pin) {
+#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
+ /* Only available on later PMICs */
+ case SUNXI_GPIO_AXP0_VBUS_ENABLE:
+ ret = pmic_bus_clrbits(AXP_MISC_CTRL,
+ AXP_MISC_CTRL_N_VBUSEN_FUNC);
+ if (ret)
+ return ret;
+
+ return axp_gpio_set_value(dev, pin, val);
+#endif
+ default:
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
+
+ return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
+ AXP_GPIO_CTRL_OUTPUT_LOW);
+ }
+}
+
+static int axp_gpio_get_value(struct udevice *dev, unsigned pin)
+{
+ u8 reg, val, mask;
+ int ret;
+
+ switch (pin) {
+#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
+ /* Only available on later PMICs */
+ case SUNXI_GPIO_AXP0_VBUS_ENABLE:
+ ret = pmic_bus_read(AXP_VBUS_IPSOUT, &val);
+ mask = AXP_VBUS_IPSOUT_DRIVEBUS;
+ break;
+#endif
+ default:
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
+
+ ret = pmic_bus_read(AXP_GPIO_STATE, &val);
+ mask = 1 << (pin + AXP_GPIO_STATE_OFFSET);
+ }
+ if (ret)
+ return ret;
+
+ return (val & mask) ? 1 : 0;
+}
+
+static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val)
+{
+ u8 reg;
+
+ switch (pin) {
+#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC
+ /* Only available on later PMICs */
+ case SUNXI_GPIO_AXP0_VBUS_ENABLE:
+ if (val)
+ return pmic_bus_setbits(AXP_VBUS_IPSOUT,
+ AXP_VBUS_IPSOUT_DRIVEBUS);
+ else
+ return pmic_bus_clrbits(AXP_VBUS_IPSOUT,
+ AXP_VBUS_IPSOUT_DRIVEBUS);
+#endif
+ default:
+ reg = axp_get_gpio_ctrl_reg(pin);
+ if (reg == 0)
+ return -EINVAL;
+
+ return pmic_bus_write(reg, val ? AXP_GPIO_CTRL_OUTPUT_HIGH :
+ AXP_GPIO_CTRL_OUTPUT_LOW);
+ }
+}
+
+static const struct dm_gpio_ops gpio_axp_ops = {
+ .direction_input = axp_gpio_direction_input,
+ .direction_output = axp_gpio_direction_output,
+ .get_value = axp_gpio_get_value,
+ .set_value = axp_gpio_set_value,
+};
+
+static int gpio_axp_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Tell the uclass how many GPIOs we have */
+ uc_priv->bank_name = strdup(SUNXI_GPIO_AXP0_PREFIX);
+ uc_priv->gpio_count = SUNXI_GPIO_AXP0_GPIO_COUNT;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_axp) = {
+ .name = "gpio_axp",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_axp_ops,
+ .probe = gpio_axp_probe,
+};
+
+int axp_gpio_init(void)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = pmic_bus_init();
+ if (ret)
+ return ret;
+
+ /* There is no devicetree support for the axp yet, so bind directly */
+ ret = device_bind_driver(dm_root(), "gpio_axp", "AXP-gpio", &dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpio/bcm2835_gpio.c b/drivers/gpio/bcm2835_gpio.c
new file mode 100644
index 00000000000..704a6fa7121
--- /dev/null
+++ b/drivers/gpio/bcm2835_gpio.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Vikram Narayananan
+ * <vikram186@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <fdtdec.h>
+
+struct bcm2835_gpios {
+ struct bcm2835_gpio_regs *reg;
+ struct udevice *pinctrl;
+};
+
+static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
+{
+ struct bcm2835_gpios *gpios = dev_get_priv(dev);
+ unsigned val;
+
+ val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+ val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ val |= (BCM2835_GPIO_INPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ writel(val, &gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+ return 0;
+}
+
+static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned int gpio,
+ int value)
+{
+ struct bcm2835_gpios *gpios = dev_get_priv(dev);
+ unsigned val;
+
+ gpio_set_value(gpio, value);
+
+ val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+ val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ val |= (BCM2835_GPIO_OUTPUT << BCM2835_GPIO_FSEL_SHIFT(gpio));
+ writel(val, &gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+ return 0;
+}
+
+static int bcm2835_get_value(const struct bcm2835_gpios *gpios, unsigned gpio)
+{
+ unsigned val;
+
+ val = readl(&gpios->reg->gplev[BCM2835_GPIO_COMMON_BANK(gpio)]);
+
+ return (val >> BCM2835_GPIO_COMMON_SHIFT(gpio)) & 0x1;
+}
+
+static int bcm2835_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ const struct bcm2835_gpios *gpios = dev_get_priv(dev);
+
+ return bcm2835_get_value(gpios, gpio);
+}
+
+static int bcm2835_gpio_set_value(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct bcm2835_gpios *gpios = dev_get_priv(dev);
+ u32 *output_reg = value ? gpios->reg->gpset : gpios->reg->gpclr;
+
+ writel(1 << BCM2835_GPIO_COMMON_SHIFT(gpio),
+ &output_reg[BCM2835_GPIO_COMMON_BANK(gpio)]);
+
+ return 0;
+}
+
+static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct bcm2835_gpios *priv = dev_get_priv(dev);
+ int funcid;
+
+ funcid = pinctrl_get_gpio_mux(priv->pinctrl, 0, offset);
+
+ switch (funcid) {
+ case BCM2835_GPIO_OUTPUT:
+ return GPIOF_OUTPUT;
+ case BCM2835_GPIO_INPUT:
+ return GPIOF_INPUT;
+ default:
+ return GPIOF_FUNC;
+ }
+}
+
+static const struct dm_gpio_ops gpio_bcm2835_ops = {
+ .direction_input = bcm2835_gpio_direction_input,
+ .direction_output = bcm2835_gpio_direction_output,
+ .get_value = bcm2835_gpio_get_value,
+ .set_value = bcm2835_gpio_set_value,
+ .get_function = bcm2835_gpio_get_function,
+};
+
+static int bcm2835_gpio_probe(struct udevice *dev)
+{
+ struct bcm2835_gpios *gpios = dev_get_priv(dev);
+ struct bcm2835_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->bank_name = "GPIO";
+ uc_priv->gpio_count = BCM2835_GPIO_COUNT;
+ gpios->reg = (struct bcm2835_gpio_regs *)plat->base;
+
+ /* We know we're spawned by the pinctrl driver */
+ gpios->pinctrl = dev->parent;
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static int bcm2835_gpio_of_to_plat(struct udevice *dev)
+{
+ struct bcm2835_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->base = addr;
+ return 0;
+}
+#endif
+
+U_BOOT_DRIVER(gpio_bcm2835) = {
+ .name = "gpio_bcm2835",
+ .id = UCLASS_GPIO,
+ .of_to_plat = of_match_ptr(bcm2835_gpio_of_to_plat),
+ .plat_auto = sizeof(struct bcm2835_gpio_plat),
+ .ops = &gpio_bcm2835_ops,
+ .probe = bcm2835_gpio_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+ .priv_auto = sizeof(struct bcm2835_gpios),
+};
diff --git a/drivers/gpio/bcm6345_gpio.c b/drivers/gpio/bcm6345_gpio.c
new file mode 100644
index 00000000000..e031f71a784
--- /dev/null
+++ b/drivers/gpio/bcm6345_gpio.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
+ *
+ * Derived from linux/arch/mips/bcm63xx/gpio.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+struct bcm6345_gpio_priv {
+ void __iomem *reg_dirout;
+ void __iomem *reg_data;
+};
+
+static int bcm6345_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(readl(priv->reg_data) & BIT(offset));
+}
+
+static int bcm6345_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ if (value)
+ setbits_32(priv->reg_data, BIT(offset));
+ else
+ clrbits_32(priv->reg_data, BIT(offset));
+
+ return 0;
+}
+
+static int bcm6345_gpio_set_direction(void __iomem *dirout, unsigned offset,
+ bool input)
+{
+ if (input)
+ clrbits_32(dirout, BIT(offset));
+ else
+ setbits_32(dirout, BIT(offset));
+
+ return 0;
+}
+
+static int bcm6345_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 1);
+}
+
+static int bcm6345_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ bcm6345_gpio_set_value(dev, offset, value);
+
+ return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 0);
+}
+
+static int bcm6345_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ if (readl(priv->reg_dirout) & BIT(offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops bcm6345_gpio_ops = {
+ .direction_input = bcm6345_gpio_direction_input,
+ .direction_output = bcm6345_gpio_direction_output,
+ .get_value = bcm6345_gpio_get_value,
+ .set_value = bcm6345_gpio_set_value,
+ .get_function = bcm6345_gpio_get_function,
+};
+
+static int bcm6345_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct bcm6345_gpio_priv *priv = dev_get_priv(dev);
+
+ priv->reg_dirout = dev_remap_addr_index(dev, 0);
+ if (!priv->reg_dirout)
+ return -EINVAL;
+
+ priv->reg_data = dev_remap_addr_index(dev, 1);
+ if (!priv->reg_data)
+ return -EINVAL;
+
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", 32);
+ uc_priv->bank_name = dev->name;
+
+ return 0;
+}
+
+static const struct udevice_id bcm6345_gpio_ids[] = {
+ { .compatible = "brcm,bcm6345-gpio" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(bcm6345_gpio) = {
+ .name = "bcm6345-gpio",
+ .id = UCLASS_GPIO,
+ .of_match = bcm6345_gpio_ids,
+ .ops = &bcm6345_gpio_ops,
+ .priv_auto = sizeof(struct bcm6345_gpio_priv),
+ .probe = bcm6345_gpio_probe,
+};
diff --git a/drivers/gpio/cortina_gpio.c b/drivers/gpio/cortina_gpio.c
new file mode 100644
index 00000000000..72ef523be96
--- /dev/null
+++ b/drivers/gpio/cortina_gpio.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Cortina-Access
+ *
+ * GPIO Driver for Cortina Access CAxxxx Line of SoCs
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <linux/compiler.h>
+
+/* GPIO Register Map */
+#define CORTINA_GPIO_CFG 0x00
+#define CORTINA_GPIO_OUT 0x04
+#define CORTINA_GPIO_IN 0x08
+#define CORTINA_GPIO_LVL 0x0C
+#define CORTINA_GPIO_EDGE 0x10
+#define CORTINA_GPIO_BOTHEDGE 0x14
+#define CORTINA_GPIO_IE 0x18
+#define CORTINA_GPIO_INT 0x1C
+#define CORTINA_GPIO_STAT 0x20
+
+struct cortina_gpio_bank {
+ void __iomem *base;
+};
+
+#ifdef CONFIG_DM_GPIO
+static int ca_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ setbits_32(priv->base, BIT(offset));
+ return 0;
+}
+
+static int
+ca_gpio_direction_output(struct udevice *dev, unsigned int offset, int value)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ clrbits_32(priv->base, BIT(offset));
+ return 0;
+}
+
+static int ca_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ return readl(priv->base + CORTINA_GPIO_IN) & BIT(offset);
+}
+
+static int ca_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ setbits_32(priv->base + CORTINA_GPIO_OUT, BIT(offset));
+ return 0;
+}
+
+static int ca_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ if (readl(priv->base) & BIT(offset))
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops gpio_cortina_ops = {
+ .direction_input = ca_gpio_direction_input,
+ .direction_output = ca_gpio_direction_output,
+ .get_value = ca_gpio_get_value,
+ .set_value = ca_gpio_set_value,
+ .get_function = ca_gpio_get_function,
+};
+
+static int ca_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct cortina_gpio_bank *priv = dev_get_priv(dev);
+
+ priv->base = dev_remap_addr_index(dev, 0);
+ if (!priv->base)
+ return -EINVAL;
+
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", 32);
+ uc_priv->bank_name = dev->name;
+
+ debug("Done Cortina GPIO init\n");
+ return 0;
+}
+
+static const struct udevice_id ca_gpio_ids[] = {
+ {.compatible = "cortina,ca-gpio"},
+ {}
+};
+
+U_BOOT_DRIVER(cortina_gpio) = {
+ .name = "cortina-gpio",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_cortina_ops,
+ .probe = ca_gpio_probe,
+ .priv_auto = sizeof(struct cortina_gpio_bank),
+ .of_match = ca_gpio_ids,
+};
+#endif /* CONFIG_DM_GPIO */
diff --git a/drivers/gpio/da8xx_gpio.c b/drivers/gpio/da8xx_gpio.c
new file mode 100644
index 00000000000..b310f2dbf65
--- /dev/null
+++ b/drivers/gpio/da8xx_gpio.c
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPIO driver for TI DaVinci DA8xx SOCs.
+ *
+ * (C) Copyright 2011 Guralp Systems Ltd.
+ * Laurence Withers <lwithers@guralp.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#include "da8xx_gpio.h"
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+#include <asm/arch/hardware.h>
+#include <asm/arch/davinci_misc.h>
+
+static struct gpio_registry {
+ int is_registered;
+ char name[GPIO_NAME_SIZE];
+} gpio_registry[MAX_NUM_GPIOS];
+
+#if defined(CONFIG_SOC_DA8XX)
+#define pinmux(x) (&davinci_syscfg_regs->pinmux[x])
+
+#if defined(CONFIG_SOC_DA8XX) && !defined(CONFIG_SOC_DA850)
+static const struct pinmux_config gpio_pinmux[] = {
+ { pinmux(13), 8, 6 }, /* GP0[0] */
+ { pinmux(13), 8, 7 },
+ { pinmux(14), 8, 0 },
+ { pinmux(14), 8, 1 },
+ { pinmux(14), 8, 2 },
+ { pinmux(14), 8, 3 },
+ { pinmux(14), 8, 4 },
+ { pinmux(14), 8, 5 },
+ { pinmux(14), 8, 6 },
+ { pinmux(14), 8, 7 },
+ { pinmux(15), 8, 0 },
+ { pinmux(15), 8, 1 },
+ { pinmux(15), 8, 2 },
+ { pinmux(15), 8, 3 },
+ { pinmux(15), 8, 4 },
+ { pinmux(15), 8, 5 },
+ { pinmux(15), 8, 6 }, /* GP1[0] */
+ { pinmux(15), 8, 7 },
+ { pinmux(16), 8, 0 },
+ { pinmux(16), 8, 1 },
+ { pinmux(16), 8, 2 },
+ { pinmux(16), 8, 3 },
+ { pinmux(16), 8, 4 },
+ { pinmux(16), 8, 5 },
+ { pinmux(16), 8, 6 },
+ { pinmux(16), 8, 7 },
+ { pinmux(17), 8, 0 },
+ { pinmux(17), 8, 1 },
+ { pinmux(17), 8, 2 },
+ { pinmux(17), 8, 3 },
+ { pinmux(17), 8, 4 },
+ { pinmux(17), 8, 5 },
+ { pinmux(17), 8, 6 }, /* GP2[0] */
+ { pinmux(17), 8, 7 },
+ { pinmux(18), 8, 0 },
+ { pinmux(18), 8, 1 },
+ { pinmux(18), 8, 2 },
+ { pinmux(18), 8, 3 },
+ { pinmux(18), 8, 4 },
+ { pinmux(18), 8, 5 },
+ { pinmux(18), 8, 6 },
+ { pinmux(18), 8, 7 },
+ { pinmux(19), 8, 0 },
+ { pinmux(9), 8, 2 },
+ { pinmux(9), 8, 3 },
+ { pinmux(9), 8, 4 },
+ { pinmux(9), 8, 5 },
+ { pinmux(9), 8, 6 },
+ { pinmux(10), 8, 1 }, /* GP3[0] */
+ { pinmux(10), 8, 2 },
+ { pinmux(10), 8, 3 },
+ { pinmux(10), 8, 4 },
+ { pinmux(10), 8, 5 },
+ { pinmux(10), 8, 6 },
+ { pinmux(10), 8, 7 },
+ { pinmux(11), 8, 0 },
+ { pinmux(11), 8, 1 },
+ { pinmux(11), 8, 2 },
+ { pinmux(11), 8, 3 },
+ { pinmux(11), 8, 4 },
+ { pinmux(9), 8, 7 },
+ { pinmux(2), 8, 6 },
+ { pinmux(11), 8, 5 },
+ { pinmux(11), 8, 6 },
+ { pinmux(12), 8, 4 }, /* GP4[0] */
+ { pinmux(12), 8, 5 },
+ { pinmux(12), 8, 6 },
+ { pinmux(12), 8, 7 },
+ { pinmux(13), 8, 0 },
+ { pinmux(13), 8, 1 },
+ { pinmux(13), 8, 2 },
+ { pinmux(13), 8, 3 },
+ { pinmux(13), 8, 4 },
+ { pinmux(13), 8, 5 },
+ { pinmux(11), 8, 7 },
+ { pinmux(12), 8, 0 },
+ { pinmux(12), 8, 1 },
+ { pinmux(12), 8, 2 },
+ { pinmux(12), 8, 3 },
+ { pinmux(9), 8, 1 },
+ { pinmux(7), 8, 3 }, /* GP5[0] */
+ { pinmux(7), 8, 4 },
+ { pinmux(7), 8, 5 },
+ { pinmux(7), 8, 6 },
+ { pinmux(7), 8, 7 },
+ { pinmux(8), 8, 0 },
+ { pinmux(8), 8, 1 },
+ { pinmux(8), 8, 2 },
+ { pinmux(8), 8, 3 },
+ { pinmux(8), 8, 4 },
+ { pinmux(8), 8, 5 },
+ { pinmux(8), 8, 6 },
+ { pinmux(8), 8, 7 },
+ { pinmux(9), 8, 0 },
+ { pinmux(7), 8, 1 },
+ { pinmux(7), 8, 2 },
+ { pinmux(5), 8, 1 }, /* GP6[0] */
+ { pinmux(5), 8, 2 },
+ { pinmux(5), 8, 3 },
+ { pinmux(5), 8, 4 },
+ { pinmux(5), 8, 5 },
+ { pinmux(5), 8, 6 },
+ { pinmux(5), 8, 7 },
+ { pinmux(6), 8, 0 },
+ { pinmux(6), 8, 1 },
+ { pinmux(6), 8, 2 },
+ { pinmux(6), 8, 3 },
+ { pinmux(6), 8, 4 },
+ { pinmux(6), 8, 5 },
+ { pinmux(6), 8, 6 },
+ { pinmux(6), 8, 7 },
+ { pinmux(7), 8, 0 },
+ { pinmux(1), 8, 0 }, /* GP7[0] */
+ { pinmux(1), 8, 1 },
+ { pinmux(1), 8, 2 },
+ { pinmux(1), 8, 3 },
+ { pinmux(1), 8, 4 },
+ { pinmux(1), 8, 5 },
+ { pinmux(1), 8, 6 },
+ { pinmux(1), 8, 7 },
+ { pinmux(2), 8, 0 },
+ { pinmux(2), 8, 1 },
+ { pinmux(2), 8, 2 },
+ { pinmux(2), 8, 3 },
+ { pinmux(2), 8, 4 },
+ { pinmux(2), 8, 5 },
+ { pinmux(0), 1, 0 },
+ { pinmux(0), 1, 1 },
+};
+#else /* CONFIG_SOC_DA8XX && CONFIG_SOC_DA850 */
+static const struct pinmux_config gpio_pinmux[] = {
+ { pinmux(1), 8, 7 }, /* GP0[0] */
+ { pinmux(1), 8, 6 },
+ { pinmux(1), 8, 5 },
+ { pinmux(1), 8, 4 },
+ { pinmux(1), 8, 3 },
+ { pinmux(1), 8, 2 },
+ { pinmux(1), 8, 1 },
+ { pinmux(1), 8, 0 },
+ { pinmux(0), 8, 7 },
+ { pinmux(0), 8, 6 },
+ { pinmux(0), 8, 5 },
+ { pinmux(0), 8, 4 },
+ { pinmux(0), 8, 3 },
+ { pinmux(0), 8, 2 },
+ { pinmux(0), 8, 1 },
+ { pinmux(0), 8, 0 },
+ { pinmux(4), 8, 7 }, /* GP1[0] */
+ { pinmux(4), 8, 6 },
+ { pinmux(4), 8, 5 },
+ { pinmux(4), 8, 4 },
+ { pinmux(4), 8, 3 },
+ { pinmux(4), 8, 2 },
+ { pinmux(4), 4, 1 },
+ { pinmux(4), 4, 0 },
+ { pinmux(3), 4, 0 },
+ { pinmux(2), 4, 6 },
+ { pinmux(2), 4, 5 },
+ { pinmux(2), 4, 4 },
+ { pinmux(2), 4, 3 },
+ { pinmux(2), 4, 2 },
+ { pinmux(2), 4, 1 },
+ { pinmux(2), 8, 0 },
+ { pinmux(6), 8, 7 }, /* GP2[0] */
+ { pinmux(6), 8, 6 },
+ { pinmux(6), 8, 5 },
+ { pinmux(6), 8, 4 },
+ { pinmux(6), 8, 3 },
+ { pinmux(6), 8, 2 },
+ { pinmux(6), 8, 1 },
+ { pinmux(6), 8, 0 },
+ { pinmux(5), 8, 7 },
+ { pinmux(5), 8, 6 },
+ { pinmux(5), 8, 5 },
+ { pinmux(5), 8, 4 },
+ { pinmux(5), 8, 3 },
+ { pinmux(5), 8, 2 },
+ { pinmux(5), 8, 1 },
+ { pinmux(5), 8, 0 },
+ { pinmux(8), 8, 7 }, /* GP3[0] */
+ { pinmux(8), 8, 6 },
+ { pinmux(8), 8, 5 },
+ { pinmux(8), 8, 4 },
+ { pinmux(8), 8, 3 },
+ { pinmux(8), 8, 2 },
+ { pinmux(8), 8, 1 },
+ { pinmux(8), 8, 0 },
+ { pinmux(7), 8, 7 },
+ { pinmux(7), 8, 6 },
+ { pinmux(7), 8, 5 },
+ { pinmux(7), 8, 4 },
+ { pinmux(7), 8, 3 },
+ { pinmux(7), 8, 2 },
+ { pinmux(7), 8, 1 },
+ { pinmux(7), 8, 0 },
+ { pinmux(10), 8, 7 }, /* GP4[0] */
+ { pinmux(10), 8, 6 },
+ { pinmux(10), 8, 5 },
+ { pinmux(10), 8, 4 },
+ { pinmux(10), 8, 3 },
+ { pinmux(10), 8, 2 },
+ { pinmux(10), 8, 1 },
+ { pinmux(10), 8, 0 },
+ { pinmux(9), 8, 7 },
+ { pinmux(9), 8, 6 },
+ { pinmux(9), 8, 5 },
+ { pinmux(9), 8, 4 },
+ { pinmux(9), 8, 3 },
+ { pinmux(9), 8, 2 },
+ { pinmux(9), 8, 1 },
+ { pinmux(9), 8, 0 },
+ { pinmux(12), 8, 7 }, /* GP5[0] */
+ { pinmux(12), 8, 6 },
+ { pinmux(12), 8, 5 },
+ { pinmux(12), 8, 4 },
+ { pinmux(12), 8, 3 },
+ { pinmux(12), 8, 2 },
+ { pinmux(12), 8, 1 },
+ { pinmux(12), 8, 0 },
+ { pinmux(11), 8, 7 },
+ { pinmux(11), 8, 6 },
+ { pinmux(11), 8, 5 },
+ { pinmux(11), 8, 4 },
+ { pinmux(11), 8, 3 },
+ { pinmux(11), 8, 2 },
+ { pinmux(11), 8, 1 },
+ { pinmux(11), 8, 0 },
+ { pinmux(19), 8, 6 }, /* GP6[0] */
+ { pinmux(19), 8, 5 },
+ { pinmux(19), 8, 4 },
+ { pinmux(19), 8, 3 },
+ { pinmux(19), 8, 2 },
+ { pinmux(16), 8, 1 },
+ { pinmux(14), 8, 1 },
+ { pinmux(14), 8, 0 },
+ { pinmux(13), 8, 7 },
+ { pinmux(13), 8, 6 },
+ { pinmux(13), 8, 5 },
+ { pinmux(13), 8, 4 },
+ { pinmux(13), 8, 3 },
+ { pinmux(13), 8, 2 },
+ { pinmux(13), 8, 1 },
+ { pinmux(13), 8, 0 },
+ { pinmux(18), 8, 1 }, /* GP7[0] */
+ { pinmux(18), 8, 0 },
+ { pinmux(17), 8, 7 },
+ { pinmux(17), 8, 6 },
+ { pinmux(17), 8, 5 },
+ { pinmux(17), 8, 4 },
+ { pinmux(17), 8, 3 },
+ { pinmux(17), 8, 2 },
+ { pinmux(17), 8, 1 },
+ { pinmux(17), 8, 0 },
+ { pinmux(16), 8, 7 },
+ { pinmux(16), 8, 6 },
+ { pinmux(16), 8, 5 },
+ { pinmux(16), 8, 4 },
+ { pinmux(16), 8, 3 },
+ { pinmux(16), 8, 2 },
+ { pinmux(19), 8, 0 }, /* GP8[0] */
+ { pinmux(3), 4, 7 },
+ { pinmux(3), 4, 6 },
+ { pinmux(3), 4, 5 },
+ { pinmux(3), 4, 4 },
+ { pinmux(3), 4, 3 },
+ { pinmux(3), 4, 2 },
+ { pinmux(2), 4, 7 },
+ { pinmux(19), 8, 1 },
+ { pinmux(19), 8, 0 },
+ { pinmux(18), 8, 7 },
+ { pinmux(18), 8, 6 },
+ { pinmux(18), 8, 5 },
+ { pinmux(18), 8, 4 },
+ { pinmux(18), 8, 3 },
+ { pinmux(18), 8, 2 },
+};
+#endif /* CONFIG_SOC_DA8XX && !CONFIG_SOC_DA850 */
+#else /* !CONFIG_SOC_DA8XX */
+#define davinci_configure_pin_mux(a, b)
+#endif /* CONFIG_SOC_DA8XX */
+
+int gpio_request(unsigned int gpio, const char *label)
+{
+ if (gpio >= MAX_NUM_GPIOS)
+ return -1;
+
+ if (gpio_registry[gpio].is_registered)
+ return -1;
+
+ gpio_registry[gpio].is_registered = 1;
+ strncpy(gpio_registry[gpio].name, label, GPIO_NAME_SIZE);
+ gpio_registry[gpio].name[GPIO_NAME_SIZE - 1] = 0;
+
+ davinci_configure_pin_mux(&gpio_pinmux[gpio], 1);
+
+ return 0;
+}
+
+int gpio_free(unsigned int gpio)
+{
+ if (gpio >= MAX_NUM_GPIOS)
+ return -1;
+
+ if (!gpio_registry[gpio].is_registered)
+ return -1;
+
+ gpio_registry[gpio].is_registered = 0;
+ gpio_registry[gpio].name[0] = '\0';
+ /* Do not configure as input or change pin mux here */
+ return 0;
+}
+#endif
+
+static int _gpio_direction_input(struct davinci_gpio *bank, unsigned int gpio)
+{
+ setbits_le32(&bank->dir, 1U << GPIO_BIT(gpio));
+ return 0;
+}
+
+static int _gpio_get_value(struct davinci_gpio *bank, unsigned int gpio)
+{
+ unsigned int ip;
+ ip = in_le32(&bank->in_data) & (1U << GPIO_BIT(gpio));
+ return ip ? 1 : 0;
+}
+
+static int _gpio_set_value(struct davinci_gpio *bank, unsigned int gpio, int value)
+{
+ if (value)
+ bank->set_data = 1U << GPIO_BIT(gpio);
+ else
+ bank->clr_data = 1U << GPIO_BIT(gpio);
+
+ return 0;
+}
+
+static int _gpio_get_dir(struct davinci_gpio *bank, unsigned int gpio)
+{
+ return in_le32(&bank->dir) & (1U << GPIO_BIT(gpio));
+}
+
+static int _gpio_direction_output(struct davinci_gpio *bank, unsigned int gpio,
+ int value)
+{
+ clrbits_le32(&bank->dir, 1U << GPIO_BIT(gpio));
+ _gpio_set_value(bank, gpio, value);
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+
+void gpio_info(void)
+{
+ unsigned int gpio, dir, val;
+ struct davinci_gpio *bank;
+
+ for (gpio = 0; gpio < MAX_NUM_GPIOS; ++gpio) {
+ bank = GPIO_BANK(gpio);
+ dir = _gpio_get_dir(bank, gpio);
+ val = gpio_get_value(gpio);
+
+ printf("% 4d: %s: %d [%c] %s\n",
+ gpio, dir ? " in" : "out", val,
+ gpio_registry[gpio].is_registered ? 'x' : ' ',
+ gpio_registry[gpio].name);
+ }
+}
+
+int gpio_direction_input(unsigned int gpio)
+{
+ struct davinci_gpio *bank;
+
+ bank = GPIO_BANK(gpio);
+ return _gpio_direction_input(bank, gpio);
+}
+
+int gpio_direction_output(unsigned int gpio, int value)
+{
+ struct davinci_gpio *bank;
+
+ bank = GPIO_BANK(gpio);
+ return _gpio_direction_output(bank, gpio, value);
+}
+
+int gpio_get_value(unsigned int gpio)
+{
+ struct davinci_gpio *bank;
+
+ bank = GPIO_BANK(gpio);
+ return _gpio_get_value(bank, gpio);
+}
+
+int gpio_set_value(unsigned int gpio, int value)
+{
+ struct davinci_gpio *bank;
+
+ bank = GPIO_BANK(gpio);
+ return _gpio_set_value(bank, gpio, value);
+}
+
+#else /* DM_GPIO */
+
+static struct davinci_gpio *davinci_get_gpio_bank(struct udevice *dev, unsigned int offset)
+{
+ struct davinci_gpio_bank *bank = dev_get_priv(dev);
+ unsigned long addr;
+
+ /*
+ * The device tree is not broken into banks but the infrastructure is
+ * expecting it this way, so we'll first include the 0x10 offset, then
+ * calculate the bank manually based on the offset.
+ * Casting 'addr' as Unsigned long is needed to make the math work.
+ */
+ addr = ((unsigned long)(struct davinci_gpio *)bank->base) +
+ 0x10 + (0x28 * (offset >> 5));
+ return (struct davinci_gpio *)addr;
+}
+
+static int davinci_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct davinci_gpio *base = davinci_get_gpio_bank(dev, offset);
+
+ /*
+ * Fetch the address based on GPIO, but only pass the masked low 32-bits
+ */
+ _gpio_direction_input(base, (offset & 0x1f));
+ return 0;
+}
+
+static int davinci_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct davinci_gpio *base = davinci_get_gpio_bank(dev, offset);
+
+ _gpio_direction_output(base, (offset & 0x1f), value);
+ return 0;
+}
+
+static int davinci_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct davinci_gpio *base = davinci_get_gpio_bank(dev, offset);
+
+ return _gpio_get_value(base, (offset & 0x1f));
+}
+
+static int davinci_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct davinci_gpio *base = davinci_get_gpio_bank(dev, offset);
+
+ _gpio_set_value(base, (offset & 0x1f), value);
+
+ return 0;
+}
+
+static int davinci_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ unsigned int dir;
+ struct davinci_gpio *base = davinci_get_gpio_bank(dev, offset);
+
+ dir = _gpio_get_dir(base, offset);
+
+ if (dir)
+ return GPIOF_INPUT;
+
+ return GPIOF_OUTPUT;
+}
+
+static int davinci_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+
+ if (args->args[1] & GPIO_ACTIVE_LOW)
+ desc->flags = GPIOD_ACTIVE_LOW;
+ else
+ desc->flags = 0;
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_davinci_ops = {
+ .direction_input = davinci_gpio_direction_input,
+ .direction_output = davinci_gpio_direction_output,
+ .get_value = davinci_gpio_get_value,
+ .set_value = davinci_gpio_set_value,
+ .get_function = davinci_gpio_get_function,
+ .xlate = davinci_gpio_xlate,
+};
+
+static int davinci_gpio_probe(struct udevice *dev)
+{
+ struct davinci_gpio_bank *bank = dev_get_priv(dev);
+ struct davinci_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *fdt = gd->fdt_blob;
+ int node = dev_of_offset(dev);
+
+ uc_priv->bank_name = plat->port_name;
+ uc_priv->gpio_count = fdtdec_get_int(fdt, node, "ti,ngpio", -1);
+ bank->base = (struct davinci_gpio *)plat->base;
+ return 0;
+}
+
+static const struct udevice_id davinci_gpio_ids[] = {
+ { .compatible = "ti,dm6441-gpio" },
+ { .compatible = "ti,k2g-gpio" },
+ { .compatible = "ti,keystone-gpio" },
+ { }
+};
+
+static int davinci_gpio_of_to_plat(struct udevice *dev)
+{
+ struct davinci_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+ char name[18], *str;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->base = addr;
+
+ sprintf(name, "gpio@%4x_", (unsigned int)plat->base);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ plat->port_name = str;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(ti_dm6441_gpio) = {
+ .name = "ti_dm6441_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_davinci_ops,
+ .of_to_plat = of_match_ptr(davinci_gpio_of_to_plat),
+ .of_match = davinci_gpio_ids,
+ .bind = dm_scan_fdt_dev,
+ .plat_auto = sizeof(struct davinci_gpio_plat),
+ .probe = davinci_gpio_probe,
+ .priv_auto = sizeof(struct davinci_gpio_bank),
+};
+
+#endif
diff --git a/drivers/gpio/da8xx_gpio.h b/drivers/gpio/da8xx_gpio.h
new file mode 100644
index 00000000000..ca59d6a90b3
--- /dev/null
+++ b/drivers/gpio/da8xx_gpio.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _GPIO_DA8XX_DEFS_H_
+#define _GPIO_DA8XX_DEFS_H_
+
+struct davinci_gpio {
+ unsigned int dir;
+ unsigned int out_data;
+ unsigned int set_data;
+ unsigned int clr_data;
+ unsigned int in_data;
+ unsigned int set_rising;
+ unsigned int clr_rising;
+ unsigned int set_falling;
+ unsigned int clr_falling;
+ unsigned int intstat;
+};
+
+struct davinci_gpio_bank {
+ int num_gpio;
+ unsigned int irq_num;
+ unsigned int irq_mask;
+ unsigned long *in_use;
+ struct davinci_gpio *base;
+};
+
+#define GPIO_NAME_SIZE 20
+#define MAX_NUM_GPIOS 144
+#define GPIO_BIT(gp) ((gp) & 0x1F)
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+
+/* Information about a GPIO bank */
+struct davinci_gpio_plat {
+ int bank_index;
+ ulong base; /* address of registers in physical memory */
+ const char *port_name;
+};
+#endif
+
+#endif
diff --git a/drivers/gpio/dwapb_gpio.c b/drivers/gpio/dwapb_gpio.c
new file mode 100644
index 00000000000..7a6eae9ba18
--- /dev/null
+++ b/drivers/gpio/dwapb_gpio.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Marek Vasut <marex@denx.de>
+ *
+ * DesignWare APB GPIO driver
+ */
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/read.h>
+#include <errno.h>
+#include <reset.h>
+
+#define GPIO_SWPORT_DR(p) (0x00 + (p) * 0xc)
+#define GPIO_SWPORT_DDR(p) (0x04 + (p) * 0xc)
+#define GPIO_INTEN 0x30
+#define GPIO_INTMASK 0x34
+#define GPIO_INTTYPE_LEVEL 0x38
+#define GPIO_INT_POLARITY 0x3c
+#define GPIO_INTSTATUS 0x40
+#define GPIO_PORTA_DEBOUNCE 0x48
+#define GPIO_PORTA_EOI 0x4c
+#define GPIO_EXT_PORT(p) (0x50 + (p) * 4)
+
+struct gpio_dwapb_priv {
+ struct reset_ctl_bulk resets;
+};
+
+struct gpio_dwapb_plat {
+ const char *name;
+ int bank;
+ int pins;
+ void __iomem *base;
+};
+
+static int dwapb_gpio_direction_input(struct udevice *dev, unsigned pin)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+
+ clrbits_le32(plat->base + GPIO_SWPORT_DDR(plat->bank), 1 << pin);
+ return 0;
+}
+
+static int dwapb_gpio_direction_output(struct udevice *dev, unsigned pin,
+ int val)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+
+ setbits_le32(plat->base + GPIO_SWPORT_DDR(plat->bank), 1 << pin);
+
+ if (val)
+ setbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
+ else
+ clrbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
+
+ return 0;
+}
+
+static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+
+ if (val)
+ setbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
+ else
+ clrbits_le32(plat->base + GPIO_SWPORT_DR(plat->bank), 1 << pin);
+
+ return 0;
+}
+
+static int dwapb_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+ u32 gpio;
+
+ gpio = readl(plat->base + GPIO_SWPORT_DDR(plat->bank));
+
+ if (gpio & BIT(offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int dwapb_gpio_get_value(struct udevice *dev, unsigned pin)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+ u32 value;
+
+ if (dwapb_gpio_get_function(dev, pin) == GPIOF_OUTPUT)
+ value = readl(plat->base + GPIO_SWPORT_DR(plat->bank));
+ else
+ value = readl(plat->base + GPIO_EXT_PORT(plat->bank));
+ return !!(value & BIT(pin));
+}
+
+static const struct dm_gpio_ops gpio_dwapb_ops = {
+ .direction_input = dwapb_gpio_direction_input,
+ .direction_output = dwapb_gpio_direction_output,
+ .get_value = dwapb_gpio_get_value,
+ .set_value = dwapb_gpio_set_value,
+ .get_function = dwapb_gpio_get_function,
+};
+
+static int gpio_dwapb_reset(struct udevice *dev)
+{
+ int ret;
+ struct gpio_dwapb_priv *priv = dev_get_priv(dev);
+
+ ret = reset_get_bulk(dev, &priv->resets);
+ if (ret) {
+ /* Return 0 if error due to !CONFIG_DM_RESET and reset
+ * DT property is not present.
+ */
+ if (ret == -ENOENT || ret == -ENOTSUPP)
+ return 0;
+
+ dev_warn(dev, "Can't get reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_deassert_bulk(&priv->resets);
+ if (ret) {
+ reset_release_bulk(&priv->resets);
+ dev_err(dev, "Failed to reset: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gpio_dwapb_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+
+ if (!plat) {
+ /* Reset on parent device only */
+ return gpio_dwapb_reset(dev);
+ }
+
+ priv->gpio_count = plat->pins;
+ priv->bank_name = plat->name;
+
+ return 0;
+}
+
+static int gpio_dwapb_bind(struct udevice *dev)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+ struct udevice *subdev;
+ fdt_addr_t base;
+ int ret, bank = 0;
+ ofnode node;
+
+ /* If this is a child device, there is nothing to do here */
+ if (plat)
+ return 0;
+
+ base = dev_read_addr(dev);
+ if (base == FDT_ADDR_T_NONE) {
+ debug("Can't get the GPIO register base address\n");
+ return -ENXIO;
+ }
+
+ for (node = dev_read_first_subnode(dev); ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ if (!ofnode_read_bool(node, "gpio-controller"))
+ continue;
+
+ plat = devm_kcalloc(dev, 1, sizeof(*plat), GFP_KERNEL);
+ if (!plat)
+ return -ENOMEM;
+
+ plat->base = (void *)base;
+ plat->bank = bank;
+ plat->pins = ofnode_read_u32_default(node, "snps,nr-gpios", 0);
+
+ if (ofnode_read_string_index(node, "bank-name", 0,
+ &plat->name)) {
+ /*
+ * Fall back to node name. This means accessing pins
+ * via bank name won't work.
+ */
+ char name[32];
+
+ snprintf(name, sizeof(name), "%s_",
+ ofnode_get_name(node));
+ plat->name = strdup(name);
+ if (!plat->name) {
+ kfree(plat);
+ return -ENOMEM;
+ }
+ }
+
+ ret = device_bind(dev, dev->driver, plat->name, plat, node,
+ &subdev);
+ if (ret)
+ return ret;
+
+ bank++;
+ }
+
+ return 0;
+}
+
+static int gpio_dwapb_remove(struct udevice *dev)
+{
+ struct gpio_dwapb_plat *plat = dev_get_plat(dev);
+ struct gpio_dwapb_priv *priv = dev_get_priv(dev);
+
+ if (!plat && priv)
+ return reset_release_bulk(&priv->resets);
+
+ return 0;
+}
+
+static const struct udevice_id gpio_dwapb_ids[] = {
+ { .compatible = "snps,dw-apb-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_dwapb) = {
+ .name = "gpio-dwapb",
+ .id = UCLASS_GPIO,
+ .of_match = gpio_dwapb_ids,
+ .ops = &gpio_dwapb_ops,
+ .bind = gpio_dwapb_bind,
+ .probe = gpio_dwapb_probe,
+ .remove = gpio_dwapb_remove,
+ .priv_auto = sizeof(struct gpio_dwapb_priv),
+};
diff --git a/drivers/gpio/ftgpio010.c b/drivers/gpio/ftgpio010.c
new file mode 100644
index 00000000000..6c091d4fd87
--- /dev/null
+++ b/drivers/gpio/ftgpio010.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Faraday Technology's FTGPIO010 controller.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+struct ftgpio010_regs {
+ u32 out;
+ u32 in;
+ u32 direction; // 1 - output
+ u32 reserved;
+ u32 set;
+ u32 clear;
+};
+
+struct ftgpio010_plat {
+ struct ftgpio010_regs __iomem *regs;
+};
+
+static int ftgpio010_direction_input(struct udevice *dev, unsigned int pin)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+ struct ftgpio010_regs *const regs = plat->regs;
+
+ clrbits_le32(&regs->direction, 1 << pin);
+ return 0;
+}
+
+static int ftgpio010_direction_output(struct udevice *dev, unsigned int pin,
+ int val)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+ struct ftgpio010_regs *const regs = plat->regs;
+
+ /* change the data first, then the direction. to avoid glitch */
+ out_le32(val ? &regs->set : &regs->clear, 1 << pin);
+ setbits_le32(&regs->direction, 1 << pin);
+
+ return 0;
+}
+
+static int ftgpio010_get_value(struct udevice *dev, unsigned int pin)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+ struct ftgpio010_regs *const regs = plat->regs;
+
+ return in_le32(&regs->in) >> pin & 1;
+}
+
+static int ftgpio010_set_value(struct udevice *dev, unsigned int pin, int val)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+ struct ftgpio010_regs *const regs = plat->regs;
+
+ out_le32(val ? &regs->set : &regs->clear, 1 << pin);
+ return 0;
+}
+
+static int ftgpio010_get_function(struct udevice *dev, unsigned int pin)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+ struct ftgpio010_regs *const regs = plat->regs;
+
+ if (in_le32(&regs->direction) >> pin & 1)
+ return GPIOF_OUTPUT;
+ return GPIOF_INPUT;
+}
+
+static int ftgpio010_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = ofnode_read_u32_default(dev_ofnode(dev),
+ "nr-gpios", 32);
+ return 0;
+}
+
+static int ftgpio010_of_to_plat(struct udevice *dev)
+{
+ struct ftgpio010_plat *plat = dev_get_plat(dev);
+
+ plat->regs = dev_read_addr_ptr(dev);
+ return 0;
+}
+
+static const struct dm_gpio_ops ftgpio010_ops = {
+ .direction_input = ftgpio010_direction_input,
+ .direction_output = ftgpio010_direction_output,
+ .get_value = ftgpio010_get_value,
+ .set_value = ftgpio010_set_value,
+ .get_function = ftgpio010_get_function,
+};
+
+static const struct udevice_id ftgpio010_ids[] = {
+ { .compatible = "faraday,ftgpio010" },
+ { }
+};
+
+U_BOOT_DRIVER(ftgpio010) = {
+ .name = "ftgpio010",
+ .id = UCLASS_GPIO,
+ .of_match = ftgpio010_ids,
+ .ops = &ftgpio010_ops,
+ .of_to_plat = ftgpio010_of_to_plat,
+ .plat_auto = sizeof(struct ftgpio010_plat),
+ .probe = ftgpio010_probe,
+};
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
new file mode 100644
index 00000000000..1c3d18796b3
--- /dev/null
+++ b/drivers/gpio/gpio-aspeed.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2015 IBM Corp.
+ * Joel Stanley <joel@jms.id.au>
+ * Ryan Chen <ryan_chen@aspeedtech.com>
+ *
+ * Implementation extracted from the Linux kernel and adapted for u-boot.
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+#include <config.h>
+#include <clk.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <linux/bug.h>
+#include <linux/sizes.h>
+
+struct aspeed_gpio_priv {
+ void *regs;
+};
+
+struct aspeed_gpio_bank {
+ u16 val_regs; /* +0: Rd: read input value, Wr: set write latch
+ * +4: Rd/Wr: Direction (0=in, 1=out)
+ */
+ u16 rdata_reg; /* Rd: read write latch, Wr: <none> */
+ u16 irq_regs;
+ u16 debounce_regs;
+ u16 tolerance_regs;
+ u16 cmdsrc_regs;
+ const char names[4][3];
+};
+
+static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
+ {
+ .val_regs = 0x0000,
+ .rdata_reg = 0x00c0,
+ .irq_regs = 0x0008,
+ .debounce_regs = 0x0040,
+ .tolerance_regs = 0x001c,
+ .cmdsrc_regs = 0x0060,
+ .names = { "A", "B", "C", "D" },
+ },
+ {
+ .val_regs = 0x0020,
+ .rdata_reg = 0x00c4,
+ .irq_regs = 0x0028,
+ .debounce_regs = 0x0048,
+ .tolerance_regs = 0x003c,
+ .cmdsrc_regs = 0x0068,
+ .names = { "E", "F", "G", "H" },
+ },
+ {
+ .val_regs = 0x0070,
+ .rdata_reg = 0x00c8,
+ .irq_regs = 0x0098,
+ .debounce_regs = 0x00b0,
+ .tolerance_regs = 0x00ac,
+ .cmdsrc_regs = 0x0090,
+ .names = { "I", "J", "K", "L" },
+ },
+ {
+ .val_regs = 0x0078,
+ .rdata_reg = 0x00cc,
+ .irq_regs = 0x00e8,
+ .debounce_regs = 0x0100,
+ .tolerance_regs = 0x00fc,
+ .cmdsrc_regs = 0x00e0,
+ .names = { "M", "N", "O", "P" },
+ },
+ {
+ .val_regs = 0x0080,
+ .rdata_reg = 0x00d0,
+ .irq_regs = 0x0118,
+ .debounce_regs = 0x0130,
+ .tolerance_regs = 0x012c,
+ .cmdsrc_regs = 0x0110,
+ .names = { "Q", "R", "S", "T" },
+ },
+ {
+ .val_regs = 0x0088,
+ .rdata_reg = 0x00d4,
+ .irq_regs = 0x0148,
+ .debounce_regs = 0x0160,
+ .tolerance_regs = 0x015c,
+ .cmdsrc_regs = 0x0140,
+ .names = { "U", "V", "W", "X" },
+ },
+ {
+ .val_regs = 0x01E0,
+ .rdata_reg = 0x00d8,
+ .irq_regs = 0x0178,
+ .debounce_regs = 0x0190,
+ .tolerance_regs = 0x018c,
+ .cmdsrc_regs = 0x0170,
+ .names = { "Y", "Z", "AA", "AB" },
+ },
+ {
+ .val_regs = 0x01e8,
+ .rdata_reg = 0x00dc,
+ .irq_regs = 0x01a8,
+ .debounce_regs = 0x01c0,
+ .tolerance_regs = 0x01bc,
+ .cmdsrc_regs = 0x01a0,
+ .names = { "AC", "", "", "" },
+ },
+};
+
+enum aspeed_gpio_reg {
+ reg_val,
+ reg_rdata,
+ reg_dir,
+ reg_irq_enable,
+ reg_irq_type0,
+ reg_irq_type1,
+ reg_irq_type2,
+ reg_irq_status,
+ reg_debounce_sel1,
+ reg_debounce_sel2,
+ reg_tolerance,
+ reg_cmdsrc0,
+ reg_cmdsrc1,
+};
+
+#define GPIO_VAL_VALUE 0x00
+#define GPIO_VAL_DIR 0x04
+
+#define GPIO_IRQ_ENABLE 0x00
+#define GPIO_IRQ_TYPE0 0x04
+#define GPIO_IRQ_TYPE1 0x08
+#define GPIO_IRQ_TYPE2 0x0c
+#define GPIO_IRQ_STATUS 0x10
+
+#define GPIO_DEBOUNCE_SEL1 0x00
+#define GPIO_DEBOUNCE_SEL2 0x04
+
+#define GPIO_CMDSRC_0 0x00
+#define GPIO_CMDSRC_1 0x04
+#define GPIO_CMDSRC_ARM 0
+#define GPIO_CMDSRC_LPC 1
+#define GPIO_CMDSRC_COLDFIRE 2
+#define GPIO_CMDSRC_RESERVED 3
+
+/* This will be resolved at compile time */
+static inline void __iomem *bank_reg(struct aspeed_gpio_priv *gpio,
+ const struct aspeed_gpio_bank *bank,
+ const enum aspeed_gpio_reg reg)
+{
+ switch (reg) {
+ case reg_val:
+ return gpio->regs + bank->val_regs + GPIO_VAL_VALUE;
+ case reg_rdata:
+ return gpio->regs + bank->rdata_reg;
+ case reg_dir:
+ return gpio->regs + bank->val_regs + GPIO_VAL_DIR;
+ case reg_irq_enable:
+ return gpio->regs + bank->irq_regs + GPIO_IRQ_ENABLE;
+ case reg_irq_type0:
+ return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE0;
+ case reg_irq_type1:
+ return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE1;
+ case reg_irq_type2:
+ return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE2;
+ case reg_irq_status:
+ return gpio->regs + bank->irq_regs + GPIO_IRQ_STATUS;
+ case reg_debounce_sel1:
+ return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL1;
+ case reg_debounce_sel2:
+ return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
+ case reg_tolerance:
+ return gpio->regs + bank->tolerance_regs;
+ case reg_cmdsrc0:
+ return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_0;
+ case reg_cmdsrc1:
+ return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_1;
+ }
+ BUG();
+}
+
+#define GPIO_BANK(x) ((x) >> 5)
+#define GPIO_OFFSET(x) ((x) & 0x1f)
+#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
+
+static const struct aspeed_gpio_bank *to_bank(unsigned int offset)
+{
+ unsigned int bank = GPIO_BANK(offset);
+
+ WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks));
+ return &aspeed_gpio_banks[bank];
+}
+
+static int
+aspeed_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ u32 dir = readl(bank_reg(priv, bank, reg_dir));
+
+ dir &= ~GPIO_BIT(offset);
+ writel(dir, bank_reg(priv, bank, reg_dir));
+
+ return 0;
+}
+
+static int aspeed_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ u32 dir = readl(bank_reg(priv, bank, reg_dir));
+ u32 output = readl(bank_reg(priv, bank, reg_rdata));
+
+ dir |= GPIO_BIT(offset);
+ writel(dir, bank_reg(priv, bank, reg_dir));
+
+ if (value)
+ output |= GPIO_BIT(offset);
+ else
+ output &= ~GPIO_BIT(offset);
+
+ writel(output, bank_reg(priv, bank, reg_val));
+
+ return 0;
+}
+
+static int aspeed_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+ return !!(readl(bank_reg(priv, bank, reg_val)) & GPIO_BIT(offset));
+}
+
+static int
+aspeed_gpio_set_value(struct udevice *dev, unsigned int offset, int value)
+{
+ struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+ u32 data = readl(bank_reg(priv, bank, reg_rdata));
+
+ if (value)
+ data |= GPIO_BIT(offset);
+ else
+ data &= ~GPIO_BIT(offset);
+
+ writel(data, bank_reg(priv, bank, reg_val));
+
+ return 0;
+}
+
+static int aspeed_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+ const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+ if (readl(bank_reg(priv, bank, reg_dir)) & GPIO_BIT(offset))
+ return GPIOF_OUTPUT;
+
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops aspeed_gpio_ops = {
+ .direction_input = aspeed_gpio_direction_input,
+ .direction_output = aspeed_gpio_direction_output,
+ .get_value = aspeed_gpio_get_value,
+ .set_value = aspeed_gpio_set_value,
+ .get_function = aspeed_gpio_get_function,
+};
+
+static int aspeed_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct aspeed_gpio_priv *priv = dev_get_priv(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);
+
+ return 0;
+}
+
+static const struct udevice_id aspeed_gpio_ids[] = {
+ { .compatible = "aspeed,ast2400-gpio", },
+ { .compatible = "aspeed,ast2500-gpio", },
+ { .compatible = "aspeed,ast2600-gpio", },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_aspeed) = {
+ .name = "gpio-aspeed",
+ .id = UCLASS_GPIO,
+ .of_match = aspeed_gpio_ids,
+ .ops = &aspeed_gpio_ops,
+ .probe = aspeed_gpio_probe,
+ .priv_auto = sizeof(struct aspeed_gpio_priv),
+};
diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c
new file mode 100644
index 00000000000..ca7aa14eeb2
--- /dev/null
+++ b/drivers/gpio/gpio-fxl6408.c
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Toradex
+ * Copyright (C) 2016 Broadcom
+ */
+
+/**
+ * DOC: FXL6408 I2C to GPIO expander.
+ *
+ * This chip has 8 GPIO lines out of it, and is controlled by an I2C
+ * bus (a pair of lines), providing 4x expansion of GPIO lines. It
+ * also provides an interrupt line out for notifying of state changes.
+ *
+ * Any preconfigured state will be left in place until the GPIO lines
+ * get activated. At power on, everything is treated as an input,
+ * default input is HIGH and pulled-up, all interrupts are masked.
+ *
+ * Documentation can be found at:
+ * ------------------------------
+ *
+ * https://www.fairchildsemi.com/datasheets/FX/FXL6408.pdf
+ *
+ * This driver bases on:
+ * ---------------------
+ *
+ * - the original driver by Eric Anholt <eric@anholt.net>:
+ * https://patchwork.kernel.org/patch/9148419/
+ * - the Toradex version by Max Krummenacher <max.krummenacher@toradex.com>:
+ * http://git.toradex.com/cgit/linux-toradex.git/tree/drivers/gpio/gpio-fxl6408.c?h=toradex_5.4-2.3.x-imx
+ * - the U-Boot PCA953x driver by Peng Fan <van.freenix@gmail.com>:
+ * drivers/gpio/pca953x_gpio.c
+ *
+ * TODO:
+ * - Add interrupts support
+ * - Replace deprecated callbacks direction_input/output() with set_flags()
+ */
+
+#include <asm-generic/gpio.h>
+#include <asm/global_data.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <i2c.h>
+#include <linux/bitops.h>
+#include <log.h>
+
+#define REG_DEVID_CTRL 0x1
+# define SW_RST BIT(0)
+# define RST_INT BIT(1)
+/** 0b101 is the Manufacturer's ID assigned to Fairchild by Nokia */
+# define MF_ID_FAIRCHILD 5
+
+/** Bits set here indicate that the GPIO is an output */
+#define REG_IO_DIR 0x3
+
+/**
+ * REG_OUT_STATE - a high-output state register address
+ *
+ * Bits set here, when the corresponding bit of REG_IO_DIR is set,
+ * drive the output high instead of low.
+ */
+#define REG_OUT_STATE 0x5
+
+/** Bits here make the output High-Z, instead of the OUTPUT value */
+#define REG_OUT_HIGH_Z 0x7
+
+/**
+ * REG_IN_DEFAULT_STATE - an interrupt state register address
+ *
+ * Bits here define the expected input state of the GPIO.
+ * INTERRUPT_STATUS bits will be set when the INPUT transitions away
+ * from this value.
+ */
+#define REG_IN_DEFAULT_STATE 0x9
+
+/**
+ * REG_PULL_ENABLE - a pull-up/down enable state register address
+ *
+ * Bits here enable either pull up or pull down according to
+ * REG_PULL_MODE.
+ */
+#define REG_PULL_ENABLE 0xb
+
+/**
+ * REG_PULL_MODE - a pull-up/pull-down mode state register address
+ *
+ * Bits set here selects a pull-up/pull-down state of pin, which
+ * is configured as Input and the corresponding REG_PULL_ENABLE bit is
+ * set.
+ */
+#define REG_PULL_MODE 0xd
+
+/** Returns the current status (1 = HIGH) of the input pins */
+#define REG_IN_STATUS 0xf
+
+/** Mask of pins which can generate interrupts */
+#define REG_INT_MASK 0x11
+
+/** Mask of pins which have generated an interrupt. Cleared on read */
+#define REG_INT_STATUS 0x13
+
+/* Manufacturer's ID getting from Device ID & Ctrl register */
+enum {
+ MF_ID_MASK = GENMASK(7, 5),
+ MF_ID_SHIFT = 5,
+};
+
+/* Firmware revision getting from Device ID & Ctrl register */
+enum {
+ FW_REV_MASK = GENMASK(4, 2),
+ FW_REV_SHIFT = 2,
+};
+
+enum io_direction {
+ DIR_IN = 0,
+ DIR_OUT = 1,
+};
+
+/**
+ * struct fxl6408_info - Data for fxl6408
+ *
+ * @dev: udevice structure for the device
+ * @addr: i2c slave address
+ * @device_id: hold the value of device id register
+ * @reg_io_dir: hold the value of direction register
+ * @reg_output: hold the value of output register
+ */
+struct fxl6408_info {
+ struct udevice *dev;
+ int addr;
+ u8 device_id;
+ u8 reg_io_dir;
+ u8 reg_output;
+};
+
+static inline int fxl6408_write(struct udevice *dev, int reg, u8 val)
+{
+ return dm_i2c_write(dev, reg, &val, 1);
+}
+
+static int fxl6408_read(struct udevice *dev, int reg)
+{
+ int ret;
+ u8 tmp;
+
+ ret = dm_i2c_read(dev, reg, &tmp, 1);
+ if (!ret)
+ ret = tmp;
+
+ return ret;
+}
+
+/**
+ * fxl6408_is_output() - check whether the gpio configures as either
+ * output or input.
+ *
+ * @dev: an instance of a driver
+ * @offset: a gpio offset
+ *
+ * Return: false - input, true - output.
+ */
+static bool fxl6408_is_output(struct udevice *dev, int offset)
+{
+ struct fxl6408_info *info = dev_get_plat(dev);
+
+ return info->reg_io_dir & BIT(offset);
+}
+
+static int fxl6408_get_value(struct udevice *dev, uint offset)
+{
+ int ret, reg = fxl6408_is_output(dev, offset) ? REG_OUT_STATE : REG_IN_STATUS;
+
+ ret = fxl6408_read(dev, reg);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & BIT(offset));
+}
+
+static int fxl6408_set_value(struct udevice *dev, uint offset, int value)
+{
+ struct fxl6408_info *info = dev_get_plat(dev);
+ u8 val;
+ int ret;
+
+ if (value)
+ val = info->reg_output | BIT(offset);
+ else
+ val = info->reg_output & ~BIT(offset);
+
+ ret = fxl6408_write(dev, REG_OUT_STATE, val);
+ if (ret < 0)
+ return ret;
+
+ info->reg_output = val;
+
+ return 0;
+}
+
+static int fxl6408_set_direction(struct udevice *dev, uint offset,
+ enum io_direction dir)
+{
+ struct fxl6408_info *info = dev_get_plat(dev);
+ u8 val;
+ int ret;
+
+ if (dir == DIR_IN)
+ val = info->reg_io_dir & ~BIT(offset);
+ else
+ val = info->reg_io_dir | BIT(offset);
+
+ ret = fxl6408_write(dev, REG_IO_DIR, val);
+ if (ret < 0)
+ return ret;
+
+ info->reg_io_dir = val;
+
+ return 0;
+}
+
+static int fxl6408_direction_input(struct udevice *dev, uint offset)
+{
+ return fxl6408_set_direction(dev, offset, DIR_IN);
+}
+
+static int fxl6408_direction_output(struct udevice *dev, uint offset, int value)
+{
+ int ret;
+
+ /* Configure output value */
+ ret = fxl6408_set_value(dev, offset, value);
+ if (ret < 0)
+ return ret;
+
+ /* Configure direction as output */
+ fxl6408_set_direction(dev, offset, DIR_OUT);
+
+ return 0;
+}
+
+static int fxl6408_get_function(struct udevice *dev, uint offset)
+{
+ if (fxl6408_is_output(dev, offset))
+ return GPIOF_OUTPUT;
+
+ return GPIOF_INPUT;
+}
+
+static int fxl6408_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops fxl6408_ops = {
+ .direction_input = fxl6408_direction_input,
+ .direction_output = fxl6408_direction_output,
+ .get_value = fxl6408_get_value,
+ .set_value = fxl6408_set_value,
+ .get_function = fxl6408_get_function,
+ .xlate = fxl6408_xlate,
+};
+
+static int fxl6408_probe(struct udevice *dev)
+{
+ struct fxl6408_info *info = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char bank_name[32], *tmp_str;
+ int addr, ret, size;
+ u32 val32;
+
+ addr = dev_read_addr(dev);
+ if (addr == 0)
+ return -EINVAL;
+
+ info->addr = addr;
+
+ /*
+ * Check the device ID register to see if it's responding.
+ * This also clears RST_INT as a side effect, so we won't get
+ * the "we've been power cycled" interrupt once interrupts
+ * being enabled.
+ */
+ ret = fxl6408_read(dev, REG_DEVID_CTRL);
+ if (ret < 0) {
+ dev_err(dev, "FXL6408 probe returned %d\n", ret);
+ return ret;
+ }
+
+ if ((ret & MF_ID_MASK) >> MF_ID_SHIFT != MF_ID_FAIRCHILD) {
+ dev_err(dev, "FXL6408 probe: wrong Manufacturer's ID: 0x%02x\n", ret);
+ return -ENXIO;
+ }
+ info->device_id = ret;
+
+ /*
+ * Disable High-Z of outputs, so that the OUTPUT updates
+ * actually take effect.
+ */
+ ret = fxl6408_write(dev, REG_OUT_HIGH_Z, (u8)0);
+ if (ret < 0) {
+ dev_err(dev, "Error writing High-Z register\n");
+ return ret;
+ }
+
+ /*
+ * If configured, set initial output state and direction,
+ * otherwise read them from the chip.
+ */
+ if (dev_read_u32(dev, "initial_io_dir", &val32)) {
+ ret = fxl6408_read(dev, REG_IO_DIR);
+ if (ret < 0) {
+ dev_err(dev, "Error reading direction register\n");
+ return ret;
+ }
+ info->reg_io_dir = ret;
+ } else {
+ info->reg_io_dir = val32 & 0xFF;
+ ret = fxl6408_write(dev, REG_IO_DIR, info->reg_io_dir);
+ if (ret < 0) {
+ dev_err(dev, "Error setting direction register\n");
+ return ret;
+ }
+ }
+
+ if (dev_read_u32(dev, "initial_output", &val32)) {
+ ret = fxl6408_read(dev, REG_OUT_STATE);
+ if (ret < 0) {
+ dev_err(dev, "Error reading output register\n");
+ return ret;
+ }
+ info->reg_output = ret;
+ } else {
+ info->reg_output = val32 & 0xFF;
+ ret = fxl6408_write(dev, REG_OUT_STATE, info->reg_output);
+ if (ret < 0) {
+ dev_err(dev, "Error setting output register\n");
+ return ret;
+ }
+ }
+
+ tmp_str = (char *)dev_read_prop(dev, "bank-name", &size);
+ if (tmp_str) {
+ snprintf(bank_name, sizeof(bank_name), "%s@%x_", tmp_str,
+ info->addr);
+ } else {
+ snprintf(bank_name, sizeof(bank_name), "gpio@%x_", info->addr);
+ }
+
+ tmp_str = strdup(bank_name);
+ if (!tmp_str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = tmp_str;
+ uc_priv->gpio_count = dev_get_driver_data(dev);
+ uc_priv->gpio_base = -1;
+
+ dev_dbg(dev, "%s (FW rev. %d) is ready\n", bank_name,
+ (info->device_id & FW_REV_MASK) >> FW_REV_SHIFT);
+
+ return 0;
+}
+
+static const struct udevice_id fxl6408_ids[] = {
+ { .compatible = "fcs,fxl6408", .data = 8 },
+ { }
+};
+
+U_BOOT_DRIVER(fxl6408_gpio) = {
+ .name = "fxl6408_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &fxl6408_ops,
+ .probe = fxl6408_probe,
+ .of_match = fxl6408_ids,
+ .plat_auto = sizeof(struct fxl6408_info),
+};
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
new file mode 100644
index 00000000000..70778501232
--- /dev/null
+++ b/drivers/gpio/gpio-rcar.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include "../pinctrl/renesas/sh_pfc.h"
+
+#define GPIO_IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
+#define GPIO_INOUTSEL 0x04 /* General Input/Output Switching Register */
+#define GPIO_OUTDT 0x08 /* General Output Register */
+#define GPIO_INDT 0x0c /* General Input Register */
+#define GPIO_INTDT 0x10 /* Interrupt Display Register */
+#define GPIO_INTCLR 0x14 /* Interrupt Clear Register */
+#define GPIO_INTMSK 0x18 /* Interrupt Mask Register */
+#define GPIO_MSKCLR 0x1c /* Interrupt Mask Clear Register */
+#define GPIO_POSNEG 0x20 /* Positive/Negative Logic Select Register */
+#define GPIO_EDGLEVEL 0x24 /* Edge/level Select Register */
+#define GPIO_FILONOFF 0x28 /* Chattering Prevention On/Off Register */
+#define GPIO_BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
+#define GPIO_INEN 0x50 /* General Input Enable Register */
+
+#define RCAR_MAX_GPIO_PER_BANK 32
+
+#define RCAR_GPIO_HAS_INEN BIT(0)
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rcar_gpio_priv {
+ void __iomem *regs;
+ u32 quirks;
+ int pfc_offset;
+};
+
+static int rcar_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct rcar_gpio_priv *priv = dev_get_priv(dev);
+ const u32 bit = BIT(offset);
+
+ /*
+ * Testing on r8a7790 shows that INDT does not show correct pin state
+ * when configured as output, so use OUTDT in case of output pins.
+ */
+ if (readl(priv->regs + GPIO_INOUTSEL) & bit)
+ return !!(readl(priv->regs + GPIO_OUTDT) & bit);
+ else
+ return !!(readl(priv->regs + GPIO_INDT) & bit);
+}
+
+static int rcar_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct rcar_gpio_priv *priv = dev_get_priv(dev);
+
+ if (value)
+ setbits_le32(priv->regs + GPIO_OUTDT, BIT(offset));
+ else
+ clrbits_le32(priv->regs + GPIO_OUTDT, BIT(offset));
+
+ return 0;
+}
+
+static void rcar_gpio_set_direction(struct udevice *dev, unsigned offset,
+ bool output)
+{
+ struct rcar_gpio_priv *priv = dev_get_priv(dev);
+ void __iomem *regs = priv->regs;
+
+ /*
+ * follow steps in the GPIO documentation for
+ * "Setting General Output Mode" and
+ * "Setting General Input Mode"
+ */
+
+ /* Configure postive logic in POSNEG */
+ clrbits_le32(regs + GPIO_POSNEG, BIT(offset));
+
+ /* Select "Input Enable/Disable" in INEN */
+ if (priv->quirks & RCAR_GPIO_HAS_INEN) {
+ if (output)
+ clrbits_le32(regs + GPIO_INEN, BIT(offset));
+ else
+ setbits_le32(regs + GPIO_INEN, BIT(offset));
+ }
+
+ /* Select "General Input/Output Mode" in IOINTSEL */
+ clrbits_le32(regs + GPIO_IOINTSEL, BIT(offset));
+
+ /* Select Input Mode or Output Mode in INOUTSEL */
+ if (output)
+ setbits_le32(regs + GPIO_INOUTSEL, BIT(offset));
+ else
+ clrbits_le32(regs + GPIO_INOUTSEL, BIT(offset));
+}
+
+static int rcar_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ rcar_gpio_set_direction(dev, offset, false);
+
+ return 0;
+}
+
+static int rcar_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ /* write GPIO value to output before selecting output mode of pin */
+ rcar_gpio_set_value(dev, offset, value);
+ rcar_gpio_set_direction(dev, offset, true);
+
+ return 0;
+}
+
+static int rcar_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct rcar_gpio_priv *priv = dev_get_priv(dev);
+
+ if (readl(priv->regs + GPIO_INOUTSEL) & BIT(offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops rcar_gpio_ops = {
+ .request = pinctrl_gpio_request,
+ .rfree = pinctrl_gpio_free,
+ .direction_input = rcar_gpio_direction_input,
+ .direction_output = rcar_gpio_direction_output,
+ .get_value = rcar_gpio_get_value,
+ .set_value = rcar_gpio_set_value,
+ .get_function = rcar_gpio_get_function,
+};
+
+static int rcar_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct rcar_gpio_priv *priv = dev_get_priv(dev);
+ struct fdtdec_phandle_args args;
+ struct clk clk;
+ int node = dev_of_offset(dev);
+ int ret;
+
+ priv->regs = dev_read_addr_ptr(dev);
+ priv->quirks = dev_get_driver_data(dev);
+ uc_priv->bank_name = dev->name;
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
+ NULL, 3, 0, &args);
+ priv->pfc_offset = ret == 0 ? args.args[1] : -1;
+ uc_priv->gpio_count = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get GPIO bank clock\n");
+ return ret;
+ }
+
+ ret = clk_enable(&clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable GPIO bank clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id rcar_gpio_ids[] = {
+ { .compatible = "renesas,gpio-r8a7795" },
+ { .compatible = "renesas,gpio-r8a7796" },
+ { .compatible = "renesas,gpio-r8a77965" },
+ { .compatible = "renesas,gpio-r8a77970" },
+ { .compatible = "renesas,gpio-r8a77990" },
+ { .compatible = "renesas,gpio-r8a77995" },
+ { .compatible = "renesas,gpio-r8a779a0", .data = RCAR_GPIO_HAS_INEN },
+ { .compatible = "renesas,rcar-gen2-gpio" },
+ { .compatible = "renesas,rcar-gen3-gpio" },
+ { .compatible = "renesas,rcar-gen4-gpio", .data = RCAR_GPIO_HAS_INEN },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(rcar_gpio) = {
+ .name = "rcar-gpio",
+ .id = UCLASS_GPIO,
+ .of_match = rcar_gpio_ids,
+ .ops = &rcar_gpio_ops,
+ .priv_auto = sizeof(struct rcar_gpio_priv),
+ .probe = rcar_gpio_probe,
+};
diff --git a/drivers/gpio/gpio-rza1.c b/drivers/gpio/gpio-rza1.c
new file mode 100644
index 00000000000..f14be871e8d
--- /dev/null
+++ b/drivers/gpio/gpio-rza1.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Marek Vasut <marek.vasut@gmail.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#define P(bank) (0x0000 + (bank) * 4)
+#define PSR(bank) (0x0100 + (bank) * 4)
+#define PPR(bank) (0x0200 + (bank) * 4)
+#define PM(bank) (0x0300 + (bank) * 4)
+#define PMC(bank) (0x0400 + (bank) * 4)
+#define PFC(bank) (0x0500 + (bank) * 4)
+#define PFCE(bank) (0x0600 + (bank) * 4)
+#define PNOT(bank) (0x0700 + (bank) * 4)
+#define PMSR(bank) (0x0800 + (bank) * 4)
+#define PMCSR(bank) (0x0900 + (bank) * 4)
+#define PFCAE(bank) (0x0A00 + (bank) * 4)
+#define PIBC(bank) (0x4000 + (bank) * 4)
+#define PBDC(bank) (0x4100 + (bank) * 4)
+#define PIPC(bank) (0x4200 + (bank) * 4)
+
+#define RZA1_MAX_GPIO_PER_BANK 16
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct r7s72100_gpio_priv {
+ void __iomem *regs;
+ int bank;
+};
+
+static int r7s72100_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct r7s72100_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(readw(priv->regs + PPR(priv->bank)) & BIT(offset));
+}
+
+static int r7s72100_gpio_set_value(struct udevice *dev, unsigned line,
+ int value)
+{
+ struct r7s72100_gpio_priv *priv = dev_get_priv(dev);
+
+ writel(BIT(line + 16) | (value ? BIT(line) : 0),
+ priv->regs + PSR(priv->bank));
+
+ return 0;
+}
+
+static void r7s72100_gpio_set_direction(struct udevice *dev, unsigned line,
+ bool output)
+{
+ struct r7s72100_gpio_priv *priv = dev_get_priv(dev);
+
+ writel(BIT(line + 16), priv->regs + PMCSR(priv->bank));
+ writel(BIT(line + 16) | (output ? 0 : BIT(line)),
+ priv->regs + PMSR(priv->bank));
+
+ clrsetbits_le16(priv->regs + PIBC(priv->bank), BIT(line),
+ output ? 0 : BIT(line));
+}
+
+static int r7s72100_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ r7s72100_gpio_set_direction(dev, offset, false);
+ return 0;
+}
+
+static int r7s72100_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ /* write GPIO value to output before selecting output mode of pin */
+ r7s72100_gpio_set_value(dev, offset, value);
+ r7s72100_gpio_set_direction(dev, offset, true);
+
+ return 0;
+}
+
+static int r7s72100_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct r7s72100_gpio_priv *priv = dev_get_priv(dev);
+
+ if (readw(priv->regs + PM(priv->bank)) & BIT(offset))
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops r7s72100_gpio_ops = {
+ .direction_input = r7s72100_gpio_direction_input,
+ .direction_output = r7s72100_gpio_direction_output,
+ .get_value = r7s72100_gpio_get_value,
+ .set_value = r7s72100_gpio_set_value,
+ .get_function = r7s72100_gpio_get_function,
+};
+
+static int r7s72100_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct r7s72100_gpio_priv *priv = dev_get_priv(dev);
+ struct fdtdec_phandle_args args;
+ int node = dev_of_offset(dev);
+ int ret;
+
+ fdt_addr_t addr_base;
+
+ uc_priv->bank_name = dev->name;
+ dev = dev_get_parent(dev);
+ addr_base = dev_read_addr(dev);
+ if (addr_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = (void __iomem *)addr_base;
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
+ NULL, 3, 0, &args);
+ priv->bank = ret == 0 ? (args.args[1] / RZA1_MAX_GPIO_PER_BANK) : -1;
+ uc_priv->gpio_count = ret == 0 ? args.args[2] : RZA1_MAX_GPIO_PER_BANK;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(r7s72100_gpio) = {
+ .name = "r7s72100-gpio",
+ .id = UCLASS_GPIO,
+ .ops = &r7s72100_gpio_ops,
+ .priv_auto = sizeof(struct r7s72100_gpio_priv),
+ .probe = r7s72100_gpio_probe,
+};
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
new file mode 100644
index 00000000000..4234cd912c9
--- /dev/null
+++ b/drivers/gpio/gpio-uclass.c
@@ -0,0 +1,1558 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ */
+
+#define LOG_CATEGORY UCLASS_GPIO
+
+#include <common.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <log.h>
+#include <dm/devres.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <acpi/acpi_device.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/bug.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define GPIO_ALLOC_BITS 32
+
+/**
+ * gpio_desc_init() - Initialize the GPIO descriptor
+ *
+ * @desc: GPIO descriptor to initialize
+ * @dev: GPIO device
+ * @offset: Offset of device GPIO
+ */
+static void gpio_desc_init(struct gpio_desc *desc,
+ struct udevice *dev,
+ uint offset)
+{
+ desc->dev = dev;
+ desc->offset = offset;
+ desc->flags = 0;
+}
+
+/**
+ * gpio_to_device() - Convert global GPIO number to device, number
+ *
+ * Convert the GPIO number to an entry in the list of GPIOs
+ * or GPIO blocks registered with the GPIO controller. Returns
+ * entry on success, NULL on error.
+ *
+ * @gpio: The numeric representation of the GPIO
+ * @desc: Returns description (desc->flags will always be 0)
+ * Return: 0 if found, -ENOENT if not found
+ */
+static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc)
+{
+ struct gpio_dev_priv *uc_priv;
+ struct udevice *dev;
+
+ for (uclass_first_device(UCLASS_GPIO, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ uc_priv = dev_get_uclass_priv(dev);
+ if (gpio >= uc_priv->gpio_base &&
+ gpio < uc_priv->gpio_base + uc_priv->gpio_count) {
+ gpio_desc_init(desc, dev, gpio - uc_priv->gpio_base);
+ return 0;
+ }
+ }
+
+ /* No such GPIO */
+ return -ENOENT;
+}
+
+/**
+ * gpio_is_claimed() - Test whether GPIO is claimed by consumer
+ *
+ * Test whether GPIO is claimed by consumer already.
+ *
+ * @uc_priv: gpio_dev_priv pointer.
+ * @offset: gpio offset within the device
+ * @return: true if claimed, false if not claimed
+ */
+static bool gpio_is_claimed(struct gpio_dev_priv *uc_priv, unsigned int offset)
+{
+ return !!(uc_priv->claimed[offset / GPIO_ALLOC_BITS] & BIT(offset % GPIO_ALLOC_BITS));
+}
+
+/**
+ * gpio_set_claim() - Set GPIO claimed by consumer
+ *
+ * Set a bit which indicate the GPIO is claimed by consumer
+ *
+ * @uc_priv: gpio_dev_priv pointer.
+ * @offset: gpio offset within the device
+ */
+static void gpio_set_claim(struct gpio_dev_priv *uc_priv, unsigned int offset)
+{
+ uc_priv->claimed[offset / GPIO_ALLOC_BITS] |= BIT(offset % GPIO_ALLOC_BITS);
+}
+
+/**
+ * gpio_clear_claim() - Clear GPIO claimed by consumer
+ *
+ * Clear a bit which indicate the GPIO is claimed by consumer
+ *
+ * @uc_priv: gpio_dev_priv pointer.
+ * @offset: gpio offset within the device
+ */
+static void gpio_clear_claim(struct gpio_dev_priv *uc_priv, unsigned int offset)
+{
+ uc_priv->claimed[offset / GPIO_ALLOC_BITS] &= ~BIT(offset % GPIO_ALLOC_BITS);
+}
+
+#if CONFIG_IS_ENABLED(DM_GPIO_LOOKUP_LABEL)
+/**
+ * dm_gpio_lookup_label() - look for name in gpio device
+ *
+ * search in uc_priv, if there is a gpio with labelname same
+ * as name.
+ *
+ * @name: name which is searched
+ * @uc_priv: gpio_dev_priv pointer.
+ * @offset: gpio offset within the device
+ * @return: 0 if found, -ENOENT if not.
+ */
+static int dm_gpio_lookup_label(const char *name,
+ struct gpio_dev_priv *uc_priv, ulong *offset)
+{
+ int i;
+
+ *offset = -1;
+ for (i = 0; i < uc_priv->gpio_count; i++) {
+ if (!gpio_is_claimed(uc_priv, i))
+ continue;
+ if (!strcmp(name, uc_priv->name[i])) {
+ *offset = i;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+#else
+static int
+dm_gpio_lookup_label(const char *name, struct gpio_dev_priv *uc_priv,
+ ulong *offset)
+{
+ return -ENOENT;
+}
+#endif
+
+int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc)
+{
+ struct gpio_dev_priv *uc_priv = NULL;
+ struct udevice *dev;
+ ulong offset;
+ int numeric;
+
+ numeric = isdigit(*name) ? dectoul(name, NULL) : -1;
+ for (uclass_first_device(UCLASS_GPIO, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ int len;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ if (numeric != -1) {
+ offset = numeric - uc_priv->gpio_base;
+ /* Allow GPIOs to be numbered from 0 */
+ if (offset < uc_priv->gpio_count)
+ break;
+ }
+
+ len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;
+
+ if (!strncasecmp(name, uc_priv->bank_name, len)) {
+ if (!strict_strtoul(name + len, 10, &offset))
+ if (offset < uc_priv->gpio_count)
+ break;
+ }
+
+ /*
+ * if we did not found a gpio through its bank
+ * name, we search for a valid gpio label.
+ */
+ if (!dm_gpio_lookup_label(name, uc_priv, &offset))
+ break;
+ }
+
+ if (!dev)
+ return -EINVAL;
+
+ gpio_desc_init(desc, dev, offset);
+
+ return 0;
+}
+
+int gpio_lookup_name(const char *name, struct udevice **devp,
+ unsigned int *offsetp, unsigned int *gpiop)
+{
+ struct gpio_desc desc;
+ int ret;
+
+ if (devp)
+ *devp = NULL;
+ ret = dm_gpio_lookup_name(name, &desc);
+ if (ret)
+ return ret;
+
+ if (devp)
+ *devp = desc.dev;
+ if (offsetp)
+ *offsetp = desc.offset;
+ if (gpiop) {
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc.dev);
+
+ *gpiop = uc_priv->gpio_base + desc.offset;
+ }
+
+ return 0;
+}
+
+unsigned long gpio_flags_xlate(uint32_t arg)
+{
+ unsigned long flags = 0;
+
+ if (arg & GPIO_ACTIVE_LOW)
+ flags |= GPIOD_ACTIVE_LOW;
+
+ /*
+ * need to test 2 bits for gpio output binding:
+ * OPEN_DRAIN (0x6) = SINGLE_ENDED (0x2) | LINE_OPEN_DRAIN (0x4)
+ * OPEN_SOURCE (0x2) = SINGLE_ENDED (0x2) | LINE_OPEN_SOURCE (0x0)
+ */
+ if (arg & GPIO_SINGLE_ENDED) {
+ if (arg & GPIO_LINE_OPEN_DRAIN)
+ flags |= GPIOD_OPEN_DRAIN;
+ else
+ flags |= GPIOD_OPEN_SOURCE;
+ }
+
+ if (arg & GPIO_PULL_UP)
+ flags |= GPIOD_PULL_UP;
+
+ if (arg & GPIO_PULL_DOWN)
+ flags |= GPIOD_PULL_DOWN;
+
+ return flags;
+}
+
+int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (args->args_count < 1)
+ return -EINVAL;
+
+ desc->offset = args->args[0];
+ if (desc->offset >= uc_priv->gpio_count)
+ return -EINVAL;
+
+ if (args->args_count < 2)
+ return 0;
+
+ desc->flags = gpio_flags_xlate(args->args[1]);
+
+ return 0;
+}
+
+static int gpio_find_and_xlate(struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+
+ if (ops->xlate)
+ return ops->xlate(desc->dev, desc, args);
+ else
+ return gpio_xlate_offs_flags(desc->dev, desc, args);
+}
+
+#if CONFIG_IS_ENABLED(GPIO_HOG)
+
+struct gpio_hog_priv {
+ struct gpio_desc gpiod;
+};
+
+struct gpio_hog_data {
+ int gpiod_flags;
+ int value;
+ u32 val[2];
+};
+
+static int gpio_hog_of_to_plat(struct udevice *dev)
+{
+ struct gpio_hog_data *plat = dev_get_plat(dev);
+ const char *nodename;
+ int ret;
+
+ plat->value = 0;
+ if (dev_read_bool(dev, "input")) {
+ plat->gpiod_flags = GPIOD_IS_IN;
+ } else if (dev_read_bool(dev, "output-high")) {
+ plat->value = 1;
+ plat->gpiod_flags = GPIOD_IS_OUT;
+ } else if (dev_read_bool(dev, "output-low")) {
+ plat->gpiod_flags = GPIOD_IS_OUT;
+ } else {
+ printf("%s: missing gpio-hog state.\n", __func__);
+ return -EINVAL;
+ }
+ ret = dev_read_u32_array(dev, "gpios", plat->val, 2);
+ if (ret) {
+ printf("%s: wrong gpios property, 2 values needed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ nodename = dev_read_string(dev, "line-name");
+ if (nodename)
+ device_set_name(dev, nodename);
+
+ return 0;
+}
+
+static int gpio_hog_probe(struct udevice *dev)
+{
+ struct gpio_hog_data *plat = dev_get_plat(dev);
+ struct gpio_hog_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = gpio_dev_request_index(dev->parent, dev->name, "gpio-hog",
+ plat->val[0], plat->gpiod_flags,
+ plat->val[1], &priv->gpiod);
+ if (ret < 0) {
+ debug("%s: node %s could not get gpio.\n", __func__,
+ dev->name);
+ return ret;
+ }
+
+ if (plat->gpiod_flags == GPIOD_IS_OUT) {
+ ret = dm_gpio_set_value(&priv->gpiod, plat->value);
+ if (ret < 0) {
+ debug("%s: node %s could not set gpio.\n", __func__,
+ dev->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int gpio_hog_lookup_name(const char *name, struct gpio_desc **desc)
+{
+ struct udevice *dev;
+
+ *desc = NULL;
+ if (!uclass_get_device_by_name(UCLASS_NOP, name, &dev)) {
+ struct gpio_hog_priv *priv = dev_get_priv(dev);
+
+ *desc = &priv->gpiod;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+U_BOOT_DRIVER(gpio_hog) = {
+ .name = "gpio_hog",
+ .id = UCLASS_NOP,
+ .of_to_plat = gpio_hog_of_to_plat,
+ .probe = gpio_hog_probe,
+ .priv_auto = sizeof(struct gpio_hog_priv),
+ .plat_auto = sizeof(struct gpio_hog_data),
+};
+#else
+int gpio_hog_lookup_name(const char *name, struct gpio_desc **desc)
+{
+ return 0;
+}
+#endif
+
+int dm_gpio_request(struct gpio_desc *desc, const char *label)
+{
+ const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+ struct udevice *dev = desc->dev;
+ struct gpio_dev_priv *uc_priv;
+ char *str;
+ int ret;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ if (gpio_is_claimed(uc_priv, desc->offset))
+ return -EBUSY;
+ str = strdup(label);
+ if (!str)
+ return -ENOMEM;
+ if (ops->request) {
+ ret = ops->request(dev, desc->offset, label);
+ if (ret) {
+ free(str);
+ return ret;
+ }
+ }
+
+ gpio_set_claim(uc_priv, desc->offset);
+ uc_priv->name[desc->offset] = str;
+
+ return 0;
+}
+
+static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
+{
+#if !defined(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
+ va_list args;
+ char buf[40];
+
+ va_start(args, fmt);
+ vscnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ return dm_gpio_request(desc, buf);
+#else
+ return dm_gpio_request(desc, fmt);
+#endif
+}
+
+/**
+ * gpio_request() - [COMPAT] Request GPIO
+ * gpio: GPIO number
+ * label: Name for the requested GPIO
+ *
+ * The label is copied and allocated so the caller does not need to keep
+ * the pointer around.
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+ struct gpio_desc desc;
+ int ret;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+
+ return dm_gpio_request(&desc, label);
+}
+
+/**
+ * gpio_requestf() - [COMPAT] Request GPIO
+ * @gpio: GPIO number
+ * @fmt: Format string for the requested GPIO
+ * @...: Arguments for the printf() format string
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_requestf(unsigned gpio, const char *fmt, ...)
+{
+#if !defined(CONFIG_SPL_BUILD) || !CONFIG_IS_ENABLED(USE_TINY_PRINTF)
+ va_list args;
+ char buf[40];
+
+ va_start(args, fmt);
+ vscnprintf(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ return gpio_request(gpio, buf);
+#else
+ return gpio_request(gpio, fmt);
+#endif
+}
+
+int _dm_gpio_free(struct udevice *dev, uint offset)
+{
+ const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+ struct gpio_dev_priv *uc_priv;
+ int ret;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ if (!gpio_is_claimed(uc_priv, offset))
+ return -ENXIO;
+ if (ops->rfree) {
+ ret = ops->rfree(dev, offset);
+ if (ret)
+ return ret;
+ }
+
+ gpio_clear_claim(uc_priv, offset);
+ free(uc_priv->name[offset]);
+ uc_priv->name[offset] = NULL;
+
+ return 0;
+}
+
+/**
+ * gpio_free() - [COMPAT] Relinquish GPIO
+ * gpio: GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_free(unsigned gpio)
+{
+ struct gpio_desc desc;
+ int ret;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+
+ return _dm_gpio_free(desc.dev, desc.offset);
+}
+
+static int check_reserved(const struct gpio_desc *desc, const char *func)
+{
+ struct gpio_dev_priv *uc_priv;
+
+ if (!dm_gpio_is_valid(desc))
+ return -ENOENT;
+
+ uc_priv = dev_get_uclass_priv(desc->dev);
+ if (!gpio_is_claimed(uc_priv, desc->offset)) {
+ printf("%s: %s: error: gpio %s%d not reserved\n",
+ desc->dev->name, func,
+ uc_priv->bank_name ? uc_priv->bank_name : "",
+ desc->offset);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * gpio_direction_input() - [COMPAT] Set GPIO direction to input
+ * gpio: GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_input(unsigned gpio)
+{
+ struct gpio_desc desc;
+ int ret;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+
+ return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN);
+}
+
+/**
+ * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value
+ * gpio: GPIO number
+ * value: Logical value to be set on the GPIO pin
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_output(unsigned gpio, int value)
+{
+ struct gpio_desc desc;
+ ulong flags;
+ int ret;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+
+ flags = GPIOD_IS_OUT;
+ if (value)
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ return dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, flags);
+}
+
+static int _gpio_get_value(const struct gpio_desc *desc)
+{
+ const struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
+ int value;
+
+ value = ops->get_value(desc->dev, desc->offset);
+
+ return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
+}
+
+int dm_gpio_get_value(const struct gpio_desc *desc)
+{
+ int ret;
+
+ ret = check_reserved(desc, "get_value");
+ if (ret)
+ return ret;
+
+ return _gpio_get_value(desc);
+}
+
+int dm_gpio_set_value(const struct gpio_desc *desc, int value)
+{
+ const struct dm_gpio_ops *ops;
+ int ret;
+
+ ret = check_reserved(desc, "set_value");
+ if (ret)
+ return ret;
+
+ if (desc->flags & GPIOD_ACTIVE_LOW)
+ value = !value;
+
+ /* GPIOD_ are directly managed by driver in set_flags */
+ ops = gpio_get_ops(desc->dev);
+ if (ops->set_flags) {
+ ulong flags = desc->flags;
+
+ if (value)
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ else
+ flags &= ~GPIOD_IS_OUT_ACTIVE;
+ return ops->set_flags(desc->dev, desc->offset, flags);
+ }
+
+ /*
+ * Emulate open drain by not actively driving the line high or
+ * Emulate open source by not actively driving the line low
+ */
+ if ((desc->flags & GPIOD_OPEN_DRAIN && value) ||
+ (desc->flags & GPIOD_OPEN_SOURCE && !value))
+ return ops->direction_input(desc->dev, desc->offset);
+ else if (desc->flags & GPIOD_OPEN_DRAIN ||
+ desc->flags & GPIOD_OPEN_SOURCE)
+ return ops->direction_output(desc->dev, desc->offset, value);
+
+ ret = ops->set_value(desc->dev, desc->offset, value);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* check dir flags invalid configuration */
+static int check_dir_flags(ulong flags)
+{
+ if ((flags & GPIOD_IS_OUT) && (flags & GPIOD_IS_IN)) {
+ log_debug("%s: flags 0x%lx has GPIOD_IS_OUT and GPIOD_IS_IN\n",
+ __func__, flags);
+ return -EINVAL;
+ }
+
+ if ((flags & GPIOD_PULL_UP) && (flags & GPIOD_PULL_DOWN)) {
+ log_debug("%s: flags 0x%lx has GPIOD_PULL_UP and GPIOD_PULL_DOWN\n",
+ __func__, flags);
+ return -EINVAL;
+ }
+
+ if ((flags & GPIOD_OPEN_DRAIN) && (flags & GPIOD_OPEN_SOURCE)) {
+ log_debug("%s: flags 0x%lx has GPIOD_OPEN_DRAIN and GPIOD_OPEN_SOURCE\n",
+ __func__, flags);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * _dm_gpio_set_flags() - Send flags to the driver
+ *
+ * This uses the best available method to send the given flags to the driver.
+ * Note that if flags & GPIOD_ACTIVE_LOW, the driver sees the opposite value
+ * of GPIOD_IS_OUT_ACTIVE.
+ *
+ * @desc: GPIO description
+ * @flags: flags value to set
+ * Return: 0 if OK, -ve on error
+ */
+static int _dm_gpio_set_flags(struct gpio_desc *desc, ulong flags)
+{
+ struct udevice *dev = desc->dev;
+ const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret = 0;
+
+ ret = check_dir_flags(flags);
+ if (ret) {
+ dev_dbg(dev,
+ "%s error: set_dir_flags for gpio %s%d has invalid dir flags 0x%lx\n",
+ desc->dev->name,
+ uc_priv->bank_name ? uc_priv->bank_name : "",
+ desc->offset, flags);
+
+ return ret;
+ }
+
+ /* If active low, invert the output state */
+ if ((flags & (GPIOD_IS_OUT | GPIOD_ACTIVE_LOW)) ==
+ (GPIOD_IS_OUT | GPIOD_ACTIVE_LOW))
+ flags ^= GPIOD_IS_OUT_ACTIVE;
+
+ /* GPIOD_ are directly managed by driver in set_flags */
+ if (ops->set_flags) {
+ ret = ops->set_flags(dev, desc->offset, flags);
+ } else {
+ if (flags & GPIOD_IS_OUT) {
+ bool value = flags & GPIOD_IS_OUT_ACTIVE;
+
+ ret = ops->direction_output(dev, desc->offset, value);
+ } else if (flags & GPIOD_IS_IN) {
+ ret = ops->direction_input(dev, desc->offset);
+ }
+ }
+
+ return ret;
+}
+
+int dm_gpio_clrset_flags(struct gpio_desc *desc, ulong clr, ulong set)
+{
+ ulong flags;
+ int ret;
+
+ ret = check_reserved(desc, "set_dir_flags");
+ if (ret)
+ return ret;
+
+ flags = (desc->flags & ~clr) | set;
+
+ ret = _dm_gpio_set_flags(desc, flags);
+ if (ret)
+ return ret;
+
+ /* save the flags also in descriptor */
+ desc->flags = flags;
+
+ return 0;
+}
+
+int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+{
+ /* combine the requested flags (for IN/OUT) and the descriptor flags */
+ return dm_gpio_clrset_flags(desc, GPIOD_MASK_DIR, flags);
+}
+
+int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr,
+ ulong set)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ ret = dm_gpio_clrset_flags(&desc[i], clr, set);
+ if (ret)
+ return log_ret(ret);
+ }
+
+ return 0;
+}
+
+int dm_gpio_get_flags(struct gpio_desc *desc, ulong *flagsp)
+{
+ struct udevice *dev = desc->dev;
+ int ret, value;
+ const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+ ulong flags;
+
+ ret = check_reserved(desc, "get_flags");
+ if (ret)
+ return ret;
+
+ /* GPIOD_ are directly provided by driver except GPIOD_ACTIVE_LOW */
+ if (ops->get_flags) {
+ ret = ops->get_flags(dev, desc->offset, &flags);
+ if (ret)
+ return ret;
+
+ /* GPIOD_ACTIVE_LOW is saved in desc->flags */
+ value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
+ if (desc->flags & GPIOD_ACTIVE_LOW)
+ value = !value;
+ flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
+ flags |= (desc->flags & GPIOD_ACTIVE_LOW);
+ if (value)
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ } else {
+ flags = desc->flags;
+ /* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
+ flags &= ~GPIOD_IS_OUT_ACTIVE;
+ if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
+ flags |= GPIOD_IS_OUT_ACTIVE;
+ }
+ *flagsp = flags;
+
+ return 0;
+}
+
+/**
+ * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value
+ * gpio: GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns the value of the GPIO pin, or negative value
+ * on error.
+ */
+int gpio_get_value(unsigned gpio)
+{
+ int ret;
+
+ struct gpio_desc desc;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+ return dm_gpio_get_value(&desc);
+}
+
+/**
+ * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin
+ * gpio: GPIO number
+ * value: Logical value to be set on the GPIO pin.
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_set_value(unsigned gpio, int value)
+{
+ struct gpio_desc desc;
+ int ret;
+
+ ret = gpio_to_device(gpio, &desc);
+ if (ret)
+ return ret;
+ return dm_gpio_set_value(&desc, value);
+}
+
+const char *gpio_get_bank_info(struct udevice *dev, int *bit_count)
+{
+ struct gpio_dev_priv *priv;
+
+ /* Must be called on an active device */
+ priv = dev_get_uclass_priv(dev);
+ assert(priv);
+
+ *bit_count = priv->gpio_count;
+ return priv->bank_name;
+}
+
+static const char * const gpio_function[GPIOF_COUNT] = {
+ "input",
+ "output",
+ "unused",
+ "unknown",
+ "func",
+};
+
+static int get_function(struct udevice *dev, int offset, bool skip_unused,
+ const char **namep)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+
+ BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
+ if (!device_active(dev))
+ return -ENODEV;
+ if (offset < 0 || offset >= uc_priv->gpio_count)
+ return -EINVAL;
+ if (namep)
+ *namep = uc_priv->name[offset];
+ if (skip_unused && !gpio_is_claimed(uc_priv, offset))
+ return GPIOF_UNUSED;
+ if (ops->get_function) {
+ int ret;
+
+ ret = ops->get_function(dev, offset);
+ if (ret < 0)
+ return ret;
+ if (ret >= ARRAY_SIZE(gpio_function))
+ return -ENODATA;
+ return ret;
+ }
+
+ return GPIOF_UNKNOWN;
+}
+
+int gpio_get_function(struct udevice *dev, int offset, const char **namep)
+{
+ return get_function(dev, offset, true, namep);
+}
+
+int gpio_get_raw_function(struct udevice *dev, int offset, const char **namep)
+{
+ return get_function(dev, offset, false, namep);
+}
+
+int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
+{
+ const struct dm_gpio_ops *ops = gpio_get_ops(dev);
+ struct gpio_dev_priv *priv;
+ char *str = buf;
+ const char *label;
+ int func;
+ int ret;
+ int len;
+ bool used;
+
+ BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
+
+ *buf = 0;
+ priv = dev_get_uclass_priv(dev);
+ ret = gpio_get_raw_function(dev, offset, &label);
+ if (ret < 0)
+ return ret;
+ func = ret;
+ len = snprintf(str, buffsize, "%s%d: %s",
+ priv->bank_name ? priv->bank_name : "",
+ offset, gpio_function[func]);
+
+ switch (func) {
+ case GPIOF_FUNC:
+ snprintf(str + len, buffsize - len, " %s", label ? label : "");
+ break;
+ case GPIOF_INPUT:
+ case GPIOF_OUTPUT:
+ case GPIOF_UNUSED:
+ ret = ops->get_value(dev, offset);
+ if (ret < 0)
+ return ret;
+ used = gpio_get_function(dev, offset, &label) != GPIOF_UNUSED;
+ snprintf(str + len, buffsize - len, ": %d [%c]%s%s",
+ ret,
+ used ? 'x' : ' ',
+ label ? " " : "",
+ label ? label : "");
+ break;
+ }
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
+{
+ const struct dm_gpio_ops *ops;
+
+ memset(gpio, '\0', sizeof(*gpio));
+ if (!dm_gpio_is_valid(desc)) {
+ /* Indicate that the GPIO is not valid */
+ gpio->pin_count = 0;
+ gpio->pins[0] = 0;
+ return -EINVAL;
+ }
+
+ ops = gpio_get_ops(desc->dev);
+ if (!ops->get_acpi)
+ return -ENOSYS;
+
+ return ops->get_acpi(desc, gpio);
+}
+#endif
+
+int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
+{
+ int i, ret;
+ int gpio;
+
+ for (i = 0; i < 32; i++) {
+ gpio = gpio_num_array[i];
+ if (gpio == -1)
+ break;
+ ret = gpio_requestf(gpio, fmt, i);
+ if (ret)
+ goto err;
+ ret = gpio_direction_input(gpio);
+ if (ret) {
+ gpio_free(gpio);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ for (i--; i >= 0; i--)
+ gpio_free(gpio_num_array[i]);
+
+ return ret;
+}
+
+/*
+ * get a number comprised of multiple GPIO values. gpio_num_array points to
+ * the array of gpio pin numbers to scan, terminated by -1.
+ */
+int gpio_get_values_as_int(const int *gpio_list)
+{
+ int gpio;
+ unsigned bitmask = 1;
+ unsigned vector = 0;
+ int ret;
+
+ while (bitmask &&
+ ((gpio = *gpio_list++) != -1)) {
+ ret = gpio_get_value(gpio);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ vector |= bitmask;
+ bitmask <<= 1;
+ }
+
+ return vector;
+}
+
+int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count)
+{
+ unsigned bitmask = 1;
+ unsigned vector = 0;
+ int ret, i;
+
+ for (i = 0; i < count; i++) {
+ ret = dm_gpio_get_value(&desc_list[i]);
+ if (ret < 0)
+ return ret;
+ else if (ret)
+ vector |= bitmask;
+ bitmask <<= 1;
+ }
+
+ return vector;
+}
+
+int dm_gpio_get_values_as_int_base3(struct gpio_desc *desc_list,
+ int count)
+{
+ static const char tristate[] = "01z";
+ enum {
+ PULLUP,
+ PULLDOWN,
+
+ NUM_OPTIONS,
+ };
+ int vals[NUM_OPTIONS];
+ uint mask;
+ uint vector = 0;
+ int ret, i;
+
+ /*
+ * Limit to 19 digits which should be plenty. This avoids overflow of a
+ * 32-bit int
+ */
+ assert(count < 20);
+
+ for (i = 0; i < NUM_OPTIONS; i++) {
+ uint flags = GPIOD_IS_IN;
+
+ flags |= (i == PULLDOWN) ? GPIOD_PULL_DOWN : GPIOD_PULL_UP;
+ ret = dm_gpios_clrset_flags(desc_list, count, GPIOD_MASK_PULL,
+ flags);
+ if (ret)
+ return log_msg_ret("pu", ret);
+
+ /* Give the lines time to settle */
+ udelay(10);
+
+ ret = dm_gpio_get_values_as_int(desc_list, count);
+ if (ret < 0)
+ return log_msg_ret("get1", ret);
+ vals[i] = ret;
+ }
+
+ log_debug("values: %x %x, count = %d\n", vals[0], vals[1], count);
+ for (i = count - 1, mask = 1 << i; i >= 0; i--, mask >>= 1) {
+ uint pd = vals[PULLDOWN] & mask ? 1 : 0;
+ uint pu = vals[PULLUP] & mask ? 1 : 0;
+ uint digit;
+
+ /*
+ * Get value with internal pulldown active. If this is 1 then
+ * there is a stronger external pullup, which we call 1. If not
+ * then call it 0.
+ *
+ * If the values differ then the pin is floating so we call
+ * this a 2.
+ */
+ if (pu == pd)
+ digit = pd;
+ else
+ digit = 2;
+ log_debug("%c ", tristate[digit]);
+ vector = 3 * vector + digit;
+ }
+ log_debug("vector=%d\n", vector);
+
+ return vector;
+}
+
+/**
+ * gpio_request_tail: common work for requesting a gpio.
+ *
+ * ret: return value from previous work in function which calls
+ * this function.
+ * This seems bogus (why calling this function instead not
+ * calling it and end caller function instead?).
+ * Because on error in caller function we want to set some
+ * default values in gpio desc and have a common error
+ * debug message, which provides this function.
+ * nodename: Name of node for which gpio gets requested
+ * used for gpio label name.
+ * args: pointer to output arguments structure
+ * list_name: Name of GPIO list
+ * used for gpio label name.
+ * index: gpio index in gpio list
+ * used for gpio label name.
+ * desc: pointer to gpio descriptor, filled from this
+ * function.
+ * flags: gpio flags to use.
+ * add_index: should index added to gpio label name
+ * gpio_dev: pointer to gpio device from which the gpio
+ * will be requested. If NULL try to get the
+ * gpio device with uclass_get_device_by_ofnode()
+ *
+ * return: In error case this function sets default values in
+ * gpio descriptor, also emmits a debug message.
+ * On success it returns 0 else the error code from
+ * function calls, or the error code passed through
+ * ret to this function.
+ *
+ */
+static int gpio_request_tail(int ret, const char *nodename,
+ struct ofnode_phandle_args *args,
+ const char *list_name, int index,
+ struct gpio_desc *desc, int flags,
+ bool add_index, struct udevice *gpio_dev)
+{
+ gpio_desc_init(desc, gpio_dev, 0);
+ if (ret)
+ goto err;
+
+ if (!desc->dev) {
+ ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
+ &desc->dev);
+ if (ret) {
+#if CONFIG_IS_ENABLED(MAX77663_GPIO) || CONFIG_IS_ENABLED(PALMAS_GPIO)
+ struct udevice *pmic;
+ ret = uclass_get_device_by_ofnode(UCLASS_PMIC, args->node,
+ &pmic);
+ if (ret) {
+ log_debug("%s: PMIC device get failed, err %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ device_foreach_child(desc->dev, pmic) {
+ if (device_get_uclass_id(desc->dev) == UCLASS_GPIO)
+ break;
+ }
+
+ /* if loop exits without GPIO device return error */
+ if (device_get_uclass_id(desc->dev) != UCLASS_GPIO)
+ goto err;
+#else
+ debug("%s: uclass_get_device_by_ofnode failed\n",
+ __func__);
+ goto err;
+#endif
+ }
+ }
+ ret = gpio_find_and_xlate(desc, args);
+ if (ret) {
+ debug("%s: gpio_find_and_xlate failed\n", __func__);
+ goto err;
+ }
+ ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
+ nodename, list_name, index);
+ if (ret) {
+ debug("%s: dm_gpio_requestf failed\n", __func__);
+ goto err;
+ }
+
+ /* Keep any direction flags provided by the devicetree */
+ ret = dm_gpio_set_dir_flags(desc,
+ flags | (desc->flags & GPIOD_MASK_DIR));
+ if (ret) {
+ debug("%s: dm_gpio_set_dir failed\n", __func__);
+ goto err;
+ }
+
+ return 0;
+err:
+ debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n",
+ __func__, nodename, list_name, index, ret);
+ return ret;
+}
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static int _gpio_request_by_name_nodev(ofnode node, const char *list_name,
+ int index, struct gpio_desc *desc,
+ int flags, bool add_index)
+{
+ struct ofnode_phandle_args args;
+ int ret;
+
+ ret = ofnode_parse_phandle_with_args(node, list_name, "#gpio-cells", 0,
+ index, &args);
+
+ return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+ index, desc, flags, add_index, NULL);
+}
+
+int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
+ struct gpio_desc *desc, int flags)
+{
+ return _gpio_request_by_name_nodev(node, list_name, index, desc, flags,
+ index > 0);
+}
+
+int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
+ struct gpio_desc *desc, int flags)
+{
+ struct ofnode_phandle_args args;
+ ofnode node;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0,
+ index, &args);
+ node = dev_ofnode(dev);
+ return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+ index, desc, flags, index > 0, NULL);
+}
+
+int gpio_request_by_line_name(struct udevice *dev, const char *line_name,
+ struct gpio_desc *desc, int flags)
+{
+ int ret;
+
+ if (!dev) {
+ uclass_foreach_dev_probe(UCLASS_GPIO, dev)
+ if (!gpio_request_by_line_name(dev, line_name, desc, flags))
+ return 0;
+ return -ENOENT;
+ }
+
+ ret = dev_read_stringlist_search(dev, "gpio-line-names", line_name);
+ if (ret < 0)
+ return ret;
+
+ desc->dev = dev;
+ desc->offset = ret;
+ desc->flags = 0;
+
+ ret = dm_gpio_request(desc, line_name);
+ if (ret) {
+ debug("%s: dm_gpio_requestf failed\n", __func__);
+ return ret;
+ }
+
+ ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
+ if (ret)
+ debug("%s: dm_gpio_set_dir failed\n", __func__);
+
+ return ret;
+}
+
+int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
+ struct gpio_desc *desc, int max_count,
+ int flags)
+{
+ int count;
+ int ret;
+
+ for (count = 0; count < max_count; count++) {
+ ret = _gpio_request_by_name_nodev(node, list_name, count,
+ &desc[count], flags, true);
+ if (ret == -ENOENT)
+ break;
+ else if (ret)
+ goto err;
+ }
+
+ /* We ran out of GPIOs in the list */
+ return count;
+
+err:
+ gpio_free_list_nodev(desc, count);
+
+ return ret;
+}
+
+int gpio_request_list_by_name(struct udevice *dev, const char *list_name,
+ struct gpio_desc *desc, int max_count,
+ int flags)
+{
+ /*
+ * This isn't ideal since we don't use dev->name in the debug()
+ * calls in gpio_request_by_name(), but we can do this until
+ * gpio_request_list_by_name_nodev() can be dropped.
+ */
+ return gpio_request_list_by_name_nodev(dev_ofnode(dev), list_name, desc,
+ max_count, flags);
+}
+
+int gpio_get_list_count(struct udevice *dev, const char *list_name)
+{
+ int ret;
+
+ ret = dev_count_phandle_with_args(dev, list_name, "#gpio-cells",
+ -ENOENT);
+ if (ret < 0) {
+ debug("%s: Node '%s', property '%s', GPIO count failed: %d\n",
+ __func__, dev->name, list_name, ret);
+ }
+
+ return ret;
+}
+#endif /* OF_PLATDATA */
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+int gpio_request_by_phandle(struct udevice *dev,
+ const struct phandle_2_arg *cells,
+ struct gpio_desc *desc, int flags)
+{
+ struct ofnode_phandle_args args;
+ struct udevice *gpio_dev;
+ const int index = 0;
+ int ret;
+
+ ret = device_get_by_ofplat_idx(cells->idx, &gpio_dev);
+ if (ret)
+ return ret;
+ args.args[0] = cells->arg[0];
+ args.args[1] = cells->arg[1];
+
+ return gpio_request_tail(ret, NULL, &args, NULL, index, desc, flags,
+ index > 0, gpio_dev);
+}
+#endif
+
+int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)
+{
+ /* For now, we don't do any checking of dev */
+ return _dm_gpio_free(desc->dev, desc->offset);
+}
+
+int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count)
+{
+ int i;
+
+ /* For now, we don't do any checking of dev */
+ for (i = 0; i < count; i++)
+ dm_gpio_free(dev, &desc[i]);
+
+ return 0;
+}
+
+int gpio_free_list_nodev(struct gpio_desc *desc, int count)
+{
+ return gpio_free_list(NULL, desc, count);
+}
+
+/* We need to renumber the GPIOs when any driver is probed/removed */
+static int gpio_renumber(struct udevice *removed_dev)
+{
+ struct gpio_dev_priv *uc_priv;
+ struct udevice *dev;
+ struct uclass *uc;
+ unsigned base;
+ int ret;
+
+ ret = uclass_get(UCLASS_GPIO, &uc);
+ if (ret)
+ return ret;
+
+ /* Ensure that we have a base for each bank */
+ base = 0;
+ uclass_foreach_dev(dev, uc) {
+ if (device_active(dev) && dev != removed_dev) {
+ uc_priv = dev_get_uclass_priv(dev);
+ uc_priv->gpio_base = base;
+ base += uc_priv->gpio_count;
+ }
+ }
+
+ return 0;
+}
+
+int gpio_get_number(const struct gpio_desc *desc)
+{
+ struct udevice *dev = desc->dev;
+ struct gpio_dev_priv *uc_priv;
+
+ if (!dev)
+ return -1;
+ uc_priv = dev_get_uclass_priv(dev);
+
+ return uc_priv->gpio_base + desc->offset;
+}
+
+static int gpio_post_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
+ if (!uc_priv->name)
+ return -ENOMEM;
+
+ uc_priv->claimed = calloc(DIV_ROUND_UP(uc_priv->gpio_count,
+ GPIO_ALLOC_BITS),
+ GPIO_ALLOC_BITS / 8);
+ if (!uc_priv->claimed) {
+ free(uc_priv->name);
+ return -ENOMEM;
+ }
+
+ return gpio_renumber(NULL);
+}
+
+static int gpio_pre_remove(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int i;
+
+ for (i = 0; i < uc_priv->gpio_count; i++) {
+ if (uc_priv->name[i])
+ free(uc_priv->name[i]);
+ }
+ free(uc_priv->claimed);
+ free(uc_priv->name);
+
+ return gpio_renumber(dev);
+}
+
+int gpio_dev_request_index(struct udevice *dev, const char *nodename,
+ char *list_name, int index, int flags,
+ int dtflags, struct gpio_desc *desc)
+{
+ struct ofnode_phandle_args args;
+
+ args.node = ofnode_null();
+ args.args_count = 2;
+ args.args[0] = index;
+ args.args[1] = dtflags;
+
+ return gpio_request_tail(0, nodename, &args, list_name, index, desc,
+ flags, 0, dev);
+}
+
+static void devm_gpiod_release(struct udevice *dev, void *res)
+{
+ dm_gpio_free(dev, res);
+}
+
+static int devm_gpiod_match(struct udevice *dev, void *res, void *data)
+{
+ return res == data;
+}
+
+struct gpio_desc *devm_gpiod_get_index(struct udevice *dev, const char *id,
+ unsigned int index, int flags)
+{
+ int rc;
+ struct gpio_desc *desc;
+ char *propname;
+ static const char suffix[] = "-gpios";
+
+ propname = malloc(strlen(id) + sizeof(suffix));
+ if (!propname) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ strcpy(propname, id);
+ strcat(propname, suffix);
+
+ desc = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc),
+ __GFP_ZERO);
+ if (unlikely(!desc)) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = gpio_request_by_name(dev, propname, index, desc, flags);
+
+end:
+ if (propname)
+ free(propname);
+
+ if (rc)
+ return ERR_PTR(rc);
+
+ devres_add(dev, desc);
+
+ return desc;
+}
+
+struct gpio_desc *devm_gpiod_get_index_optional(struct udevice *dev,
+ const char *id,
+ unsigned int index,
+ int flags)
+{
+ struct gpio_desc *desc = devm_gpiod_get_index(dev, id, index, flags);
+
+ if (IS_ERR(desc))
+ return NULL;
+
+ return desc;
+}
+
+void devm_gpiod_put(struct udevice *dev, struct gpio_desc *desc)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_gpiod_release, devm_gpiod_match, desc);
+ WARN_ON(rc);
+}
+
+static int gpio_post_bind(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(GPIO_HOG) && dev_has_ofnode(dev)) {
+ struct udevice *child;
+ ofnode node;
+
+ dev_for_each_subnode(node, dev) {
+ if (ofnode_read_bool(node, "gpio-hog")) {
+ const char *name = ofnode_get_name(node);
+ int ret;
+
+ ret = device_bind_driver_to_node(dev,
+ "gpio_hog",
+ name, node,
+ &child);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure gpio-hogs are probed after bind
+ * since hogs can be essential to the hardware
+ * system.
+ */
+ dev_or_flags(child, DM_FLAG_PROBE_AFTER_BIND);
+ }
+ }
+ }
+
+ return 0;
+}
+
+UCLASS_DRIVER(gpio) = {
+ .id = UCLASS_GPIO,
+ .name = "gpio",
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .post_probe = gpio_post_probe,
+ .post_bind = gpio_post_bind,
+ .pre_remove = gpio_pre_remove,
+ .per_device_auto = sizeof(struct gpio_dev_priv),
+};
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
new file mode 100644
index 00000000000..61c705b5ac5
--- /dev/null
+++ b/drivers/gpio/gpio-uniphier.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016-2017 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+#include <linux/errno.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <dt-bindings/gpio/uniphier-gpio.h>
+
+#define UNIPHIER_GPIO_PORT_DATA 0x0 /* data */
+#define UNIPHIER_GPIO_PORT_DIR 0x4 /* direction (1:in, 0:out) */
+#define UNIPHIER_GPIO_IRQ_EN 0x90 /* irq enable */
+
+struct uniphier_gpio_priv {
+ void __iomem *regs;
+};
+
+static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
+{
+ unsigned int reg;
+
+ reg = (bank + 1) * 8;
+
+ /*
+ * Unfortunately, the GPIO port registers are not contiguous because
+ * offset 0x90-0x9f is used for IRQ. Add 0x10 when crossing the region.
+ */
+ if (reg >= UNIPHIER_GPIO_IRQ_EN)
+ reg += 0x10;
+
+ return reg;
+}
+
+static void uniphier_gpio_get_bank_and_mask(unsigned int offset,
+ unsigned int *bank, u32 *mask)
+{
+ *bank = offset / UNIPHIER_GPIO_LINES_PER_BANK;
+ *mask = BIT(offset % UNIPHIER_GPIO_LINES_PER_BANK);
+}
+
+static void uniphier_gpio_reg_update(struct uniphier_gpio_priv *priv,
+ unsigned int reg, u32 mask, u32 val)
+{
+ u32 tmp;
+
+ tmp = readl(priv->regs + reg);
+ tmp &= ~mask;
+ tmp |= mask & val;
+ writel(tmp, priv->regs + reg);
+}
+
+static void uniphier_gpio_bank_write(struct udevice *dev, unsigned int bank,
+ unsigned int reg, u32 mask, u32 val)
+{
+ struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+
+ if (!mask)
+ return;
+
+ uniphier_gpio_reg_update(priv, uniphier_gpio_bank_to_reg(bank) + reg,
+ mask, val);
+}
+
+static void uniphier_gpio_offset_write(struct udevice *dev, unsigned int offset,
+ unsigned int reg, int val)
+{
+ unsigned int bank;
+ u32 mask;
+
+ uniphier_gpio_get_bank_and_mask(offset, &bank, &mask);
+
+ uniphier_gpio_bank_write(dev, bank, reg, mask, val ? mask : 0);
+}
+
+static int uniphier_gpio_offset_read(struct udevice *dev,
+ unsigned int offset, unsigned int reg)
+{
+ struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+ unsigned int bank, reg_offset;
+ u32 mask;
+
+ uniphier_gpio_get_bank_and_mask(offset, &bank, &mask);
+ reg_offset = uniphier_gpio_bank_to_reg(bank) + reg;
+
+ return !!(readl(priv->regs + reg_offset) & mask);
+}
+
+static int uniphier_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_PORT_DIR) ?
+ GPIOF_INPUT : GPIOF_OUTPUT;
+}
+
+static int uniphier_gpio_direction_input(struct udevice *dev,
+ unsigned int offset)
+{
+ uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DIR, 1);
+
+ return 0;
+}
+
+static int uniphier_gpio_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DATA, value);
+ uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DIR, 0);
+
+ return 0;
+}
+
+static int uniphier_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_PORT_DATA);
+}
+
+static int uniphier_gpio_set_value(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_PORT_DATA, value);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops uniphier_gpio_ops = {
+ .direction_input = uniphier_gpio_direction_input,
+ .direction_output = uniphier_gpio_direction_output,
+ .get_value = uniphier_gpio_get_value,
+ .set_value = uniphier_gpio_set_value,
+ .get_function = uniphier_gpio_get_function,
+};
+
+static int uniphier_gpio_probe(struct udevice *dev)
+{
+ struct uniphier_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = devm_ioremap(dev, addr, SZ_512);
+ if (!priv->regs)
+ return -ENOMEM;
+
+ uc_priv->gpio_count = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
+ "ngpios", 0);
+
+ return 0;
+}
+
+static const struct udevice_id uniphier_gpio_match[] = {
+ { .compatible = "socionext,uniphier-gpio" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(uniphier_gpio) = {
+ .name = "uniphier-gpio",
+ .id = UCLASS_GPIO,
+ .of_match = uniphier_gpio_match,
+ .probe = uniphier_gpio_probe,
+ .priv_auto = sizeof(struct uniphier_gpio_priv),
+ .ops = &uniphier_gpio_ops,
+};
diff --git a/drivers/gpio/gpio_slg7xl45106.c b/drivers/gpio/gpio_slg7xl45106.c
new file mode 100644
index 00000000000..4ad06c18b4b
--- /dev/null
+++ b/drivers/gpio/gpio_slg7xl45106.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * slg7xl45106_i2c_gpo driver
+ *
+ * Copyright (C) 2021 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <i2c.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <asm/arch/hardware.h>
+
+#define SLG7XL45106_REG 0xdb
+
+static int slg7xl45106_i2c_gpo_direction_input(struct udevice *dev,
+ unsigned int offset)
+{
+ return 0;
+}
+
+static int slg7xl45106_i2c_gpo_xlate(struct udevice *dev,
+ struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = (unsigned int)args->args[0];
+ desc->flags = (args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0);
+
+ return 0;
+}
+
+static int slg7xl45106_i2c_gpo_set_value(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, SLG7XL45106_REG, &val, 1);
+ if (ret)
+ return ret;
+
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+
+ return dm_i2c_write(dev, SLG7XL45106_REG, &val, 1);
+}
+
+static int slg7xl45106_i2c_gpo_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ return slg7xl45106_i2c_gpo_set_value(dev, offset, value);
+}
+
+static int slg7xl45106_i2c_gpo_get_value(struct udevice *dev,
+ unsigned int offset)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, SLG7XL45106_REG, &val, 1);
+ if (ret)
+ return ret;
+
+ return !!(val & BIT(offset));
+}
+
+static int slg7xl45106_i2c_gpo_get_function(struct udevice *dev,
+ unsigned int offset)
+{
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops slg7xl45106_i2c_gpo_ops = {
+ .direction_input = slg7xl45106_i2c_gpo_direction_input,
+ .direction_output = slg7xl45106_i2c_gpo_direction_output,
+ .get_value = slg7xl45106_i2c_gpo_get_value,
+ .set_value = slg7xl45106_i2c_gpo_set_value,
+ .get_function = slg7xl45106_i2c_gpo_get_function,
+ .xlate = slg7xl45106_i2c_gpo_xlate,
+};
+
+static int slg7xl45106_i2c_gpo_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *label_ptr;
+
+ label_ptr = dev_read_prop(dev, "label", NULL);
+ if (label_ptr) {
+ uc_priv->bank_name = strdup(label_ptr);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+ } else {
+ uc_priv->bank_name = dev->name;
+ }
+
+ uc_priv->gpio_count = 8;
+
+ return 0;
+}
+
+static const struct udevice_id slg7xl45106_i2c_gpo_ids[] = {
+ { .compatible = "dlg,slg7xl45106",},
+ { }
+};
+
+U_BOOT_DRIVER(slg7xl45106_i2c_gpo) = {
+ .name = "slg7xl45106_i2c_gpo",
+ .id = UCLASS_GPIO,
+ .ops = &slg7xl45106_i2c_gpo_ops,
+ .of_match = slg7xl45106_i2c_gpo_ids,
+ .probe = slg7xl45106_i2c_gpo_probe,
+};
diff --git a/drivers/gpio/hi6220_gpio.c b/drivers/gpio/hi6220_gpio.c
new file mode 100644
index 00000000000..e287c31b93f
--- /dev/null
+++ b/drivers/gpio/hi6220_gpio.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Linaro
+ * Peter Griffin <peter.griffin@linaro.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <linux/bitops.h>
+
+static int hi6220_gpio_direction_input(struct udevice *dev, unsigned int gpio)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+ u8 data;
+
+ data = readb(bank->base + HI6220_GPIO_DIR);
+ data &= ~(1 << gpio);
+ writeb(data, bank->base + HI6220_GPIO_DIR);
+
+ return 0;
+}
+
+static int hi6220_gpio_set_value(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ writeb(!!value << gpio, bank->base + (BIT(gpio + 2)));
+ return 0;
+}
+
+static int hi6220_gpio_direction_output(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+ u8 data;
+
+ data = readb(bank->base + HI6220_GPIO_DIR);
+ data |= 1 << gpio;
+ writeb(data, bank->base + HI6220_GPIO_DIR);
+
+ hi6220_gpio_set_value(dev, gpio, value);
+
+ return 0;
+}
+
+static int hi6220_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ return !!readb(bank->base + (BIT(gpio + 2)));
+}
+
+static const struct dm_gpio_ops gpio_hi6220_ops = {
+ .direction_input = hi6220_gpio_direction_input,
+ .direction_output = hi6220_gpio_direction_output,
+ .get_value = hi6220_gpio_get_value,
+ .set_value = hi6220_gpio_set_value,
+};
+
+static int hi6220_gpio_probe(struct udevice *dev)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+ struct hikey_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[18], *str;
+
+ sprintf(name, "GPIO%d_", plat->bank_index);
+
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = HI6220_GPIO_PER_BANK;
+
+ bank->base = (u8 *)plat->base;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_hi6220) = {
+ .name = "gpio_hi6220",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_hi6220_ops,
+ .probe = hi6220_gpio_probe,
+ .priv_auto = sizeof(struct gpio_bank),
+};
diff --git a/drivers/gpio/hsdk-creg-gpio.c b/drivers/gpio/hsdk-creg-gpio.c
new file mode 100644
index 00000000000..66f8441840b
--- /dev/null
+++ b/drivers/gpio/hsdk-creg-gpio.c
@@ -0,0 +1,169 @@
+/*
+ * Synopsys HSDK SDP Generic PLL clock driver
+ *
+ * Copyright (C) 2017 Synopsys
+ * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <log.h>
+#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/printk.h>
+
+#define DRV_NAME "gpio_creg"
+
+struct hsdk_creg_gpio {
+ u32 *regs;
+ u8 shift;
+ u8 activate;
+ u8 deactivate;
+ u8 bit_per_gpio;
+};
+
+static int hsdk_creg_gpio_set_value(struct udevice *dev, unsigned oft, int val)
+{
+ struct hsdk_creg_gpio *hcg = dev_get_priv(dev);
+ u8 reg_shift = oft * hcg->bit_per_gpio + hcg->shift;
+ u32 reg = readl(hcg->regs);
+
+ reg &= ~(GENMASK(hcg->bit_per_gpio - 1, 0) << reg_shift);
+ reg |= ((val ? hcg->deactivate : hcg->activate) << reg_shift);
+
+ writel(reg, hcg->regs);
+
+ return 0;
+}
+
+static int hsdk_creg_gpio_direction_output(struct udevice *dev, unsigned oft,
+ int val)
+{
+ hsdk_creg_gpio_set_value(dev, oft, val);
+
+ return 0;
+}
+
+static int hsdk_creg_gpio_direction_input(struct udevice *dev, unsigned oft)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ pr_err("%s can't be used as input!\n", uc_priv->bank_name);
+
+ return -ENOTSUPP;
+}
+
+static int hsdk_creg_gpio_get_value(struct udevice *dev, unsigned int oft)
+{
+ struct hsdk_creg_gpio *hcg = dev_get_priv(dev);
+ u32 val = readl(hcg->regs);
+
+ val >>= oft * hcg->bit_per_gpio + hcg->shift;
+ val &= GENMASK(hcg->bit_per_gpio - 1, 0);
+ return (val == hcg->deactivate) ? 1 : 0;
+}
+
+static const struct dm_gpio_ops hsdk_creg_gpio_ops = {
+ .direction_output = hsdk_creg_gpio_direction_output,
+ .direction_input = hsdk_creg_gpio_direction_input,
+ .set_value = hsdk_creg_gpio_set_value,
+ .get_value = hsdk_creg_gpio_get_value,
+};
+
+static int hsdk_creg_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct hsdk_creg_gpio *hcg = dev_get_priv(dev);
+ u32 shift, bit_per_gpio, activate, deactivate, gpio_count;
+ const u8 *defaults;
+
+ hcg->regs = dev_read_addr_ptr(dev);
+ gpio_count = dev_read_u32_default(dev, "gpio-count", 1);
+ shift = dev_read_u32_default(dev, "gpio-first-shift", 0);
+ bit_per_gpio = dev_read_u32_default(dev, "gpio-bit-per-line", 1);
+ activate = dev_read_u32_default(dev, "gpio-activate-val", 1);
+ deactivate = dev_read_u32_default(dev, "gpio-deactivate-val", 0);
+ defaults = dev_read_u8_array_ptr(dev, "gpio-default-val", gpio_count);
+
+ uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
+ if (!uc_priv->bank_name)
+ uc_priv->bank_name = dev_read_name(dev);
+
+ if (!bit_per_gpio) {
+ pr_err("%s: 'gpio-bit-per-line' can't be 0\n",
+ uc_priv->bank_name);
+
+ return -EINVAL;
+ }
+
+ if (!gpio_count) {
+ pr_err("%s: 'gpio-count' can't be 0\n",
+ uc_priv->bank_name);
+
+ return -EINVAL;
+ }
+
+ if ((gpio_count * bit_per_gpio + shift) > 32) {
+ pr_err("%s: u32 io register overflow: try to use %u bits\n",
+ uc_priv->bank_name, gpio_count * bit_per_gpio + shift);
+
+ return -EINVAL;
+ }
+
+ if (GENMASK(31, bit_per_gpio) & activate) {
+ pr_err("%s: 'gpio-activate-val' can't be more than %lu\n",
+ uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0));
+
+ return -EINVAL;
+ }
+
+ if (GENMASK(31, bit_per_gpio) & deactivate) {
+ pr_err("%s: 'gpio-deactivate-val' can't be more than %lu\n",
+ uc_priv->bank_name, GENMASK(bit_per_gpio - 1, 0));
+
+ return -EINVAL;
+ }
+
+ if (activate == deactivate) {
+ pr_err("%s: 'gpio-deactivate-val' and 'gpio-activate-val' can't be equal\n",
+ uc_priv->bank_name);
+
+ return -EINVAL;
+ }
+
+ hcg->shift = (u8)shift;
+ hcg->bit_per_gpio = (u8)bit_per_gpio;
+ hcg->activate = (u8)activate;
+ hcg->deactivate = (u8)deactivate;
+ uc_priv->gpio_count = gpio_count;
+
+ /* Setup default GPIO value if we have "gpio-default-val" array */
+ if (defaults)
+ for (u8 i = 0; i < gpio_count; i++)
+ hsdk_creg_gpio_set_value(dev, i, defaults[i]);
+
+ pr_debug("%s GPIO [0x%p] controller with %d gpios probed\n",
+ uc_priv->bank_name, hcg->regs, uc_priv->gpio_count);
+
+ return 0;
+}
+
+static const struct udevice_id hsdk_creg_gpio_ids[] = {
+ { .compatible = "snps,creg-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_hsdk_creg) = {
+ .name = DRV_NAME,
+ .id = UCLASS_GPIO,
+ .ops = &hsdk_creg_gpio_ops,
+ .probe = hsdk_creg_gpio_probe,
+ .of_match = hsdk_creg_gpio_ids,
+ .plat_auto = sizeof(struct hsdk_creg_gpio),
+};
diff --git a/drivers/gpio/imx_rgpio2p.c b/drivers/gpio/imx_rgpio2p.c
new file mode 100644
index 00000000000..175e460aff5
--- /dev/null
+++ b/drivers/gpio/imx_rgpio2p.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * RGPIO2P driver for the Freescale i.MX7ULP.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <malloc.h>
+
+enum imx_rgpio2p_direction {
+ IMX_RGPIO2P_DIRECTION_IN,
+ IMX_RGPIO2P_DIRECTION_OUT,
+};
+
+#define GPIO_PER_BANK 32
+
+struct imx_rgpio2p_data {
+ struct gpio_regs *regs;
+};
+
+struct imx_rgpio2p_plat {
+ int bank_index;
+ struct gpio_regs *regs;
+};
+
+static int imx_rgpio2p_is_output(struct gpio_regs *regs, int offset)
+{
+ u32 val;
+
+ val = readl(&regs->gpio_pddr);
+
+ return val & (1 << offset) ? 1 : 0;
+}
+
+static int imx_rgpio2p_bank_get_direction(struct gpio_regs *regs, int offset)
+{
+ if ((readl(&regs->gpio_pddr) >> offset) & 0x01)
+ return IMX_RGPIO2P_DIRECTION_OUT;
+
+ return IMX_RGPIO2P_DIRECTION_IN;
+}
+
+static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset,
+ enum imx_rgpio2p_direction direction)
+{
+ u32 l;
+
+ l = readl(&regs->gpio_pddr);
+
+ switch (direction) {
+ case IMX_RGPIO2P_DIRECTION_OUT:
+ l |= 1 << offset;
+ break;
+ case IMX_RGPIO2P_DIRECTION_IN:
+ l &= ~(1 << offset);
+ }
+ writel(l, &regs->gpio_pddr);
+}
+
+static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset,
+ int value)
+{
+ if (value)
+ writel((1 << offset), &regs->gpio_psor);
+ else
+ writel((1 << offset), &regs->gpio_pcor);
+}
+
+static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset)
+{
+ if (imx_rgpio2p_bank_get_direction(regs, offset) ==
+ IMX_RGPIO2P_DIRECTION_IN)
+ return (readl(&regs->gpio_pdir) >> offset) & 0x01;
+
+ return (readl(&regs->gpio_pdor) >> offset) & 0x01;
+}
+
+static int imx_rgpio2p_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+
+ /* Configure GPIO direction as input. */
+ imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_IN);
+
+ return 0;
+}
+
+static int imx_rgpio2p_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+
+ /* Configure GPIO output value. */
+ imx_rgpio2p_bank_set_value(bank->regs, offset, value);
+
+ /* Configure GPIO direction as output. */
+ imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_OUT);
+
+ return 0;
+}
+
+static int imx_rgpio2p_get_value(struct udevice *dev, unsigned offset)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+
+ return imx_rgpio2p_bank_get_value(bank->regs, offset);
+}
+
+static int imx_rgpio2p_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+
+ imx_rgpio2p_bank_set_value(bank->regs, offset, value);
+
+ return 0;
+}
+
+static int imx_rgpio2p_get_function(struct udevice *dev, unsigned offset)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+
+ /* GPIOF_FUNC is not implemented yet */
+ if (imx_rgpio2p_is_output(bank->regs, offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops imx_rgpio2p_ops = {
+ .direction_input = imx_rgpio2p_direction_input,
+ .direction_output = imx_rgpio2p_direction_output,
+ .get_value = imx_rgpio2p_get_value,
+ .set_value = imx_rgpio2p_set_value,
+ .get_function = imx_rgpio2p_get_function,
+};
+
+static int imx_rgpio2p_probe(struct udevice *dev)
+{
+ struct imx_rgpio2p_data *bank = dev_get_priv(dev);
+ struct imx_rgpio2p_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int banknum;
+ char name[18], *str;
+
+ banknum = plat->bank_index;
+ sprintf(name, "GPIO%d_", banknum + 1);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ bank->regs = plat->regs;
+
+ return 0;
+}
+
+static int imx_rgpio2p_bind(struct udevice *dev)
+{
+ struct imx_rgpio2p_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ /*
+ * If plat already exsits, directly return.
+ * Actually only when DT is not supported, plat
+ * is statically initialized in U_BOOT_DRVINFOS.Here
+ * will return.
+ */
+ if (plat)
+ return 0;
+
+ addr = devfdt_get_addr_index(dev, 1);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /*
+ * TODO:
+ * When every board is converted to driver model and DT is supported,
+ * this can be done by auto-alloc feature, but not using calloc
+ * to alloc memory for plat.
+ *
+ * For example imx_rgpio2p_plat uses platform data rather than device
+ * tree.
+ *
+ * NOTE: DO NOT COPY this code if you are using device tree.
+ */
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+
+ plat->regs = (struct gpio_regs *)addr;
+ plat->bank_index = dev_seq(dev);
+ dev_set_plat(dev, plat);
+
+ return 0;
+}
+
+
+static const struct udevice_id imx_rgpio2p_ids[] = {
+ { .compatible = "fsl,imx7ulp-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(imx_rgpio2p) = {
+ .name = "imx_rgpio2p",
+ .id = UCLASS_GPIO,
+ .ops = &imx_rgpio2p_ops,
+ .probe = imx_rgpio2p_probe,
+ .priv_auto = sizeof(struct imx_rgpio2p_plat),
+ .of_match = imx_rgpio2p_ids,
+ .bind = imx_rgpio2p_bind,
+};
+
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+static const struct imx_rgpio2p_plat imx_plat[] = {
+ { 0, (struct gpio_regs *)RGPIO2P_GPIO1_BASE_ADDR },
+ { 1, (struct gpio_regs *)RGPIO2P_GPIO2_BASE_ADDR },
+ { 2, (struct gpio_regs *)RGPIO2P_GPIO3_BASE_ADDR },
+ { 3, (struct gpio_regs *)RGPIO2P_GPIO4_BASE_ADDR },
+ { 4, (struct gpio_regs *)RGPIO2P_GPIO5_BASE_ADDR },
+ { 5, (struct gpio_regs *)RGPIO2P_GPIO6_BASE_ADDR },
+};
+
+U_BOOT_DRVINFOS(imx_rgpio2ps) = {
+ { "imx_rgpio2p", &imx_plat[0] },
+ { "imx_rgpio2p", &imx_plat[1] },
+ { "imx_rgpio2p", &imx_plat[2] },
+ { "imx_rgpio2p", &imx_plat[3] },
+ { "imx_rgpio2p", &imx_plat[4] },
+ { "imx_rgpio2p", &imx_plat[5] },
+};
+#endif
diff --git a/drivers/gpio/intel_broadwell_gpio.c b/drivers/gpio/intel_broadwell_gpio.c
new file mode 100644
index 00000000000..20af35de2cf
--- /dev/null
+++ b/drivers/gpio/intel_broadwell_gpio.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <pch.h>
+#include <pci.h>
+#include <syscon.h>
+#include <asm/cpu.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/arch/gpio.h>
+#include <dt-bindings/gpio/x86-gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct broadwell_bank_priv - Private driver data
+ *
+ * @regs: Pointer to GPIO registers
+ * @bank: Bank number for this bank (0, 1 or 2)
+ * @offset: GPIO offset for this bank (0, 32 or 64)
+ */
+struct broadwell_bank_priv {
+ struct pch_lp_gpio_regs *regs;
+ int bank;
+ int offset;
+};
+
+static int broadwell_gpio_request(struct udevice *dev, unsigned offset,
+ const char *label)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+ u32 val;
+
+ /*
+ * Make sure that the GPIO pin we want isn't already in use for some
+ * built-in hardware function. We have to check this for every
+ * requested pin.
+ */
+ debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset);
+ val = inl(&regs->own[priv->bank]);
+ if (!(val & (1UL << offset))) {
+ debug("gpio is reserved for internal use\n");
+ return -EPERM;
+ }
+ debug("ok\n");
+
+ return 0;
+}
+
+static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+
+ setio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
+
+ return 0;
+}
+
+static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+
+ return inl(&regs->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
+ 1 : 0;
+}
+
+static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+
+ debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
+ value);
+ clrsetio_32(&regs->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
+ value ? CONFA_OUTPUT_HIGH : 0);
+
+ return 0;
+}
+
+static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+
+ broadwell_gpio_set_value(dev, offset, value);
+ clrio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
+
+ return 0;
+}
+
+static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct pch_lp_gpio_regs *regs = priv->regs;
+ u32 mask = 1UL << offset;
+
+ if (!(inl(&regs->own[priv->bank]) & mask))
+ return GPIOF_FUNC;
+ if (inl(&regs->config[priv->offset + offset]) & CONFA_DIR_INPUT)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static int broadwell_gpio_probe(struct udevice *dev)
+{
+ struct broadwell_bank_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct broadwell_bank_priv *priv = dev_get_priv(dev);
+ struct udevice *pinctrl;
+ int ret;
+
+ /* Set up pin control if available */
+ ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl);
+ debug("%s, pinctrl=%p, ret=%d\n", __func__, pinctrl, ret);
+
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ uc_priv->bank_name = plat->bank_name;
+
+ priv->regs = (struct pch_lp_gpio_regs *)(uintptr_t)plat->base_addr;
+ priv->bank = plat->bank;
+ priv->offset = priv->bank * 32;
+ debug("%s: probe done, regs %p, bank %d\n", __func__, priv->regs,
+ priv->bank);
+
+ return 0;
+}
+
+static int broadwell_gpio_of_to_plat(struct udevice *dev)
+{
+ struct broadwell_bank_plat *plat = dev_get_plat(dev);
+ u32 gpiobase;
+ int bank;
+ int ret;
+
+ ret = pch_get_gpio_base(dev->parent, &gpiobase);
+ if (ret)
+ return ret;
+
+ bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
+ if (bank == -1) {
+ debug("%s: Invalid bank number %d\n", __func__, bank);
+ return -EINVAL;
+ }
+ plat->bank = bank;
+ plat->base_addr = gpiobase;
+ plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "bank-name", NULL);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_broadwell_ops = {
+ .request = broadwell_gpio_request,
+ .direction_input = broadwell_gpio_direction_input,
+ .direction_output = broadwell_gpio_direction_output,
+ .get_value = broadwell_gpio_get_value,
+ .set_value = broadwell_gpio_set_value,
+ .get_function = broadwell_gpio_get_function,
+};
+
+static const struct udevice_id intel_broadwell_gpio_ids[] = {
+ { .compatible = "intel,broadwell-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_broadwell) = {
+ .name = "gpio_broadwell",
+ .id = UCLASS_GPIO,
+ .of_match = intel_broadwell_gpio_ids,
+ .ops = &gpio_broadwell_ops,
+ .of_to_plat = broadwell_gpio_of_to_plat,
+ .probe = broadwell_gpio_probe,
+ .priv_auto = sizeof(struct broadwell_bank_priv),
+ .plat_auto = sizeof(struct broadwell_bank_plat),
+};
diff --git a/drivers/gpio/intel_gpio.c b/drivers/gpio/intel_gpio.c
new file mode 100644
index 00000000000..4a3ec6d6350
--- /dev/null
+++ b/drivers/gpio/intel_gpio.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#define LOG_CATEGORY UCLASS_GPIO
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <p2sb.h>
+#include <pch.h>
+#include <pci.h>
+#include <syscon.h>
+#include <acpi/acpi_device.h>
+#include <asm/cpu.h>
+#include <asm/gpio.h>
+#include <asm/intel_pinctrl.h>
+#include <asm/intel_pinctrl_defs.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/arch/gpio.h>
+#include <dm/acpi.h>
+#include <dm/device-internal.h>
+#include <dt-bindings/gpio/x86-gpio.h>
+
+static int intel_gpio_get_value(struct udevice *dev, uint offset)
+{
+ struct udevice *pinctrl = dev_get_parent(dev);
+ uint mode, rx_tx;
+ u32 reg;
+
+ reg = intel_pinctrl_get_config_reg(pinctrl, offset);
+ mode = (reg & PAD_CFG0_MODE_MASK) >> PAD_CFG0_MODE_SHIFT;
+ if (!mode) {
+ rx_tx = reg & (PAD_CFG0_TX_DISABLE | PAD_CFG0_RX_DISABLE);
+ if (rx_tx == PAD_CFG0_TX_DISABLE)
+ return reg & PAD_CFG0_RX_STATE ? 1 : 0;
+ else if (rx_tx == PAD_CFG0_RX_DISABLE)
+ return reg & PAD_CFG0_TX_STATE ? 1 : 0;
+ }
+
+ return 0;
+}
+
+static int intel_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct udevice *pinctrl = dev_get_parent(dev);
+ uint config_offset;
+
+ config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
+
+ pcr_clrsetbits32(pinctrl, config_offset, PAD_CFG0_TX_STATE,
+ value ? PAD_CFG0_TX_STATE : 0);
+
+ return 0;
+}
+
+static int intel_gpio_get_function(struct udevice *dev, uint offset)
+{
+ struct udevice *pinctrl = dev_get_parent(dev);
+ uint mode, rx_tx;
+ u32 reg;
+
+ reg = intel_pinctrl_get_config_reg(pinctrl, offset);
+ mode = (reg & PAD_CFG0_MODE_MASK) >> PAD_CFG0_MODE_SHIFT;
+ if (!mode) {
+ rx_tx = reg & (PAD_CFG0_TX_DISABLE | PAD_CFG0_RX_DISABLE);
+ if (rx_tx == PAD_CFG0_TX_DISABLE)
+ return GPIOF_INPUT;
+ else if (rx_tx == PAD_CFG0_RX_DISABLE)
+ return GPIOF_OUTPUT;
+ }
+
+ return GPIOF_FUNC;
+}
+
+static int intel_gpio_xlate(struct udevice *orig_dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ struct udevice *pinctrl, *dev;
+ int gpio, ret;
+
+ /*
+ * GPIO numbers are global in the device tree so it doesn't matter
+ * which @orig_dev is used
+ */
+ gpio = args->args[0];
+ ret = intel_pinctrl_get_pad(gpio, &pinctrl, &desc->offset);
+ if (ret)
+ return log_msg_ret("bad", ret);
+ device_find_first_child(pinctrl, &dev);
+ if (!dev)
+ return log_msg_ret("no child", -ENOENT);
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+ desc->dev = dev;
+
+ /*
+ * Handle the case where the wrong GPIO device was provided, since this
+ * will not have been probed by the GPIO uclass before calling here
+ * (see gpio_request_tail()).
+ */
+ if (orig_dev != dev) {
+ ret = device_probe(dev);
+ if (ret)
+ return log_msg_ret("probe", ret);
+ }
+
+ return 0;
+}
+
+static int intel_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ struct udevice *pinctrl = dev_get_parent(dev);
+ u32 bic0 = 0, bic1 = 0;
+ u32 or0, or1;
+ uint config_offset;
+
+ config_offset = intel_pinctrl_get_config_reg_offset(pinctrl, offset);
+
+ if (flags & GPIOD_IS_OUT) {
+ bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_RX_STATE |
+ PAD_CFG0_TX_DISABLE;
+ or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_RX_DISABLE;
+ } else if (flags & GPIOD_IS_IN) {
+ bic0 |= PAD_CFG0_MODE_MASK | PAD_CFG0_TX_STATE |
+ PAD_CFG0_RX_DISABLE;
+ or0 |= PAD_CFG0_MODE_GPIO | PAD_CFG0_TX_DISABLE;
+ }
+ if (flags & GPIOD_PULL_UP) {
+ bic1 |= PAD_CFG1_PULL_MASK;
+ or1 |= PAD_CFG1_PULL_UP_20K;
+ } else if (flags & GPIOD_PULL_DOWN) {
+ bic1 |= PAD_CFG1_PULL_MASK;
+ or1 |= PAD_CFG1_PULL_DN_20K;
+ }
+
+ pcr_clrsetbits32(pinctrl, PAD_CFG0_OFFSET(config_offset), bic0, or0);
+ pcr_clrsetbits32(pinctrl, PAD_CFG1_OFFSET(config_offset), bic1, or1);
+ log_debug("%s: flags=%lx, offset=%x, config_offset=%x, %x/%x %x/%x\n",
+ dev->name, flags, offset, config_offset, bic0, or0, bic1, or1);
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int intel_gpio_get_acpi(const struct gpio_desc *desc,
+ struct acpi_gpio *gpio)
+{
+ struct udevice *pinctrl;
+ int ret;
+
+ if (!dm_gpio_is_valid(desc))
+ return -ENOENT;
+ pinctrl = dev_get_parent(desc->dev);
+
+ memset(gpio, '\0', sizeof(*gpio));
+
+ gpio->type = ACPI_GPIO_TYPE_IO;
+ gpio->pull = ACPI_GPIO_PULL_DEFAULT;
+ gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_OUTPUT;
+ gpio->polarity = ACPI_GPIO_ACTIVE_HIGH;
+ gpio->pin_count = 1;
+ gpio->pins[0] = intel_pinctrl_get_acpi_pin(pinctrl, desc->offset);
+ gpio->pin0_addr = intel_pinctrl_get_config_reg_addr(pinctrl,
+ desc->offset);
+ ret = acpi_get_path(pinctrl, gpio->resource, sizeof(gpio->resource));
+ if (ret)
+ return log_msg_ret("resource", ret);
+
+ return 0;
+}
+#endif
+
+static int intel_gpio_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static int intel_gpio_of_to_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *upriv = dev_get_uclass_priv(dev);
+ struct intel_pinctrl_priv *pinctrl_priv = dev_get_priv(dev->parent);
+ const struct pad_community *comm = pinctrl_priv->comm;
+
+ upriv->gpio_count = comm->last_pad - comm->first_pad + 1;
+ upriv->bank_name = dev->name;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_intel_ops = {
+ .get_value = intel_gpio_get_value,
+ .set_value = intel_gpio_set_value,
+ .get_function = intel_gpio_get_function,
+ .xlate = intel_gpio_xlate,
+ .set_flags = intel_gpio_set_flags,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+ .get_acpi = intel_gpio_get_acpi,
+#endif
+};
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static const struct udevice_id intel_intel_gpio_ids[] = {
+ { .compatible = "intel,gpio" },
+ { }
+};
+#endif
+
+U_BOOT_DRIVER(intel_gpio) = {
+ .name = "intel_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = of_match_ptr(intel_intel_gpio_ids),
+ .ops = &gpio_intel_ops,
+ .of_to_plat = intel_gpio_of_to_plat,
+ .probe = intel_gpio_probe,
+};
diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c
new file mode 100644
index 00000000000..2ed0d0bea9a
--- /dev/null
+++ b/drivers/gpio/intel_ich6_gpio.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ */
+
+/*
+ * This is a GPIO driver for Intel ICH6 and later. The x86 GPIOs are accessed
+ * through the PCI bus. Each PCI device has 256 bytes of configuration space,
+ * consisting of a standard header and a device-specific set of registers. PCI
+ * bus 0, device 31, function 0 gives us access to the chipset GPIOs (among
+ * other things). Within the PCI configuration space, the GPIOBASE register
+ * tells us where in the device's I/O region we can find more registers to
+ * actually access the GPIOs.
+ *
+ * PCI bus/device/function 0:1f:0 => PCI config registers
+ * PCI config register "GPIOBASE"
+ * PCI I/O space + [GPIOBASE] => start of GPIO registers
+ * GPIO registers => gpio pin function, direction, value
+ *
+ *
+ * Danger Will Robinson! Bank 0 (GPIOs 0-31) seems to be fairly stable. Most
+ * ICH versions have more, but the decoding the matrix that describes them is
+ * absurdly complex and constantly changing. We'll provide Bank 1 and Bank 2,
+ * but they will ONLY work for certain unspecified chipsets because the offset
+ * from GPIOBASE changes randomly. Even then, many GPIOs are unimplemented or
+ * reserved or subject to arcane restrictions.
+ */
+
+#define LOG_CATEGORY UCLASS_GPIO
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <pch.h>
+#include <pci.h>
+#include <asm/cpu.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define GPIO_PER_BANK 32
+
+struct ich6_bank_priv {
+ /* These are I/O addresses */
+ uint16_t use_sel;
+ uint16_t io_sel;
+ uint16_t lvl;
+ u32 lvl_write_cache;
+ bool use_lvl_write_cache;
+};
+
+#define GPIO_USESEL_OFFSET(x) (x)
+#define GPIO_IOSEL_OFFSET(x) (x + 4)
+#define GPIO_LVL_OFFSET(x) (x + 8)
+
+static int _ich6_gpio_set_value(struct ich6_bank_priv *bank, unsigned offset,
+ int value)
+{
+ u32 val;
+
+ if (bank->use_lvl_write_cache)
+ val = bank->lvl_write_cache;
+ else
+ val = inl(bank->lvl);
+
+ if (value)
+ val |= (1UL << offset);
+ else
+ val &= ~(1UL << offset);
+ outl(val, bank->lvl);
+ if (bank->use_lvl_write_cache)
+ bank->lvl_write_cache = val;
+
+ return 0;
+}
+
+static int _ich6_gpio_set_direction(uint16_t base, unsigned offset, int dir)
+{
+ u32 val;
+
+ if (!dir) {
+ val = inl(base);
+ val |= (1UL << offset);
+ outl(val, base);
+ } else {
+ val = inl(base);
+ val &= ~(1UL << offset);
+ outl(val, base);
+ }
+
+ return 0;
+}
+
+static int gpio_ich6_of_to_plat(struct udevice *dev)
+{
+ struct ich6_bank_plat *plat = dev_get_plat(dev);
+ u32 gpiobase;
+ int offset;
+ int ret;
+
+ ret = pch_get_gpio_base(dev->parent, &gpiobase);
+ if (ret)
+ return ret;
+
+ offset = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
+ if (offset == -1) {
+ debug("%s: Invalid register offset %d\n", __func__, offset);
+ return -EINVAL;
+ }
+ plat->offset = offset;
+ plat->base_addr = gpiobase + offset;
+ plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "bank-name", NULL);
+
+ return 0;
+}
+
+static int ich6_gpio_probe(struct udevice *dev)
+{
+ struct ich6_bank_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+ const void *prop;
+
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ uc_priv->bank_name = plat->bank_name;
+ bank->use_sel = plat->base_addr;
+ bank->io_sel = plat->base_addr + 4;
+ bank->lvl = plat->base_addr + 8;
+
+ prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "use-lvl-write-cache", NULL);
+ if (prop)
+ bank->use_lvl_write_cache = true;
+ else
+ bank->use_lvl_write_cache = false;
+ bank->lvl_write_cache = 0;
+
+ return 0;
+}
+
+static int ich6_gpio_request(struct udevice *dev, unsigned offset,
+ const char *label)
+{
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+ u32 tmplong;
+
+ /*
+ * Make sure that the GPIO pin we want isn't already in use for some
+ * built-in hardware function. We have to check this for every
+ * requested pin.
+ */
+ tmplong = inl(bank->use_sel);
+ if (!(tmplong & (1UL << offset))) {
+ log_debug("gpio %d is reserved for internal use\n", offset);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int ich6_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+
+ return _ich6_gpio_set_direction(bank->io_sel, offset, 0);
+}
+
+static int ich6_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ int ret;
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+
+ ret = _ich6_gpio_set_direction(bank->io_sel, offset, 1);
+ if (ret)
+ return ret;
+
+ return _ich6_gpio_set_value(bank, offset, value);
+}
+
+static int ich6_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+ u32 tmplong;
+ int r;
+
+ tmplong = inl(bank->lvl);
+ if (bank->use_lvl_write_cache)
+ tmplong |= bank->lvl_write_cache;
+ r = (tmplong & (1UL << offset)) ? 1 : 0;
+ return r;
+}
+
+static int ich6_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+ return _ich6_gpio_set_value(bank, offset, value);
+}
+
+static int ich6_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct ich6_bank_priv *bank = dev_get_priv(dev);
+ u32 mask = 1UL << offset;
+
+ if (!(inl(bank->use_sel) & mask))
+ return GPIOF_FUNC;
+ if (inl(bank->io_sel) & mask)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops gpio_ich6_ops = {
+ .request = ich6_gpio_request,
+ .direction_input = ich6_gpio_direction_input,
+ .direction_output = ich6_gpio_direction_output,
+ .get_value = ich6_gpio_get_value,
+ .set_value = ich6_gpio_set_value,
+ .get_function = ich6_gpio_get_function,
+};
+
+static const struct udevice_id intel_ich6_gpio_ids[] = {
+ { .compatible = "intel,ich6-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_ich6) = {
+ .name = "gpio_ich6",
+ .id = UCLASS_GPIO,
+ .of_match = intel_ich6_gpio_ids,
+ .ops = &gpio_ich6_ops,
+ .of_to_plat = gpio_ich6_of_to_plat,
+ .probe = ich6_gpio_probe,
+ .priv_auto = sizeof(struct ich6_bank_priv),
+ .plat_auto = sizeof(struct ich6_bank_plat),
+};
diff --git a/drivers/gpio/iproc_gpio.c b/drivers/gpio/iproc_gpio.c
new file mode 100644
index 00000000000..7187d3257b9
--- /dev/null
+++ b/drivers/gpio/iproc_gpio.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Broadcom
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/pinctrl.h>
+
+/*
+ * There are five GPIO bank register. Each bank can configure max of 32 gpios.
+ * BANK0 - gpios 0 to 31
+ * BANK1 - gpios 32 to 63
+ * BANK2 - gpios 64 to 95
+ * BANK3 - gpios 96 to 127
+ * BANK4 - gpios 128 to 150
+ *
+ * Offset difference between consecutive bank register is 0x200
+ */
+#define NGPIO_PER_BANK 32
+#define GPIO_BANK_SIZE 0x200
+#define GPIO_BANK(pin) ((pin) / NGPIO_PER_BANK)
+#define GPIO_SHIFT(pin) ((pin) % NGPIO_PER_BANK)
+#define GPIO_REG(pin, reg) (GPIO_BANK_SIZE * GPIO_BANK(pin) + (reg))
+
+/* device register offset */
+#define DATA_IN_OFFSET 0x00
+#define DATA_OUT_OFFSET 0x04
+#define OUT_EN_OFFSET 0x08
+
+/**
+ * struct iproc_gpio_pctrl_map - gpio and pinctrl mapping
+ * @gpio_pin: start of gpio number in gpio-ranges
+ * @pctrl_pin: start of pinctrl number in gpio-ranges
+ * @npins: total number of pins in gpio-ranges
+ * @node: list node
+ */
+struct iproc_gpio_pctrl_map {
+ u32 gpio_pin;
+ u32 pctrl_pin;
+ u32 npins;
+ struct list_head node;
+};
+
+/**
+ * struct iproc_gpio_pctrl_map - gpio device instance
+ * @pinctrl_dev:pointer to pinctrl device
+ * @gpiomap: list node having mapping between gpio and pinctrl
+ * @base: I/O register base address of gpio device
+ * @name: gpio device name, ex GPIO0, GPIO1
+ * @ngpios: total number of gpios
+ */
+struct iproc_gpio_plat {
+ struct udevice *pinctrl_dev;
+ struct list_head gpiomap;
+ void __iomem *base;
+ char *name;
+ u32 ngpios;
+};
+
+/**
+ * iproc_gpio_set_bit - set or clear one bit in an iproc GPIO register.
+ *
+ * The bit relates to a GPIO pin.
+ *
+ * @plat: iproc GPIO device
+ * @reg: register offset
+ * @gpio: GPIO pin
+ * @set: set or clear
+ */
+static inline void iproc_gpio_set_bit(struct iproc_gpio_plat *plat,
+ u32 reg, u32 gpio, bool set)
+{
+ u32 offset = GPIO_REG(gpio, reg);
+ u32 shift = GPIO_SHIFT(gpio);
+
+ clrsetbits_le32(plat->base + offset, BIT(shift),
+ (set ? BIT(shift) : 0));
+}
+
+static inline bool iproc_gpio_get_bit(struct iproc_gpio_plat *plat,
+ u32 reg, u32 gpio)
+{
+ u32 offset = GPIO_REG(gpio, reg);
+ u32 shift = GPIO_SHIFT(gpio);
+
+ return readl(plat->base + offset) & BIT(shift);
+}
+
+/**
+ * iproc_get_gpio_pctrl_mapping() - get associated pinctrl pin from gpio pin
+ *
+ * @plat: iproc GPIO device
+ * @gpio: GPIO pin
+ */
+static u32 iproc_get_pctrl_from_gpio(struct iproc_gpio_plat *plat, u32 gpio)
+{
+ struct iproc_gpio_pctrl_map *range = NULL;
+ struct list_head *pos, *tmp;
+ u32 ret = 0;
+
+ list_for_each_safe(pos, tmp, &plat->gpiomap) {
+ range = list_entry(pos, struct iproc_gpio_pctrl_map, node);
+ if (gpio == range->gpio_pin ||
+ gpio < (range->gpio_pin + range->npins)) {
+ ret = range->pctrl_pin + (gpio - range->gpio_pin);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * iproc_get_gpio_pctrl_mapping() - get mapping between gpio and pinctrl
+ *
+ * Read dt node "gpio-ranges" to get gpio and pinctrl mapping and store
+ * in private data structure to use it later while enabling gpio.
+ *
+ * @dev: pointer to GPIO device
+ * Return: 0 on success and -ENOMEM on failure
+ */
+static int iproc_get_gpio_pctrl_mapping(struct udevice *dev)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+ struct iproc_gpio_pctrl_map *range = NULL;
+ struct ofnode_phandle_args args;
+ int index = 0, ret;
+
+ for (;; index++) {
+ ret = dev_read_phandle_with_args(dev, "gpio-ranges",
+ NULL, 3, index, &args);
+ if (ret)
+ break;
+
+ range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+ if (!range)
+ return -ENOMEM;
+
+ range->gpio_pin = args.args[0];
+ range->pctrl_pin = args.args[1];
+ range->npins = args.args[2];
+ list_add_tail(&range->node, &plat->gpiomap);
+ }
+
+ return 0;
+}
+
+static int iproc_gpio_request(struct udevice *dev, u32 gpio, const char *label)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+ u32 pctrl;
+
+ /* nothing to do if there is no corresponding pinctrl device */
+ if (!plat->pinctrl_dev)
+ return 0;
+
+ pctrl = iproc_get_pctrl_from_gpio(plat, gpio);
+
+ return pinctrl_request(plat->pinctrl_dev, pctrl, 0);
+}
+
+static int iproc_gpio_direction_input(struct udevice *dev, u32 gpio)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+
+ iproc_gpio_set_bit(plat, OUT_EN_OFFSET, gpio, false);
+ dev_dbg(dev, "gpio:%u set input\n", gpio);
+
+ return 0;
+}
+
+static int iproc_gpio_direction_output(struct udevice *dev, u32 gpio, int value)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+
+ iproc_gpio_set_bit(plat, OUT_EN_OFFSET, gpio, true);
+ iproc_gpio_set_bit(plat, DATA_OUT_OFFSET, gpio, value);
+ dev_dbg(dev, "gpio:%u set output, value:%d\n", gpio, value);
+
+ return 0;
+}
+
+static int iproc_gpio_get_value(struct udevice *dev, u32 gpio)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+ int value;
+
+ value = iproc_gpio_get_bit(plat, DATA_IN_OFFSET, gpio);
+ dev_dbg(dev, "gpio:%u get, value:%d\n", gpio, value);
+
+ return value;
+}
+
+static int iproc_gpio_set_value(struct udevice *dev, u32 gpio, int value)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+
+ if (iproc_gpio_get_bit(plat, OUT_EN_OFFSET, gpio))
+ iproc_gpio_set_bit(plat, DATA_OUT_OFFSET, gpio, value);
+
+ dev_dbg(dev, "gpio:%u set, value:%d\n", gpio, value);
+ return 0;
+}
+
+static int iproc_gpio_get_function(struct udevice *dev, u32 gpio)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+
+ if (iproc_gpio_get_bit(plat, OUT_EN_OFFSET, gpio))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int iproc_gpio_of_to_plat(struct udevice *dev)
+{
+ struct iproc_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret;
+ char name[10];
+
+ plat->base = dev_read_addr_ptr(dev);
+ if (!plat->base) {
+ debug("%s: Failed to get base address\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = dev_read_u32(dev, "ngpios", &plat->ngpios);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to get ngpios\n", __func__);
+ return ret;
+ }
+
+ uclass_get_device_by_phandle(UCLASS_PINCTRL, dev, "gpio-ranges",
+ &plat->pinctrl_dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to get pinctrl phandle\n", __func__);
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&plat->gpiomap);
+ ret = iproc_get_gpio_pctrl_mapping(dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: Failed to get gpio to pctrl map ret(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "GPIO%d", dev_seq(dev));
+ plat->name = strdup(name);
+ if (!plat->name)
+ return -ENOMEM;
+
+ uc_priv->gpio_count = plat->ngpios;
+ uc_priv->bank_name = plat->name;
+
+ dev_info(dev, ":bank name(%s) base %p, #gpios %d\n",
+ plat->name, plat->base, plat->ngpios);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops iproc_gpio_ops = {
+ .request = iproc_gpio_request,
+ .direction_input = iproc_gpio_direction_input,
+ .direction_output = iproc_gpio_direction_output,
+ .get_value = iproc_gpio_get_value,
+ .set_value = iproc_gpio_set_value,
+ .get_function = iproc_gpio_get_function,
+};
+
+static const struct udevice_id iproc_gpio_ids[] = {
+ { .compatible = "brcm,iproc-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(iproc_gpio) = {
+ .name = "iproc_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = iproc_gpio_ids,
+ .ops = &iproc_gpio_ops,
+ .of_to_plat = iproc_gpio_of_to_plat,
+ .plat_auto = sizeof(struct iproc_gpio_plat),
+};
diff --git a/drivers/gpio/kw_gpio.c b/drivers/gpio/kw_gpio.c
new file mode 100644
index 00000000000..a15769793f1
--- /dev/null
+++ b/drivers/gpio/kw_gpio.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * arch/arm/plat-orion/gpio.c
+ *
+ * Marvell Orion SoC GPIO handling.
+ */
+
+/*
+ * Based on (mostly copied from) plat-orion based Linux 2.6 kernel driver.
+ * Removed orion_gpiochip struct and kernel level irq handling.
+ *
+ * Dieter Kiermaier dk-arm-linux@gmx.de
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/arch/soc.h>
+#include <asm/arch/gpio.h>
+
+static unsigned long gpio_valid_input[BITS_TO_LONGS(GPIO_MAX)];
+static unsigned long gpio_valid_output[BITS_TO_LONGS(GPIO_MAX)];
+
+void __set_direction(unsigned pin, int input)
+{
+ u32 u;
+
+ u = readl(GPIO_IO_CONF(pin));
+ if (input)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_IO_CONF(pin));
+
+ u = readl(GPIO_IO_CONF(pin));
+}
+
+static void __set_level(unsigned pin, int high)
+{
+ u32 u;
+
+ u = readl(GPIO_OUT(pin));
+ if (high)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_OUT(pin));
+}
+
+static void __set_blinking(unsigned pin, int blink)
+{
+ u32 u;
+
+ u = readl(GPIO_BLINK_EN(pin));
+ if (blink)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_BLINK_EN(pin));
+}
+
+int kw_gpio_is_valid(unsigned pin, int mode)
+{
+ if (pin < GPIO_MAX) {
+ if ((mode & GPIO_INPUT_OK) && !test_bit(pin, gpio_valid_input))
+ goto err_out;
+
+ if ((mode & GPIO_OUTPUT_OK) && !test_bit(pin, gpio_valid_output))
+ goto err_out;
+ return 0;
+ }
+
+err_out:
+ printf("%s: invalid GPIO %d\n", __func__, pin);
+ return 1;
+}
+
+void kw_gpio_set_valid(unsigned pin, int mode)
+{
+ if (mode == 1)
+ mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK;
+ if (mode & GPIO_INPUT_OK)
+ __set_bit(pin, gpio_valid_input);
+ else
+ __clear_bit(pin, gpio_valid_input);
+ if (mode & GPIO_OUTPUT_OK)
+ __set_bit(pin, gpio_valid_output);
+ else
+ __clear_bit(pin, gpio_valid_output);
+}
+/*
+ * GENERIC_GPIO primitives.
+ */
+int kw_gpio_direction_input(unsigned pin)
+{
+ if (kw_gpio_is_valid(pin, GPIO_INPUT_OK) != 0)
+ return 1;
+
+ /* Configure GPIO direction. */
+ __set_direction(pin, 1);
+
+ return 0;
+}
+
+int kw_gpio_direction_output(unsigned pin, int value)
+{
+ if (kw_gpio_is_valid(pin, GPIO_OUTPUT_OK) != 0)
+ {
+ printf("%s: invalid GPIO %d\n", __func__, pin);
+ return 1;
+ }
+
+ __set_blinking(pin, 0);
+
+ /* Configure GPIO output value. */
+ __set_level(pin, value);
+
+ /* Configure GPIO direction. */
+ __set_direction(pin, 0);
+
+ return 0;
+}
+
+int kw_gpio_get_value(unsigned pin)
+{
+ int val;
+
+ if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)))
+ val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin));
+ else
+ val = readl(GPIO_OUT(pin));
+
+ return (val >> (pin & 31)) & 1;
+}
+
+void kw_gpio_set_value(unsigned pin, int value)
+{
+ /* Configure GPIO output value. */
+ __set_level(pin, value);
+}
+
+void kw_gpio_set_blink(unsigned pin, int blink)
+{
+ /* Set output value to zero. */
+ __set_level(pin, 0);
+
+ /* Set blinking. */
+ __set_blinking(pin, blink);
+}
diff --git a/drivers/gpio/lpc32xx_gpio.c b/drivers/gpio/lpc32xx_gpio.c
new file mode 100644
index 00000000000..de66c765d11
--- /dev/null
+++ b/drivers/gpio/lpc32xx_gpio.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * LPC32xxGPIO driver
+ *
+ * (C) Copyright 2014 DENX Software Engineering GmbH
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch-lpc32xx/cpu.h>
+#include <asm/arch-lpc32xx/gpio.h>
+#include <asm-generic/gpio.h>
+#include <dm.h>
+
+/**
+ * LPC32xx GPIOs work in banks but are non-homogeneous:
+ * - each bank holds a different number of GPIOs
+ * - some GPIOs are input/ouput, some input only, some output only;
+ * - some GPIOs have different meanings as an input and as an output;
+ * - some GPIOs are controlled on a given port and bit index, but
+ * read on another one.
+*
+ * In order to keep this code simple, GPIOS are considered here as
+ * homogeneous and linear, from 0 to 159.
+ *
+ * ** WARNING #1 **
+ *
+ * Client code is responsible for properly using valid GPIO numbers,
+ * including cases where a single physical GPIO has differing numbers
+ * for setting its direction, reading it and/or writing to it.
+ *
+ * ** WARNING #2 **
+ *
+ * Please read NOTE in description of lpc32xx_gpio_get_function().
+ */
+
+#define LPC32XX_GPIOS 160
+
+struct lpc32xx_gpio_priv {
+ struct gpio_regs *regs;
+ /* GPIO FUNCTION: SEE WARNING #2 */
+ signed char function[LPC32XX_GPIOS];
+};
+
+/**
+ * We have 4 GPIO ports of 32 bits each
+ *
+ * Port mapping offset (32 bits each):
+ * - Port 0: 0
+ * - Port 1: 32
+ * - Port 2: 64
+ * - Port 3: GPO / GPIO (output): 96
+ * - Port 3: GPI: 128
+ */
+
+#define MAX_GPIO 160
+
+#define GPIO_TO_PORT(gpio) ((gpio / 32) & 7)
+#define GPIO_TO_RANK(gpio) (gpio % 32)
+#define GPIO_TO_MASK(gpio) (1 << (gpio % 32))
+
+/**
+ * Configure a GPIO number 'offset' as input
+ */
+
+static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ int port, mask;
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_regs *regs = gpio_priv->regs;
+
+ port = GPIO_TO_PORT(offset);
+ mask = GPIO_TO_MASK(offset);
+
+ switch (port) {
+ case 0:
+ writel(mask, &regs->p0_dir_clr);
+ break;
+ case 1:
+ writel(mask, &regs->p1_dir_clr);
+ break;
+ case 2:
+ /* ports 2 and 3 share a common direction */
+ writel(mask, &regs->p2_p3_dir_clr);
+ break;
+ case 3:
+ /* Setup direction only for GPIO_xx. */
+ if ((mask >= 25) && (mask <= 30))
+ writel(mask, &regs->p2_p3_dir_clr);
+ break;
+ case 4:
+ /* GPI_xx; nothing to do. */
+ break;
+ default:
+ return -1;
+ }
+
+ /* GPIO FUNCTION: SEE WARNING #2 */
+ gpio_priv->function[offset] = GPIOF_INPUT;
+
+ return 0;
+}
+
+/**
+ * Get the value of a GPIO
+ */
+
+static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ int port, rank, mask, value;
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_regs *regs = gpio_priv->regs;
+
+ port = GPIO_TO_PORT(offset);
+
+ switch (port) {
+ case 0:
+ value = readl(&regs->p0_inp_state);
+ break;
+ case 1:
+ value = readl(&regs->p1_inp_state);
+ break;
+ case 2:
+ value = readl(&regs->p2_inp_state);
+ break;
+ case 3:
+ /* Read GPO_xx and GPIO_xx (as output) using p3_outp_state. */
+ value = readl(&regs->p3_outp_state);
+ break;
+ case 4:
+ /* Read GPI_xx and GPIO_xx (as input) using p3_inp_state. */
+ value = readl(&regs->p3_inp_state);
+ break;
+ default:
+ return -1;
+ }
+
+ rank = GPIO_TO_RANK(offset);
+ mask = GPIO_TO_MASK(offset);
+
+ return (value & mask) >> rank;
+}
+
+/**
+ * Set a GPIO
+ */
+
+static int gpio_set(struct udevice *dev, unsigned gpio)
+{
+ int port, mask;
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_regs *regs = gpio_priv->regs;
+
+ port = GPIO_TO_PORT(gpio);
+ mask = GPIO_TO_MASK(gpio);
+
+ switch (port) {
+ case 0:
+ writel(mask, &regs->p0_outp_set);
+ break;
+ case 1:
+ writel(mask, &regs->p1_outp_set);
+ break;
+ case 2:
+ writel(mask, &regs->p2_outp_set);
+ break;
+ case 3:
+ writel(mask, &regs->p3_outp_set);
+ break;
+ case 4:
+ /* GPI_xx; invalid. */
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Clear a GPIO
+ */
+
+static int gpio_clr(struct udevice *dev, unsigned gpio)
+{
+ int port, mask;
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_regs *regs = gpio_priv->regs;
+
+ port = GPIO_TO_PORT(gpio);
+ mask = GPIO_TO_MASK(gpio);
+
+ switch (port) {
+ case 0:
+ writel(mask, &regs->p0_outp_clr);
+ break;
+ case 1:
+ writel(mask, &regs->p1_outp_clr);
+ break;
+ case 2:
+ writel(mask, &regs->p2_outp_clr);
+ break;
+ case 3:
+ writel(mask, &regs->p3_outp_clr);
+ break;
+ case 4:
+ /* GPI_xx; invalid. */
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Set the value of a GPIO
+ */
+
+static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ if (value)
+ return gpio_set(dev, offset);
+ else
+ return gpio_clr(dev, offset);
+}
+
+/**
+ * Configure a GPIO number 'offset' as output with given initial value.
+ */
+
+static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ int port, mask;
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_regs *regs = gpio_priv->regs;
+
+ port = GPIO_TO_PORT(offset);
+ mask = GPIO_TO_MASK(offset);
+
+ switch (port) {
+ case 0:
+ writel(mask, &regs->p0_dir_set);
+ break;
+ case 1:
+ writel(mask, &regs->p1_dir_set);
+ break;
+ case 2:
+ /* ports 2 and 3 share a common direction */
+ writel(mask, &regs->p2_p3_dir_set);
+ break;
+ case 3:
+ /* Setup direction only for GPIO_xx. */
+ if ((mask >= 25) && (mask <= 30))
+ writel(mask, &regs->p2_p3_dir_set);
+ break;
+ case 4:
+ /* GPI_xx; invalid. */
+ default:
+ return -1;
+ }
+
+ /* GPIO FUNCTION: SEE WARNING #2 */
+ gpio_priv->function[offset] = GPIOF_OUTPUT;
+
+ return lpc32xx_gpio_set_value(dev, offset, value);
+}
+
+/**
+ * GPIO functions are supposed to be computed from their current
+ * configuration, but that's way too complicated in LPC32XX. A simpler
+ * approach is used, where the GPIO functions are cached in an array.
+ * When the GPIO is in use, its function is either "input" or "output"
+ * depending on its direction, otherwise its function is "unknown".
+ *
+ * ** NOTE **
+ *
+ * THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX
+ * GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE.
+ */
+
+static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ return gpio_priv->function[offset];
+}
+
+static const struct dm_gpio_ops gpio_lpc32xx_ops = {
+ .direction_input = lpc32xx_gpio_direction_input,
+ .direction_output = lpc32xx_gpio_direction_output,
+ .get_value = lpc32xx_gpio_get_value,
+ .set_value = lpc32xx_gpio_set_value,
+ .get_function = lpc32xx_gpio_get_function,
+};
+
+static int lpc32xx_gpio_probe(struct udevice *dev)
+{
+ struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (dev_of_offset(dev) == -1) {
+ /* Tell the uclass how many GPIOs we have */
+ uc_priv->gpio_count = LPC32XX_GPIOS;
+ }
+
+ /* set base address for GPIO registers */
+ gpio_priv->regs = (struct gpio_regs *)GPIO_BASE;
+
+ /* all GPIO functions are unknown until requested */
+ /* GPIO FUNCTION: SEE WARNING #2 */
+ memset(gpio_priv->function, GPIOF_UNKNOWN, sizeof(gpio_priv->function));
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_lpc32xx) = {
+ .name = "gpio_lpc32xx",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_lpc32xx_ops,
+ .probe = lpc32xx_gpio_probe,
+ .priv_auto = sizeof(struct lpc32xx_gpio_priv),
+};
diff --git a/drivers/gpio/max7320_gpio.c b/drivers/gpio/max7320_gpio.c
new file mode 100644
index 00000000000..647aed907b4
--- /dev/null
+++ b/drivers/gpio/max7320_gpio.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * max7320 I2C GPIO EXPANDER DRIVER
+ *
+ * Copyright (C) 2021 Hannes Schmelzer <oe5hpm@oevsv.at>
+ * B&R Industrial Automation GmbH - http://www.br-automation.com
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm-generic/gpio.h>
+#include <linux/bitops.h>
+
+struct max7320_chip {
+ u32 outreg;
+};
+
+static int max7320_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct max7320_chip *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
+
+ int ret;
+
+ if (value)
+ plat->outreg |= BIT(offset);
+ else
+ plat->outreg &= ~BIT(offset);
+
+ ret = dm_i2c_write(dev,
+ plat->outreg & 0xff,
+ (uint8_t *)&plat->outreg + 1,
+ uc_priv->gpio_count > 8 ? 1 : 0);
+ if (ret)
+ printf("%s i2c write failed to addr %x\n", __func__,
+ chip->chip_addr);
+
+ return ret;
+}
+
+static int max7320_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct max7320_chip *plat = dev_get_plat(dev);
+
+ return (plat->outreg >> offset) & 0x1;
+}
+
+static int max7320_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ return max7320_direction_output(dev, offset, value);
+}
+
+static int max7320_get_function(struct udevice *dev, unsigned int offset)
+{
+ return GPIOF_OUTPUT;
+}
+
+static int max7320_ofdata_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", 8);
+ if (uc_priv->gpio_count > 16) {
+ printf("%s: max7320 doesn't support more than 16 gpios!",
+ __func__);
+ return -EINVAL;
+ }
+
+ uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "gpio-bank-name", NULL);
+ if (!uc_priv->bank_name)
+ uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
+ dev_of_offset(dev), NULL);
+
+ return 0;
+}
+
+static int max7320_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ debug("%s GPIO controller with %d gpios probed\n",
+ uc_priv->bank_name, uc_priv->gpio_count);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops max7320_gpio_ops = {
+ .direction_output = max7320_direction_output,
+ .set_value = max7320_set_value,
+ .get_value = max7320_get_value,
+ .get_function = max7320_get_function,
+};
+
+static const struct udevice_id max7320_gpio_ids[] = {
+ { .compatible = "maxim,max7320" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_max7320) = {
+ .name = "gpio_max7320",
+ .id = UCLASS_GPIO,
+ .ops = &max7320_gpio_ops,
+ .of_match = max7320_gpio_ids,
+ .of_to_plat = max7320_ofdata_plat,
+ .probe = max7320_gpio_probe,
+ .plat_auto = sizeof(struct max7320_chip),
+};
diff --git a/drivers/gpio/max77663_gpio.c b/drivers/gpio/max77663_gpio.c
new file mode 100644
index 00000000000..ecb60478088
--- /dev/null
+++ b/drivers/gpio/max77663_gpio.c
@@ -0,0 +1,178 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <asm/gpio.h>
+#include <power/max77663.h>
+#include <power/pmic.h>
+
+#define NUM_ENTRIES 11 /* 8 GPIOs + 3 KEYs */
+#define NUM_GPIOS 8
+
+#define MAX77663_CNFG1_GPIO 0x36
+#define GPIO_REG_ADDR(offset) (MAX77663_CNFG1_GPIO + (offset))
+
+#define MAX77663_CNFG_GPIO_DIR_MASK BIT(1)
+#define MAX77663_CNFG_GPIO_DIR_INPUT BIT(1)
+#define MAX77663_CNFG_GPIO_DIR_OUTPUT 0
+#define MAX77663_CNFG_GPIO_INPUT_VAL_MASK BIT(2)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK BIT(3)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH BIT(3)
+#define MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW 0
+#define MAX77663_CNFG_IRQ GENMASK(5, 4)
+
+#define MAX77663_ONOFFSTAT_REG 0x15
+#define EN0 BIT(2) /* KEY 2 */
+#define ACOK BIT(1) /* KEY 1 */
+#define LID BIT(0) /* KEY 0 */
+
+static int max77663_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ int ret;
+
+ if (offset >= NUM_GPIOS)
+ return 0;
+
+ ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+ MAX77663_CNFG_GPIO_DIR_MASK,
+ MAX77663_CNFG_GPIO_DIR_INPUT);
+ if (ret < 0)
+ log_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int max77663_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ u8 val;
+ int ret;
+
+ if (offset >= NUM_GPIOS)
+ return -EINVAL;
+
+ val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
+ MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+ ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+ MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+ if (ret < 0) {
+ log_debug("%s: CNFG_GPIOx val update failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+ MAX77663_CNFG_GPIO_DIR_MASK,
+ MAX77663_CNFG_GPIO_DIR_OUTPUT);
+ if (ret < 0)
+ log_debug("%s: CNFG_GPIOx dir update failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int max77663_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ int ret;
+
+ if (offset >= NUM_GPIOS) {
+ ret = pmic_reg_read(dev->parent, MAX77663_ONOFFSTAT_REG);
+ if (ret < 0) {
+ log_debug("%s: ONOFFSTAT_REG read failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return !!(ret & BIT(offset - NUM_GPIOS));
+ }
+
+ ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
+ if (ret < 0) {
+ log_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
+ return !!(ret & MAX77663_CNFG_GPIO_INPUT_VAL_MASK);
+ else
+ return !!(ret & MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK);
+}
+
+static int max77663_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ u8 val;
+ int ret;
+
+ if (offset >= NUM_GPIOS)
+ return -EINVAL;
+
+ val = (value) ? MAX77663_CNFG_GPIO_OUTPUT_VAL_HIGH :
+ MAX77663_CNFG_GPIO_OUTPUT_VAL_LOW;
+
+ ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(offset),
+ MAX77663_CNFG_GPIO_OUTPUT_VAL_MASK, val);
+ if (ret < 0)
+ log_debug("%s: CNFG_GPIO_OUT update failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int max77663_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ int ret;
+
+ if (offset >= NUM_GPIOS)
+ return GPIOF_INPUT;
+
+ ret = pmic_reg_read(dev->parent, GPIO_REG_ADDR(offset));
+ if (ret < 0) {
+ log_debug("%s: CNFG_GPIOx read failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (ret & MAX77663_CNFG_GPIO_DIR_MASK)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops max77663_gpio_ops = {
+ .direction_input = max77663_gpio_direction_input,
+ .direction_output = max77663_gpio_direction_output,
+ .get_value = max77663_gpio_get_value,
+ .set_value = max77663_gpio_set_value,
+ .get_function = max77663_gpio_get_function,
+};
+
+static int max77663_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int i, ret;
+
+ uc_priv->gpio_count = NUM_ENTRIES;
+ uc_priv->bank_name = "GPIO";
+
+ /*
+ * GPIO interrupts may be left ON after bootloader, hence let's
+ * pre-initialize hardware to the expected state by disabling all
+ * the interrupts.
+ */
+ for (i = 0; i < NUM_GPIOS; i++) {
+ ret = pmic_clrsetbits(dev->parent, GPIO_REG_ADDR(i),
+ MAX77663_CNFG_IRQ, 0);
+ if (ret < 0) {
+ log_debug("%s: failed to disable interrupt: %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(max77663_gpio) = {
+ .name = MAX77663_GPIO_DRIVER,
+ .id = UCLASS_GPIO,
+ .probe = max77663_gpio_probe,
+ .ops = &max77663_gpio_ops,
+};
diff --git a/drivers/gpio/mcp230xx_gpio.c b/drivers/gpio/mcp230xx_gpio.c
new file mode 100644
index 00000000000..9f02fd42b35
--- /dev/null
+++ b/drivers/gpio/mcp230xx_gpio.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021, Collabora Ltd.
+ * Copyright (C) 2021, General Electric Company
+ * Author(s): Sebastian Reichel <sebastian.reichel@collabora.com>
+ */
+
+#define LOG_CATEGORY UCLASS_GPIO
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+#include <dt-bindings/gpio/gpio.h>
+
+enum mcp230xx_type {
+ UNKNOWN = 0,
+ MCP23008,
+ MCP23017,
+ MCP23018,
+};
+
+#define MCP230XX_IODIR 0x00
+#define MCP230XX_GPPU 0x06
+#define MCP230XX_GPIO 0x09
+#define MCP230XX_OLAT 0x0a
+
+#define BANKSIZE 8
+
+static int mcp230xx_read(struct udevice *dev, uint reg, uint offset)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int bank = offset / BANKSIZE;
+ int mask = 1 << (offset % BANKSIZE);
+ int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
+ int ret;
+
+ ret = dm_i2c_reg_read(dev, (reg << shift) | bank);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & mask);
+}
+
+static int mcp230xx_write(struct udevice *dev, uint reg, uint offset, bool val)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int bank = offset / BANKSIZE;
+ int mask = 1 << (offset % BANKSIZE);
+ int shift = (uc_priv->gpio_count / BANKSIZE) - 1;
+
+ return dm_i2c_reg_clrset(dev, (reg << shift) | bank, mask, val ? mask : 0);
+}
+
+static int mcp230xx_get_value(struct udevice *dev, uint offset)
+{
+ int ret;
+
+ ret = mcp230xx_read(dev, MCP230XX_GPIO, offset);
+ if (ret < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mcp230xx_set_value(struct udevice *dev, uint offset, int val)
+{
+ int ret;
+
+ ret = mcp230xx_write(dev, MCP230XX_GPIO, offset, val);
+ if (ret < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int mcp230xx_get_flags(struct udevice *dev, unsigned int offset,
+ ulong *flags)
+{
+ int direction, pullup;
+
+ pullup = mcp230xx_read(dev, MCP230XX_GPPU, offset);
+ if (pullup < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, pullup);
+ return pullup;
+ }
+
+ direction = mcp230xx_read(dev, MCP230XX_IODIR, offset);
+ if (direction < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, direction);
+ return direction;
+ }
+
+ *flags = direction ? GPIOD_IS_IN : GPIOD_IS_OUT;
+
+ if (pullup)
+ *flags |= GPIOD_PULL_UP;
+
+ return 0;
+}
+
+static int mcp230xx_set_flags(struct udevice *dev, uint offset, ulong flags)
+{
+ bool input = !(flags & (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+ bool pullup = flags & GPIOD_PULL_UP;
+ ulong supported_mask;
+ int ret;
+
+ /* Note: active-low is ignored (handled by core) */
+ supported_mask = GPIOD_ACTIVE_LOW | GPIOD_MASK_DIR | GPIOD_PULL_UP;
+ if (flags & ~supported_mask) {
+ dev_err(dev, "%s unsupported flag(s): %lx\n", __func__, flags);
+ return -EINVAL;
+ }
+
+ ret = mcp230xx_write(dev, MCP230XX_OLAT, offset, !!(flags & GPIOD_IS_OUT_ACTIVE));
+ if (ret) {
+ dev_err(dev, "%s failed to setup output latch: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = mcp230xx_write(dev, MCP230XX_GPPU, offset, pullup);
+ if (ret) {
+ dev_err(dev, "%s failed to setup pull-up: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = mcp230xx_write(dev, MCP230XX_IODIR, offset, input);
+ if (ret) {
+ dev_err(dev, "%s failed to setup direction: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mcp230xx_direction_input(struct udevice *dev, uint offset)
+{
+ return mcp230xx_set_flags(dev, offset, GPIOD_IS_IN);
+}
+
+static int mcp230xx_direction_output(struct udevice *dev, uint offset, int val)
+{
+ int ret = mcp230xx_set_value(dev, offset, val);
+ if (ret < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, ret);
+ return ret;
+ }
+ return mcp230xx_set_flags(dev, offset, GPIOD_IS_OUT);
+}
+
+static int mcp230xx_get_function(struct udevice *dev, uint offset)
+{
+ int ret;
+
+ ret = mcp230xx_read(dev, MCP230XX_IODIR, offset);
+ if (ret < 0) {
+ dev_err(dev, "%s error: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return ret ? GPIOF_INPUT : GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops mcp230xx_ops = {
+ .direction_input = mcp230xx_direction_input,
+ .direction_output = mcp230xx_direction_output,
+ .get_value = mcp230xx_get_value,
+ .set_value = mcp230xx_set_value,
+ .get_function = mcp230xx_get_function,
+ .set_flags = mcp230xx_set_flags,
+ .get_flags = mcp230xx_get_flags,
+};
+
+static int mcp230xx_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[32], label[8], *str;
+ int addr, gpio_count, size;
+ const u8 *tmp;
+
+ switch (dev_get_driver_data(dev)) {
+ case MCP23008:
+ gpio_count = 8;
+ break;
+ case MCP23017:
+ case MCP23018:
+ gpio_count = 16;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ addr = dev_read_addr(dev);
+ tmp = dev_read_prop(dev, "label", &size);
+ if (tmp) {
+ memcpy(label, tmp, sizeof(label) - 1);
+ label[sizeof(label) - 1] = '\0';
+ snprintf(name, sizeof(name), "%s@%x_", label, addr);
+ } else {
+ snprintf(name, sizeof(name), "gpio@%x_", addr);
+ }
+
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = gpio_count;
+
+ dev_dbg(dev, "%s is ready\n", str);
+
+ return 0;
+}
+
+static const struct udevice_id mcp230xx_ids[] = {
+ { .compatible = "microchip,mcp23008", .data = MCP23008, },
+ { .compatible = "microchip,mcp23017", .data = MCP23017, },
+ { .compatible = "microchip,mcp23018", .data = MCP23018, },
+ { }
+};
+
+U_BOOT_DRIVER(mcp230xx) = {
+ .name = "mcp230xx",
+ .id = UCLASS_GPIO,
+ .ops = &mcp230xx_ops,
+ .probe = mcp230xx_probe,
+ .of_match = mcp230xx_ids,
+};
diff --git a/drivers/gpio/mpc83xx_spisel_boot.c b/drivers/gpio/mpc83xx_spisel_boot.c
new file mode 100644
index 00000000000..fd26a36a0f9
--- /dev/null
+++ b/drivers/gpio/mpc83xx_spisel_boot.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019 DEIF A/S
+ *
+ * GPIO driver to set/clear SPISEL_BOOT pin on mpc83xx.
+ */
+
+#include <common.h>
+#include <log.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <asm/gpio.h>
+
+struct mpc83xx_spisel_boot {
+ u32 __iomem *spi_cs;
+ ulong addr;
+ uint gpio_count;
+ ulong type;
+};
+
+static u32 gpio_mask(uint gpio)
+{
+ return (1U << (31 - (gpio)));
+}
+
+static int mpc83xx_spisel_boot_direction_input(struct udevice *dev, uint gpio)
+{
+ return -EINVAL;
+}
+
+static int mpc83xx_spisel_boot_set_value(struct udevice *dev, uint gpio, int value)
+{
+ struct mpc83xx_spisel_boot *data = dev_get_priv(dev);
+
+ debug("%s: gpio=%d, value=%u, gpio_mask=0x%08x\n", __func__,
+ gpio, value, gpio_mask(gpio));
+
+ if (value)
+ setbits_be32(data->spi_cs, gpio_mask(gpio));
+ else
+ clrbits_be32(data->spi_cs, gpio_mask(gpio));
+
+ return 0;
+}
+
+static int mpc83xx_spisel_boot_direction_output(struct udevice *dev, uint gpio, int value)
+{
+ return 0;
+}
+
+static int mpc83xx_spisel_boot_get_value(struct udevice *dev, uint gpio)
+{
+ struct mpc83xx_spisel_boot *data = dev_get_priv(dev);
+
+ return !!(in_be32(data->spi_cs) & gpio_mask(gpio));
+}
+
+static int mpc83xx_spisel_boot_get_function(struct udevice *dev, uint gpio)
+{
+ return GPIOF_OUTPUT;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static int mpc83xx_spisel_boot_of_to_plat(struct udevice *dev)
+{
+ struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+ u32 reg[2];
+
+ dev_read_u32_array(dev, "reg", reg, 2);
+ addr = dev_translate_address(dev, reg);
+
+ plat->addr = addr;
+ plat->size = reg[1];
+ plat->ngpios = dev_read_u32_default(dev, "ngpios", 1);
+
+ return 0;
+}
+#endif
+
+static int mpc83xx_spisel_boot_plat_to_priv(struct udevice *dev)
+{
+ struct mpc83xx_spisel_boot *priv = dev_get_priv(dev);
+ struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
+ unsigned long size = plat->size;
+ ulong driver_data = dev_get_driver_data(dev);
+
+ if (size == 0)
+ size = 0x04;
+
+ priv->addr = plat->addr;
+ priv->spi_cs = map_sysmem(plat->addr, size);
+
+ if (!priv->spi_cs)
+ return -ENOMEM;
+
+ priv->gpio_count = plat->ngpios;
+
+ priv->type = driver_data;
+
+ return 0;
+}
+
+static int mpc83xx_spisel_boot_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mpc83xx_spisel_boot *data = dev_get_priv(dev);
+ char name[32], *str;
+
+ mpc83xx_spisel_boot_plat_to_priv(dev);
+
+ snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
+ str = strdup(name);
+
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = data->gpio_count;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops mpc83xx_spisel_boot_ops = {
+ .direction_input = mpc83xx_spisel_boot_direction_input,
+ .direction_output = mpc83xx_spisel_boot_direction_output,
+ .get_value = mpc83xx_spisel_boot_get_value,
+ .set_value = mpc83xx_spisel_boot_set_value,
+ .get_function = mpc83xx_spisel_boot_get_function,
+};
+
+static const struct udevice_id mpc83xx_spisel_boot_ids[] = {
+ { .compatible = "fsl,mpc8309-spisel-boot" },
+ { .compatible = "fsl,mpc83xx-spisel-boot" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(spisel_boot_mpc83xx) = {
+ .name = "spisel_boot_mpc83xx",
+ .id = UCLASS_GPIO,
+ .ops = &mpc83xx_spisel_boot_ops,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ .of_to_plat = mpc83xx_spisel_boot_of_to_plat,
+ .plat_auto = sizeof(struct mpc8xxx_gpio_plat),
+ .of_match = mpc83xx_spisel_boot_ids,
+#endif
+ .probe = mpc83xx_spisel_boot_probe,
+ .priv_auto = sizeof(struct mpc83xx_spisel_boot),
+};
diff --git a/drivers/gpio/mpc8xx_gpio.c b/drivers/gpio/mpc8xx_gpio.c
new file mode 100644
index 00000000000..2f653465331
--- /dev/null
+++ b/drivers/gpio/mpc8xx_gpio.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020 CS Group
+ * Charles Frey <charles.frey@c-s.fr>
+ *
+ * based on driver/gpio/mpc8xxx_gpio.c, which is
+ * Copyright 2016 Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
+ * Copyright 2010 eXMeritus, A Boeing Company
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <asm/gpio.h>
+#include <malloc.h>
+
+enum {
+ MPC8XX_CPM1_PORTA,
+ MPC8XX_CPM1_PORTB,
+ MPC8XX_CPM1_PORTC,
+ MPC8XX_CPM1_PORTD,
+ MPC8XX_CPM1_PORTE,
+};
+
+/*
+ * The MPC885 CPU CPM has 5 I/O ports, and each ports has different
+ * register length : 16 bits for ports A,C,D and 32 bits for ports
+ * B and E.
+ *
+ * This structure allows us to select the accessors according to the
+ * port we are configuring.
+ */
+struct mpc8xx_gpio_data {
+ /* The bank's register base in memory */
+ void __iomem *base;
+ /* The address of the registers; used to identify the bank */
+ ulong addr;
+ /* The GPIO count of the bank */
+ uint gpio_count;
+ /* Type needed to use the correct accessors */
+ int type;
+};
+
+/* Structure for ports A, C, D */
+struct iop_16 {
+ u16 pdir;
+ u16 ppar;
+ u16 podr;
+ u16 pdat;
+};
+
+/* Port B */
+struct iop_32_b {
+ u32 pdir;
+ u32 ppar;
+ u32 podr;
+ u32 pdat;
+};
+
+/* Port E */
+struct iop_32_e {
+ u32 pdir;
+ u32 ppar;
+ u32 psor;
+ u32 podr;
+ u32 pdat;
+};
+
+union iop_32 {
+ struct iop_32_b b;
+ struct iop_32_e e;
+};
+
+inline u32 gpio_mask(uint gpio, int type)
+{
+ if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
+ return 1U << (31 - (gpio));
+ else
+ return 1U << (15 - (gpio));
+}
+
+static inline u16 gpio16_get_val(void __iomem *base, u16 mask, int type)
+{
+ struct iop_16 *regs = base;
+
+ return in_be16(&regs->pdat) & mask;
+}
+
+static inline u16 gpio16_get_dir(void __iomem *base, u16 mask, int type)
+{
+ struct iop_16 *regs = base;
+
+ return in_be16(&regs->pdir) & mask;
+}
+
+static inline void gpio16_set_in(void __iomem *base, u16 gpios, int type)
+{
+ struct iop_16 *regs = base;
+
+ clrbits_be16(&regs->pdat, gpios);
+ /* GPDIR register 0 -> input */
+ clrbits_be16(&regs->pdir, gpios);
+}
+
+static inline void gpio16_set_lo(void __iomem *base, u16 gpios, int type)
+{
+ struct iop_16 *regs = base;
+
+ clrbits_be16(&regs->pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be16(&regs->pdir, gpios);
+}
+
+static inline void gpio16_set_hi(void __iomem *base, u16 gpios, int type)
+{
+ struct iop_16 *regs = base;
+
+ setbits_be16(&regs->pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be16(&regs->pdir, gpios);
+}
+
+/* PORT B AND E */
+static inline u32 gpio32_get_val(void __iomem *base, u32 mask, int type)
+{
+ union iop_32 __iomem *regs = base;
+
+ if (type == MPC8XX_CPM1_PORTB)
+ return in_be32(&regs->b.pdat) & mask;
+ else
+ return in_be32(&regs->e.pdat) & mask;
+}
+
+static inline u32 gpio32_get_dir(void __iomem *base, u32 mask, int type)
+{
+ union iop_32 __iomem *regs = base;
+
+ if (type == MPC8XX_CPM1_PORTB)
+ return in_be32(&regs->b.pdir) & mask;
+ else
+ return in_be32(&regs->e.pdir) & mask;
+}
+
+static inline void gpio32_set_in(void __iomem *base, u32 gpios, int type)
+{
+ union iop_32 __iomem *regs = base;
+
+ if (type == MPC8XX_CPM1_PORTB) {
+ clrbits_be32(&regs->b.pdat, gpios);
+ /* GPDIR register 0 -> input */
+ clrbits_be32(&regs->b.pdir, gpios);
+ } else { /* Port E */
+ clrbits_be32(&regs->e.pdat, gpios);
+ /* GPDIR register 0 -> input */
+ clrbits_be32(&regs->e.pdir, gpios);
+ }
+}
+
+static inline void gpio32_set_lo(void __iomem *base, u32 gpios, int type)
+{
+ union iop_32 __iomem *regs = base;
+
+ if (type == MPC8XX_CPM1_PORTB) {
+ clrbits_be32(&regs->b.pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&regs->b.pdir, gpios);
+ } else {
+ clrbits_be32(&regs->e.pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&regs->e.pdir, gpios);
+ }
+}
+
+static inline void gpio32_set_hi(void __iomem *base, u32 gpios, int type)
+{
+ union iop_32 __iomem *regs = base;
+
+ if (type == MPC8XX_CPM1_PORTB) {
+ setbits_be32(&regs->b.pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&regs->b.pdir, gpios);
+ } else {
+ setbits_be32(&regs->e.pdat, gpios);
+ /* GPDIR register 1 -> output */
+ setbits_be32(&regs->e.pdir, gpios);
+ }
+}
+
+static int mpc8xx_gpio_direction_input(struct udevice *dev, uint gpio)
+{
+ struct mpc8xx_gpio_data *data = dev_get_priv(dev);
+ int type = data->type;
+
+ if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
+ gpio32_set_in(data->base, gpio_mask(gpio, type), type);
+ else
+ gpio16_set_in(data->base, gpio_mask(gpio, type), type);
+
+ return 0;
+}
+
+static int mpc8xx_gpio_set_value(struct udevice *dev, uint gpio, int value)
+{
+ struct mpc8xx_gpio_data *data = dev_get_priv(dev);
+ int type = data->type;
+
+ if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE) {
+ if (value)
+ gpio32_set_hi(data->base, gpio_mask(gpio, type), type);
+ else
+ gpio32_set_lo(data->base, gpio_mask(gpio, type), type);
+ } else {
+ if (value)
+ gpio16_set_hi(data->base, gpio_mask(gpio, type), type);
+ else
+ gpio16_set_lo(data->base, gpio_mask(gpio, type), type);
+ }
+
+ return 0;
+}
+
+static int mpc8xx_gpio_direction_output(struct udevice *dev, uint gpio,
+ int value)
+{
+ return mpc8xx_gpio_set_value(dev, gpio, value);
+}
+
+static int mpc8xx_gpio_get_value(struct udevice *dev, uint gpio)
+{
+ struct mpc8xx_gpio_data *data = dev_get_priv(dev);
+ int type = data->type;
+
+ /* Input -> read value from GPDAT register */
+ if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
+ return gpio32_get_val(data->base, gpio_mask(gpio, type), type);
+ else
+ return gpio16_get_val(data->base, gpio_mask(gpio, type), type);
+}
+
+static int mpc8xx_gpio_get_function(struct udevice *dev, uint gpio)
+{
+ struct mpc8xx_gpio_data *data = dev_get_priv(dev);
+ int type = data->type;
+ int dir;
+
+ if (type == MPC8XX_CPM1_PORTB || type == MPC8XX_CPM1_PORTE)
+ dir = gpio32_get_dir(data->base, gpio_mask(gpio, type), type);
+ else
+ dir = gpio16_get_dir(data->base, gpio_mask(gpio, type), type);
+ return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static int mpc8xx_gpio_ofdata_to_platdata(struct udevice *dev)
+{
+ struct mpc8xx_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+ u32 reg[2];
+
+ dev_read_u32_array(dev, "reg", reg, 2);
+ addr = dev_translate_address(dev, reg);
+
+ plat->addr = addr;
+ plat->size = reg[1];
+ plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
+
+ return 0;
+}
+
+static int mpc8xx_gpio_platdata_to_priv(struct udevice *dev)
+{
+ struct mpc8xx_gpio_data *priv = dev_get_priv(dev);
+ struct mpc8xx_gpio_plat *plat = dev_get_plat(dev);
+ unsigned long size = plat->size;
+ int type;
+
+ if (size == 0)
+ size = 0x100;
+
+ priv->addr = plat->addr;
+ priv->base = map_sysmem(plat->addr, size);
+
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->gpio_count = plat->ngpios;
+
+ type = dev_get_driver_data(dev);
+
+ if ((type == MPC8XX_CPM1_PORTA || type == MPC8XX_CPM1_PORTC ||
+ type == MPC8XX_CPM1_PORTD) && plat->ngpios == 32)
+ priv->gpio_count = 16;
+
+ priv->type = type;
+
+ return 0;
+}
+
+static int mpc8xx_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mpc8xx_gpio_data *data = dev_get_priv(dev);
+ char name[32], *str;
+
+ mpc8xx_gpio_platdata_to_priv(dev);
+
+ snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
+ str = strdup(name);
+
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = data->gpio_count;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_mpc8xx_ops = {
+ .direction_input = mpc8xx_gpio_direction_input,
+ .direction_output = mpc8xx_gpio_direction_output,
+ .get_value = mpc8xx_gpio_get_value,
+ .set_value = mpc8xx_gpio_set_value,
+ .get_function = mpc8xx_gpio_get_function,
+};
+
+static const struct udevice_id mpc8xx_gpio_ids[] = {
+ { .compatible = "fsl,cpm1-pario-bank-a", .data = MPC8XX_CPM1_PORTA },
+ { .compatible = "fsl,cpm1-pario-bank-b", .data = MPC8XX_CPM1_PORTB },
+ { .compatible = "fsl,cpm1-pario-bank-c", .data = MPC8XX_CPM1_PORTC },
+ { .compatible = "fsl,cpm1-pario-bank-d", .data = MPC8XX_CPM1_PORTD },
+ { .compatible = "fsl,cpm1-pario-bank-e", .data = MPC8XX_CPM1_PORTE },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_mpc8xx) = {
+ .name = "gpio_mpc8xx",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mpc8xx_ops,
+ .of_to_plat = mpc8xx_gpio_ofdata_to_platdata,
+ .plat_auto = sizeof(struct mpc8xx_gpio_plat),
+ .of_match = mpc8xx_gpio_ids,
+ .probe = mpc8xx_gpio_probe,
+ .priv_auto = sizeof(struct mpc8xx_gpio_data),
+};
diff --git a/drivers/gpio/mpc8xxx_gpio.c b/drivers/gpio/mpc8xxx_gpio.c
new file mode 100644
index 00000000000..f7ffd8926ad
--- /dev/null
+++ b/drivers/gpio/mpc8xxx_gpio.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ *
+ * based on arch/powerpc/include/asm/mpc85xx_gpio.h, which is
+ *
+ * Copyright 2010 eXMeritus, A Boeing Company
+ * Copyright 2020-2021 NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/of_access.h>
+
+struct mpc8xxx_gpio_data {
+ /* The bank's register base in memory */
+ struct ccsr_gpio __iomem *base;
+ /* The address of the registers; used to identify the bank */
+ phys_addr_t addr;
+ /* The GPIO count of the bank */
+ uint gpio_count;
+ /* The GPDAT register cannot be used to determine the value of output
+ * pins on MPC8572/MPC8536, so we shadow it and use the shadowed value
+ * for output pins
+ */
+ u32 dat_shadow;
+ ulong type;
+ bool little_endian;
+};
+
+enum {
+ MPC8XXX_GPIO_TYPE,
+ MPC5121_GPIO_TYPE,
+};
+
+inline u32 gpio_mask(uint gpio)
+{
+ return (1U << (31 - (gpio)));
+}
+
+static inline u32 mpc8xxx_gpio_get_val(struct udevice *dev, u32 mask)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ if (data->little_endian)
+ return in_le32(&data->base->gpdat) & mask;
+ else
+ return in_be32(&data->base->gpdat) & mask;
+}
+
+static inline u32 mpc8xxx_gpio_get_dir(struct udevice *dev, u32 mask)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ if (data->little_endian)
+ return in_le32(&data->base->gpdir) & mask;
+ else
+ return in_be32(&data->base->gpdir) & mask;
+}
+
+static inline int mpc8xxx_gpio_open_drain_val(struct udevice *dev, u32 mask)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ if (data->little_endian)
+ return in_le32(&data->base->gpodr) & mask;
+ else
+ return in_be32(&data->base->gpodr) & mask;
+}
+
+static inline void mpc8xxx_gpio_open_drain_on(struct udevice *dev, u32
+ gpios)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+ /* GPODR register 1 -> open drain on */
+ if (data->little_endian)
+ setbits_le32(&data->base->gpodr, gpios);
+ else
+ setbits_be32(&data->base->gpodr, gpios);
+}
+
+static inline void mpc8xxx_gpio_open_drain_off(struct udevice *dev,
+ u32 gpios)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+ /* GPODR register 0 -> open drain off (actively driven) */
+ if (data->little_endian)
+ clrbits_le32(&data->base->gpodr, gpios);
+ else
+ clrbits_be32(&data->base->gpodr, gpios);
+}
+
+static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+ u32 mask = gpio_mask(gpio);
+
+ /* GPDIR register 0 -> input */
+ if (data->little_endian)
+ clrbits_le32(&data->base->gpdir, mask);
+ else
+ clrbits_be32(&data->base->gpdir, mask);
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+ struct ccsr_gpio *base = data->base;
+ u32 mask = gpio_mask(gpio);
+ u32 gpdir;
+
+ if (value) {
+ data->dat_shadow |= mask;
+ } else {
+ data->dat_shadow &= ~mask;
+ }
+
+ if (data->little_endian)
+ gpdir = in_le32(&base->gpdir);
+ else
+ gpdir = in_be32(&base->gpdir);
+
+ gpdir |= gpio_mask(gpio);
+
+ if (data->little_endian) {
+ out_le32(&base->gpdat, gpdir & data->dat_shadow);
+ out_le32(&base->gpdir, gpdir);
+ } else {
+ out_be32(&base->gpdat, gpdir & data->dat_shadow);
+ out_be32(&base->gpdir, gpdir);
+ }
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_direction_output(struct udevice *dev, uint gpio,
+ int value)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ /* GPIO 28..31 are input only on MPC5121 */
+ if (data->type == MPC5121_GPIO_TYPE && gpio >= 28)
+ return -EINVAL;
+
+ return mpc8xxx_gpio_set_value(dev, gpio, value);
+}
+
+static int mpc8xxx_gpio_get_value(struct udevice *dev, uint gpio)
+{
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ if (!!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio))) {
+ /* Output -> use shadowed value */
+ return !!(data->dat_shadow & gpio_mask(gpio));
+ }
+
+ /* Input -> read value from GPDAT register */
+ return !!mpc8xxx_gpio_get_val(dev, gpio_mask(gpio));
+}
+
+static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio)
+{
+ int dir;
+
+ dir = !!mpc8xxx_gpio_get_dir(dev, gpio_mask(gpio));
+ return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+static int mpc8xxx_gpio_of_to_plat(struct udevice *dev)
+{
+ struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+
+ if (dev_read_bool(dev, "little-endian"))
+ data->little_endian = true;
+
+ plat->addr = dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat->size);
+ plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
+
+ return 0;
+}
+#endif
+
+static int mpc8xxx_gpio_plat_to_priv(struct udevice *dev)
+{
+ struct mpc8xxx_gpio_data *priv = dev_get_priv(dev);
+ struct mpc8xxx_gpio_plat *plat = dev_get_plat(dev);
+ unsigned long size = plat->size;
+ ulong driver_data = dev_get_driver_data(dev);
+
+ if (size == 0)
+ size = 0x100;
+
+ priv->addr = plat->addr;
+ priv->base = map_sysmem(plat->addr, size);
+
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->gpio_count = plat->ngpios;
+ priv->dat_shadow = 0;
+
+ priv->type = driver_data;
+
+ return 0;
+}
+
+static int mpc8xxx_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
+ char name[32], *str;
+
+ mpc8xxx_gpio_plat_to_priv(dev);
+
+ snprintf(name, sizeof(name), "MPC@%.8llx",
+ (unsigned long long)data->addr);
+ str = strdup(name);
+
+ if (!str)
+ return -ENOMEM;
+
+ if (device_is_compatible(dev, "fsl,qoriq-gpio")) {
+ if (data->little_endian)
+ out_le32(&data->base->gpibe, 0xffffffff);
+ else
+ out_be32(&data->base->gpibe, 0xffffffff);
+ }
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = data->gpio_count;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_mpc8xxx_ops = {
+ .direction_input = mpc8xxx_gpio_direction_input,
+ .direction_output = mpc8xxx_gpio_direction_output,
+ .get_value = mpc8xxx_gpio_get_value,
+ .set_value = mpc8xxx_gpio_set_value,
+ .get_function = mpc8xxx_gpio_get_function,
+};
+
+static const struct udevice_id mpc8xxx_gpio_ids[] = {
+ { .compatible = "fsl,pq3-gpio", .data = MPC8XXX_GPIO_TYPE },
+ { .compatible = "fsl,mpc8308-gpio", .data = MPC8XXX_GPIO_TYPE },
+ { .compatible = "fsl,mpc8349-gpio", .data = MPC8XXX_GPIO_TYPE },
+ { .compatible = "fsl,mpc8572-gpio", .data = MPC8XXX_GPIO_TYPE},
+ { .compatible = "fsl,mpc8610-gpio", .data = MPC8XXX_GPIO_TYPE},
+ { .compatible = "fsl,mpc5121-gpio", .data = MPC5121_GPIO_TYPE, },
+ { .compatible = "fsl,qoriq-gpio", .data = MPC8XXX_GPIO_TYPE },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_mpc8xxx) = {
+ .name = "gpio_mpc8xxx",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mpc8xxx_ops,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+ .of_to_plat = mpc8xxx_gpio_of_to_plat,
+ .plat_auto = sizeof(struct mpc8xxx_gpio_plat),
+ .of_match = mpc8xxx_gpio_ids,
+#endif
+ .probe = mpc8xxx_gpio_probe,
+ .priv_auto = sizeof(struct mpc8xxx_gpio_data),
+};
diff --git a/drivers/gpio/mscc_sgpio.c b/drivers/gpio/mscc_sgpio.c
new file mode 100644
index 00000000000..c97e44005ee
--- /dev/null
+++ b/drivers/gpio/mscc_sgpio.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi SoCs serial gpio driver
+ *
+ * Author: <lars.povlsen@microchip.com>
+ *
+ * Copyright (c) 2018 Microsemi Corporation
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <clk.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+
+#define MSCC_SGPIOS_PER_BANK 32
+#define MSCC_SGPIO_BANK_DEPTH 4
+
+enum {
+ REG_INPUT_DATA,
+ REG_PORT_CONFIG,
+ REG_PORT_ENABLE,
+ REG_SIO_CONFIG,
+ REG_SIO_CLOCK,
+ MAXREG
+};
+
+struct mscc_sgpio_bf {
+ u8 beg;
+ u8 end;
+};
+
+struct mscc_sgpio_props {
+ u8 regoff[MAXREG];
+ struct mscc_sgpio_bf auto_repeat;
+ struct mscc_sgpio_bf port_width;
+ struct mscc_sgpio_bf clk_freq;
+ struct mscc_sgpio_bf bit_source;
+};
+
+#define __M(bf) GENMASK((bf).end, (bf).beg)
+#define __F(bf, x) (__M(bf) & ((x) << (bf).beg))
+#define __X(bf, x) (((x) >> (bf).beg) & GENMASK(((bf).end - (bf).beg), 0))
+
+#define MSCC_M_CFG_SIO_AUTO_REPEAT(p) BIT(p->props->auto_repeat.beg)
+#define MSCC_F_CFG_SIO_PORT_WIDTH(p, x) __F(p->props->port_width, x)
+#define MSCC_M_CFG_SIO_PORT_WIDTH(p) __M(p->props->port_width)
+#define MSCC_F_CLOCK_SIO_CLK_FREQ(p, x) __F(p->props->clk_freq, x)
+#define MSCC_M_CLOCK_SIO_CLK_FREQ(p) __M(p->props->clk_freq)
+#define MSCC_F_PORT_CFG_BIT_SOURCE(p, x) __F(p->props->bit_source, x)
+#define MSCC_X_PORT_CFG_BIT_SOURCE(p, x) __X(p->props->bit_source, x)
+
+const struct mscc_sgpio_props props_luton = {
+ .regoff = { 0x00, 0x09, 0x29, 0x2a, 0x2b },
+ .auto_repeat = { 5, 5 },
+ .port_width = { 2, 3 },
+ .clk_freq = { 0, 11 },
+ .bit_source = { 0, 11 },
+};
+
+const struct mscc_sgpio_props props_ocelot = {
+ .regoff = { 0x00, 0x06, 0x26, 0x04, 0x05 },
+ .auto_repeat = { 10, 10 },
+ .port_width = { 7, 8 },
+ .clk_freq = { 8, 19 },
+ .bit_source = { 12, 23 },
+};
+
+struct mscc_sgpio_priv {
+ u32 bitcount;
+ u32 ports;
+ u32 clock;
+ u32 mode[MSCC_SGPIOS_PER_BANK];
+ u32 __iomem *regs;
+ const struct mscc_sgpio_props *props;
+};
+
+static inline u32 sgpio_readl(struct mscc_sgpio_priv *priv, u32 rno, u32 off)
+{
+ u32 __iomem *reg = &priv->regs[priv->props->regoff[rno] + off];
+
+ return readl(reg);
+}
+
+static inline void sgpio_writel(struct mscc_sgpio_priv *priv,
+ u32 val, u32 rno, u32 off)
+{
+ u32 __iomem *reg = &priv->regs[priv->props->regoff[rno] + off];
+
+ writel(val, reg);
+}
+
+static void sgpio_clrsetbits(struct mscc_sgpio_priv *priv,
+ u32 rno, u32 off, u32 clear, u32 set)
+{
+ u32 __iomem *reg = &priv->regs[priv->props->regoff[rno] + off];
+
+ clrsetbits_le32(reg, clear, set);
+}
+
+static int mscc_sgpio_direction_input(struct udevice *dev, unsigned int gpio)
+{
+ struct mscc_sgpio_priv *priv = dev_get_priv(dev);
+
+ u32 port = gpio % MSCC_SGPIOS_PER_BANK;
+ u32 bit = gpio / MSCC_SGPIOS_PER_BANK;
+
+ priv->mode[port] |= BIT(bit);
+
+ return 0;
+}
+
+static int mscc_sgpio_direction_output(struct udevice *dev,
+ unsigned int gpio, int value)
+{
+ struct mscc_sgpio_priv *priv = dev_get_priv(dev);
+ u32 port = gpio % MSCC_SGPIOS_PER_BANK;
+ u32 bit = gpio / MSCC_SGPIOS_PER_BANK;
+ u32 mask = 3 << (3 * bit);
+
+ debug("set: port %d, bit %d, mask 0x%08x, value %d\n",
+ port, bit, mask, value);
+
+ value = (value & 3) << (3 * bit);
+ sgpio_clrsetbits(priv, REG_PORT_CONFIG, port,
+ MSCC_F_PORT_CFG_BIT_SOURCE(priv, mask),
+ MSCC_F_PORT_CFG_BIT_SOURCE(priv, value));
+ clrbits_le32(&priv->mode[port], BIT(bit));
+
+ return 0;
+}
+
+static int mscc_sgpio_get_function(struct udevice *dev, unsigned int gpio)
+{
+ struct mscc_sgpio_priv *priv = dev_get_priv(dev);
+ u32 port = gpio % MSCC_SGPIOS_PER_BANK;
+ u32 bit = gpio / MSCC_SGPIOS_PER_BANK;
+ u32 val = priv->mode[port] & BIT(bit);
+
+ if (val)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static int mscc_sgpio_set_value(struct udevice *dev,
+ unsigned int gpio, int value)
+{
+ return mscc_sgpio_direction_output(dev, gpio, value);
+}
+
+static int mscc_sgpio_get_value(struct udevice *dev, unsigned int gpio)
+{
+ struct mscc_sgpio_priv *priv = dev_get_priv(dev);
+ u32 port = gpio % MSCC_SGPIOS_PER_BANK;
+ u32 bit = gpio / MSCC_SGPIOS_PER_BANK;
+ int ret;
+
+ if (mscc_sgpio_get_function(dev, gpio) == GPIOF_INPUT) {
+ ret = !!(sgpio_readl(priv, REG_INPUT_DATA, bit) & BIT(port));
+ } else {
+ u32 portval = sgpio_readl(priv, REG_PORT_CONFIG, port);
+
+ ret = MSCC_X_PORT_CFG_BIT_SOURCE(priv, portval);
+ ret = !!(ret & (3 << (3 * bit)));
+ }
+
+ debug("get: gpio %d, port %d, bit %d, value %d\n",
+ gpio, port, bit, ret);
+ return ret;
+}
+
+static int mscc_sgpio_get_count(struct udevice *dev)
+{
+ struct ofnode_phandle_args args;
+ int count = 0, i = 0, ret;
+
+ ret = dev_read_phandle_with_args(dev, "gpio-ranges", NULL, 3, i, &args);
+ while (ret != -ENOENT) {
+ count += args.args[2];
+ ret = dev_read_phandle_with_args(dev, "gpio-ranges", NULL, 3,
+ ++i, &args);
+ }
+ return count;
+}
+
+static int mscc_sgpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mscc_sgpio_priv *priv = dev_get_priv(dev);
+ int err, div_clock = 0, port;
+ u32 val;
+ struct clk clk;
+
+ err = clk_get_by_index(dev, 0, &clk);
+ if (!err) {
+ err = clk_get_rate(&clk);
+ if (IS_ERR_VALUE(err)) {
+ dev_err(dev, "Invalid clk rate\n");
+ return -EINVAL;
+ }
+ div_clock = err;
+ } else {
+ dev_err(dev, "Failed to get clock\n");
+ return err;
+ }
+
+ priv->props = (const struct mscc_sgpio_props *)dev_get_driver_data(dev);
+ priv->ports = dev_read_u32_default(dev, "mscc,sgpio-ports", 0xFFFFFFFF);
+ priv->clock = dev_read_u32_default(dev, "mscc,sgpio-frequency",
+ 12500000);
+ if (priv->clock <= 0 || priv->clock > div_clock) {
+ dev_err(dev, "Invalid frequency %d\n", priv->clock);
+ return -EINVAL;
+ }
+
+ uc_priv->gpio_count = mscc_sgpio_get_count(dev);
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios",
+ uc_priv->gpio_count);
+ if (uc_priv->gpio_count < 1 || uc_priv->gpio_count >
+ (4 * MSCC_SGPIOS_PER_BANK)) {
+ dev_err(dev, "Invalid gpio count %d\n", uc_priv->gpio_count);
+ return -EINVAL;
+ }
+ priv->bitcount = DIV_ROUND_UP(uc_priv->gpio_count,
+ MSCC_SGPIOS_PER_BANK);
+ debug("probe: gpios = %d, bit-count = %d\n",
+ uc_priv->gpio_count, priv->bitcount);
+
+ priv->regs = dev_read_addr_ptr(dev);
+ uc_priv->bank_name = "sgpio";
+
+ sgpio_clrsetbits(priv, REG_SIO_CONFIG, 0,
+ MSCC_M_CFG_SIO_PORT_WIDTH(priv),
+ MSCC_F_CFG_SIO_PORT_WIDTH(priv, priv->bitcount - 1) |
+ MSCC_M_CFG_SIO_AUTO_REPEAT(priv));
+ val = div_clock / priv->clock;
+ debug("probe: div-clock = %d KHz, freq = %d KHz, div = %d\n",
+ div_clock / 1000, priv->clock / 1000, val);
+ sgpio_clrsetbits(priv, REG_SIO_CLOCK, 0,
+ MSCC_M_CLOCK_SIO_CLK_FREQ(priv),
+ MSCC_F_CLOCK_SIO_CLK_FREQ(priv, val));
+
+ for (port = 0; port < 32; port++)
+ sgpio_writel(priv, 0, REG_PORT_CONFIG, port);
+ sgpio_writel(priv, priv->ports, REG_PORT_ENABLE, 0);
+
+ debug("probe: sgpio regs = %p\n", priv->regs);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops mscc_sgpio_ops = {
+ .direction_input = mscc_sgpio_direction_input,
+ .direction_output = mscc_sgpio_direction_output,
+ .get_function = mscc_sgpio_get_function,
+ .get_value = mscc_sgpio_get_value,
+ .set_value = mscc_sgpio_set_value,
+};
+
+static const struct udevice_id mscc_sgpio_ids[] = {
+ { .compatible = "mscc,luton-sgpio", .data = (ulong)&props_luton },
+ { .compatible = "mscc,ocelot-sgpio", .data = (ulong)&props_ocelot },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_mscc_sgpio) = {
+ .name = "mscc-sgpio",
+ .id = UCLASS_GPIO,
+ .of_match = mscc_sgpio_ids,
+ .ops = &mscc_sgpio_ops,
+ .probe = mscc_sgpio_probe,
+ .priv_auto = sizeof(struct mscc_sgpio_priv),
+};
diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c
new file mode 100644
index 00000000000..80cd28bb231
--- /dev/null
+++ b/drivers/gpio/msm_gpio.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm GPIO driver
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <mach/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* OE */
+#define GPIO_OE_DISABLE (0x0 << 9)
+#define GPIO_OE_ENABLE (0x1 << 9)
+#define GPIO_OE_MASK (0x1 << 9)
+
+/* GPIO_IN_OUT register shifts. */
+#define GPIO_IN 0
+#define GPIO_OUT 1
+
+struct msm_gpio_bank {
+ phys_addr_t base;
+ const struct msm_pin_data *pin_data;
+};
+
+#define GPIO_CONFIG_REG(dev, x) \
+ (qcom_pin_offset(((struct msm_gpio_bank *)dev_get_priv(dev))->pin_data->pin_offsets, x))
+
+#define GPIO_IN_OUT_REG(dev, x) \
+ (GPIO_CONFIG_REG(dev, x) + 0x4)
+
+static int msm_gpio_direction_input(struct udevice *dev, unsigned int gpio)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ /* Disable OE bit */
+ clrsetbits_le32(priv->base + GPIO_CONFIG_REG(dev, gpio),
+ GPIO_OE_MASK, GPIO_OE_DISABLE);
+
+ return 0;
+}
+
+static int msm_gpio_set_value(struct udevice *dev, unsigned int gpio, int value)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ value = !!value;
+ /* set value */
+ writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio));
+
+ return 0;
+}
+
+static int msm_gpio_direction_output(struct udevice *dev, unsigned int gpio,
+ int value)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ value = !!value;
+ /* set value */
+ writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_REG(dev, gpio));
+ /* switch direction */
+ clrsetbits_le32(priv->base + GPIO_CONFIG_REG(dev, gpio),
+ GPIO_OE_MASK, GPIO_OE_ENABLE);
+
+ return 0;
+}
+
+static int msm_gpio_get_value(struct udevice *dev, unsigned int gpio)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ return !!(readl(priv->base + GPIO_IN_OUT_REG(dev, gpio)) >> GPIO_IN);
+}
+
+static int msm_gpio_get_function(struct udevice *dev, unsigned int gpio)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ if (readl(priv->base + GPIO_CONFIG_REG(dev, gpio)) & GPIO_OE_ENABLE)
+ return GPIOF_OUTPUT;
+
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_msm_ops = {
+ .direction_input = msm_gpio_direction_input,
+ .direction_output = msm_gpio_direction_output,
+ .get_value = msm_gpio_get_value,
+ .set_value = msm_gpio_set_value,
+ .get_function = msm_gpio_get_function,
+};
+
+static int msm_gpio_probe(struct udevice *dev)
+{
+ struct msm_gpio_bank *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr(dev);
+ priv->pin_data = (struct msm_pin_data *)dev_get_driver_data(dev);
+
+ return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0;
+}
+
+static int msm_gpio_of_to_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const struct msm_pin_data *pin_data = (struct msm_pin_data *)dev_get_driver_data(dev);
+
+ /* Get the pin count from the pinctrl driver */
+ uc_priv->gpio_count = pin_data->pin_count;
+ uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "gpio-bank-name", NULL);
+ if (uc_priv->bank_name == NULL)
+ uc_priv->bank_name = "soc";
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_msm) = {
+ .name = "gpio_msm",
+ .id = UCLASS_GPIO,
+ .of_to_plat = msm_gpio_of_to_plat,
+ .probe = msm_gpio_probe,
+ .ops = &gpio_msm_ops,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .priv_auto = sizeof(struct msm_gpio_bank),
+};
diff --git a/drivers/gpio/mt7620_gpio.c b/drivers/gpio/mt7620_gpio.c
new file mode 100644
index 00000000000..713fa2ed78b
--- /dev/null
+++ b/drivers/gpio/mt7620_gpio.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ *
+ * GPIO controller driver for MediaTek MT7620 SoC
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <asm/gpio.h>
+
+enum mt7620_regs {
+ GPIO_REG_DATA,
+ GPIO_REG_DIR,
+ GPIO_REG_SET,
+ GPIO_REG_CLR,
+
+ __GPIO_REG_MAX
+};
+
+struct mt7620_gpio_priv {
+ void __iomem *base;
+ u32 regs[__GPIO_REG_MAX];
+ u32 count;
+};
+
+static int mt7620_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(readl(priv->base + priv->regs[GPIO_REG_DATA]) & BIT(offset));
+}
+
+static int mt7620_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+ u32 reg;
+
+ reg = value ? priv->regs[GPIO_REG_SET] : priv->regs[GPIO_REG_CLR];
+
+ writel(BIT(offset), priv->base + reg);
+
+ return 0;
+}
+
+static int mt7620_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+
+ clrbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));
+
+ return 0;
+}
+
+static int mt7620_gpio_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+
+ /* Set value first */
+ mt7620_gpio_set_value(dev, offset, value);
+
+ setbits_32(priv->base + priv->regs[GPIO_REG_DIR], BIT(offset));
+
+ return 0;
+}
+
+static int mt7620_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+
+ return (readl(priv->base + priv->regs[GPIO_REG_DIR]) & BIT(offset)) ?
+ GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops mt7620_gpio_ops = {
+ .direction_input = mt7620_gpio_direction_input,
+ .direction_output = mt7620_gpio_direction_output,
+ .get_value = mt7620_gpio_get_value,
+ .set_value = mt7620_gpio_set_value,
+ .get_function = mt7620_gpio_get_function,
+};
+
+static int mt7620_gpio_probe(struct udevice *dev)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const char *name;
+
+ name = dev_read_string(dev, "mediatek,bank-name");
+ if (!name)
+ name = dev->name;
+
+ uc_priv->gpio_count = priv->count;
+ uc_priv->bank_name = name;
+
+ return 0;
+}
+
+static int mt7620_gpio_of_to_plat(struct udevice *dev)
+{
+ struct mt7620_gpio_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->base = dev_remap_addr_index(dev, 0);
+ if (!priv->base) {
+ dev_err(dev, "mt7620_gpio: unable to map registers\n");
+ return -EINVAL;
+ }
+
+ ret = dev_read_u32(dev, "mediatek,gpio-num", &priv->count);
+ if (ret) {
+ dev_err(dev, "mt7620_gpio: failed to get GPIO count\n");
+ return -EINVAL;
+ }
+
+ ret = dev_read_u32_array(dev, "mediatek,register-map", priv->regs,
+ __GPIO_REG_MAX);
+ if (ret) {
+ dev_err(dev, "mt7620_gpio: unable to get register map\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id mt7620_gpio_ids[] = {
+ { .compatible = "mediatek,mt7620-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(mt7620_gpio) = {
+ .name = "mt7620_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &mt7620_gpio_ops,
+ .of_match = mt7620_gpio_ids,
+ .probe = mt7620_gpio_probe,
+ .of_to_plat = mt7620_gpio_of_to_plat,
+ .priv_auto = sizeof(struct mt7620_gpio_priv),
+};
diff --git a/drivers/gpio/mt7621_gpio.c b/drivers/gpio/mt7621_gpio.c
new file mode 100644
index 00000000000..43bb4df4da7
--- /dev/null
+++ b/drivers/gpio/mt7621_gpio.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Stefan Roese <sr@denx.de>
+ *
+ * Based on the Linux driver version which is:
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#define MTK_MAX_BANK 3
+#define MTK_BANK_WIDTH 32
+
+enum mediatek_gpio_reg {
+ GPIO_REG_CTRL = 0,
+ GPIO_REG_POL,
+ GPIO_REG_DATA,
+ GPIO_REG_DSET,
+ GPIO_REG_DCLR,
+ GPIO_REG_REDGE,
+ GPIO_REG_FEDGE,
+ GPIO_REG_HLVL,
+ GPIO_REG_LLVL,
+ GPIO_REG_STAT,
+ GPIO_REG_EDGE,
+};
+
+static void __iomem *mediatek_gpio_membase;
+
+struct mediatek_gpio_plat {
+ char bank_name[3]; /* Name of bank, e.g. "PA", "PB" etc */
+ int gpio_count;
+ int bank;
+};
+
+static u32 reg_offs(struct mediatek_gpio_plat *plat, int reg)
+{
+ return (reg * 0x10) + (plat->bank * 0x4);
+}
+
+static int mediatek_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+
+ return !!(ioread32(mediatek_gpio_membase +
+ reg_offs(plat, GPIO_REG_DATA)) & BIT(offset));
+}
+
+static int mediatek_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+
+ iowrite32(BIT(offset), mediatek_gpio_membase +
+ reg_offs(plat, value ? GPIO_REG_DSET : GPIO_REG_DCLR));
+
+ return 0;
+}
+
+static int mediatek_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+
+ clrbits_le32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL),
+ BIT(offset));
+
+ return 0;
+}
+
+static int mediatek_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+
+ setbits_le32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL),
+ BIT(offset));
+ mediatek_gpio_set_value(dev, offset, value);
+
+ return 0;
+}
+
+static int mediatek_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+ u32 t;
+
+ t = ioread32(mediatek_gpio_membase + reg_offs(plat, GPIO_REG_CTRL));
+ if (t & BIT(offset))
+ return GPIOF_OUTPUT;
+
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_mediatek_ops = {
+ .direction_input = mediatek_gpio_direction_input,
+ .direction_output = mediatek_gpio_direction_output,
+ .get_value = mediatek_gpio_get_value,
+ .set_value = mediatek_gpio_set_value,
+ .get_function = mediatek_gpio_get_function,
+};
+
+static int gpio_mediatek_probe(struct udevice *dev)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Tell the uclass how many GPIOs we have */
+ if (plat) {
+ uc_priv->gpio_count = plat->gpio_count;
+ uc_priv->bank_name = plat->bank_name;
+ }
+
+ return 0;
+}
+
+/**
+ * We have a top-level GPIO device with no actual GPIOs. It has a child
+ * device for each Mediatek bank.
+ */
+static int gpio_mediatek_bind(struct udevice *parent)
+{
+ struct mediatek_gpio_plat *plat = dev_get_plat(parent);
+ ofnode node;
+ int bank = 0;
+ int ret;
+
+ /* If this is a child device, there is nothing to do here */
+ if (plat)
+ return 0;
+
+ mediatek_gpio_membase = dev_remap_addr(parent);
+ if (!mediatek_gpio_membase)
+ return -EINVAL;
+
+ for (node = dev_read_first_subnode(parent); ofnode_valid(node);
+ node = dev_read_next_subnode(node)) {
+ struct mediatek_gpio_plat *plat;
+ struct udevice *dev;
+
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+ plat->bank_name[0] = 'P';
+ plat->bank_name[1] = 'A' + bank;
+ plat->bank_name[2] = '\0';
+ plat->gpio_count = MTK_BANK_WIDTH;
+ plat->bank = bank;
+
+ ret = device_bind(parent, parent->driver, plat->bank_name, plat,
+ node, &dev);
+ if (ret)
+ return ret;
+
+ bank++;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id mediatek_gpio_ids[] = {
+ { .compatible = "mtk,mt7621-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_mediatek) = {
+ .name = "gpio_mediatek",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mediatek_ops,
+ .of_match = mediatek_gpio_ids,
+ .bind = gpio_mediatek_bind,
+ .probe = gpio_mediatek_probe,
+};
diff --git a/drivers/gpio/mvebu_gpio.c b/drivers/gpio/mvebu_gpio.c
new file mode 100644
index 00000000000..f706a6dfa4f
--- /dev/null
+++ b/drivers/gpio/mvebu_gpio.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2016 Stefan Roese <sr@denx.de>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <linux/bitops.h>
+
+#define MVEBU_GPIOS_PER_BANK 32
+
+struct mvebu_gpio_regs {
+ u32 data_out;
+ u32 io_conf;
+ u32 blink_en;
+ u32 in_pol;
+ u32 data_in;
+};
+
+struct mvebu_gpio_priv {
+ struct mvebu_gpio_regs *regs;
+ char name[sizeof("mvebuX_")];
+};
+
+static int mvebu_gpio_direction_input(struct udevice *dev, unsigned int gpio)
+{
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+ struct mvebu_gpio_regs *regs = priv->regs;
+
+ setbits_le32(&regs->io_conf, BIT(gpio));
+
+ return 0;
+}
+
+static int mvebu_gpio_direction_output(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+ struct mvebu_gpio_regs *regs = priv->regs;
+
+ if (value)
+ setbits_le32(&regs->data_out, BIT(gpio));
+ else
+ clrbits_le32(&regs->data_out, BIT(gpio));
+ clrbits_le32(&regs->io_conf, BIT(gpio));
+
+ return 0;
+}
+
+static int mvebu_gpio_get_function(struct udevice *dev, unsigned gpio)
+{
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+ struct mvebu_gpio_regs *regs = priv->regs;
+ u32 val;
+
+ val = readl(&regs->io_conf) & BIT(gpio);
+ if (val)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_OUTPUT;
+}
+
+static int mvebu_gpio_set_value(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+ struct mvebu_gpio_regs *regs = priv->regs;
+
+ if (value)
+ setbits_le32(&regs->data_out, BIT(gpio));
+ else
+ clrbits_le32(&regs->data_out, BIT(gpio));
+
+ return 0;
+}
+
+static int mvebu_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+ struct mvebu_gpio_regs *regs = priv->regs;
+
+ return !!(readl(&regs->data_in) & BIT(gpio));
+}
+
+static int mvebu_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct mvebu_gpio_priv *priv = dev_get_priv(dev);
+
+ priv->regs = dev_read_addr_ptr(dev);
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MVEBU_GPIOS_PER_BANK);
+ sprintf(priv->name, "mvebu%d_", dev_seq(dev));
+ uc_priv->bank_name = priv->name;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops mvebu_gpio_ops = {
+#if CONFIG_IS_ENABLED(PINCTRL_ARMADA_38X)
+ .request = pinctrl_gpio_request,
+ .rfree = pinctrl_gpio_free,
+#endif
+ .direction_input = mvebu_gpio_direction_input,
+ .direction_output = mvebu_gpio_direction_output,
+ .get_function = mvebu_gpio_get_function,
+ .get_value = mvebu_gpio_get_value,
+ .set_value = mvebu_gpio_set_value,
+};
+
+static const struct udevice_id mvebu_gpio_ids[] = {
+ { .compatible = "marvell,orion-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_mvebu) = {
+ .name = "gpio_mvebu",
+ .id = UCLASS_GPIO,
+ .of_match = mvebu_gpio_ids,
+ .ops = &mvebu_gpio_ops,
+ .probe = mvebu_gpio_probe,
+ .priv_auto = sizeof(struct mvebu_gpio_priv),
+};
diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c
new file mode 100644
index 00000000000..1dec4e35e0a
--- /dev/null
+++ b/drivers/gpio/mxc_gpio.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * Copyright (C) 2011
+ * Stefano Babic, DENX Software Engineering, <sbabic@denx.de>
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <malloc.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dt-structs.h>
+#include <mapmem.h>
+
+enum mxc_gpio_direction {
+ MXC_GPIO_DIRECTION_IN,
+ MXC_GPIO_DIRECTION_OUT,
+};
+
+#define GPIO_PER_BANK 32
+
+struct mxc_gpio_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ /* Put this first since driver model will copy the data here */
+ struct dtd_gpio_mxc dtplat;
+#endif
+ int bank_index;
+ struct gpio_regs *regs;
+};
+
+struct mxc_bank_info {
+ struct gpio_regs *regs;
+};
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+#define GPIO_TO_PORT(n) ((n) / 32)
+
+/* GPIO port description */
+static unsigned long gpio_ports[] = {
+ [0] = GPIO1_BASE_ADDR,
+ [1] = GPIO2_BASE_ADDR,
+ [2] = GPIO3_BASE_ADDR,
+#if defined(CONFIG_MX51) || \
+ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || \
+ defined(CONFIG_ARCH_IMX8) || defined(CONFIG_IMXRT1050)
+ [3] = GPIO4_BASE_ADDR,
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || \
+ defined(CONFIG_ARCH_IMX8) || defined(CONFIG_IMXRT1050)
+ [4] = GPIO5_BASE_ADDR,
+#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || \
+ defined(CONFIG_IMX8M) || defined(CONFIG_IMXRT1050))
+ [5] = GPIO6_BASE_ADDR,
+#endif
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
+ defined(CONFIG_ARCH_IMX8)
+#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
+ [6] = GPIO7_BASE_ADDR,
+#endif
+#endif
+#if defined(CONFIG_ARCH_IMX8)
+ [7] = GPIO8_BASE_ADDR,
+#endif
+};
+
+static int mxc_gpio_direction(unsigned int gpio,
+ enum mxc_gpio_direction direction)
+{
+ unsigned int port = GPIO_TO_PORT(gpio);
+ struct gpio_regs *regs;
+ u32 l;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return -1;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ l = readl(&regs->gpio_dir);
+
+ switch (direction) {
+ case MXC_GPIO_DIRECTION_OUT:
+ l |= 1 << gpio;
+ break;
+ case MXC_GPIO_DIRECTION_IN:
+ l &= ~(1 << gpio);
+ }
+ writel(l, &regs->gpio_dir);
+
+ return 0;
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+ unsigned int port = GPIO_TO_PORT(gpio);
+ struct gpio_regs *regs;
+ u32 l;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return -1;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ l = readl(&regs->gpio_dr);
+ if (value)
+ l |= 1 << gpio;
+ else
+ l &= ~(1 << gpio);
+ writel(l, &regs->gpio_dr);
+
+ return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+ unsigned int port = GPIO_TO_PORT(gpio);
+ struct gpio_regs *regs;
+ u32 val;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return -1;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
+
+ return val;
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ unsigned int port = GPIO_TO_PORT(gpio);
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return -1;
+ return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ int ret = gpio_set_value(gpio, value);
+
+ if (ret < 0)
+ return ret;
+
+ return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
+}
+#endif
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+#include <fdtdec.h>
+static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
+{
+ u32 val;
+
+ val = readl(&regs->gpio_dir);
+
+ return val & (1 << offset) ? 1 : 0;
+}
+
+static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
+ enum mxc_gpio_direction direction)
+{
+ u32 l;
+
+ l = readl(&regs->gpio_dir);
+
+ switch (direction) {
+ case MXC_GPIO_DIRECTION_OUT:
+ l |= 1 << offset;
+ break;
+ case MXC_GPIO_DIRECTION_IN:
+ l &= ~(1 << offset);
+ }
+ writel(l, &regs->gpio_dir);
+}
+
+static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
+ int value)
+{
+ u32 l;
+
+ l = readl(&regs->gpio_dr);
+ if (value)
+ l |= 1 << offset;
+ else
+ l &= ~(1 << offset);
+ writel(l, &regs->gpio_dr);
+}
+
+static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
+{
+ return (readl(&regs->gpio_psr) >> offset) & 0x01;
+}
+
+/* set GPIO pin 'gpio' as an input */
+static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+
+ /* Configure GPIO direction as input. */
+ mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+
+ /* Configure GPIO output value. */
+ mxc_gpio_bank_set_value(bank->regs, offset, value);
+
+ /* Configure GPIO direction as output. */
+ mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);
+
+ return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+
+ return mxc_gpio_bank_get_value(bank->regs, offset);
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+
+ mxc_gpio_bank_set_value(bank->regs, offset, value);
+
+ return 0;
+}
+
+static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+
+ /* GPIOF_FUNC is not implemented yet */
+ if (mxc_gpio_is_output(bank->regs, offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_mxc_ops = {
+ .direction_input = mxc_gpio_direction_input,
+ .direction_output = mxc_gpio_direction_output,
+ .get_value = mxc_gpio_get_value,
+ .set_value = mxc_gpio_set_value,
+ .get_function = mxc_gpio_get_function,
+};
+
+static int mxc_gpio_probe(struct udevice *dev)
+{
+ struct mxc_bank_info *bank = dev_get_priv(dev);
+ struct mxc_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int banknum;
+ char name[18], *str;
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dtd_gpio_mxc *dtplat = &plat->dtplat;
+
+ plat->regs = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
+#endif
+
+ banknum = plat->bank_index;
+ if (IS_ENABLED(CONFIG_ARCH_IMX8))
+ sprintf(name, "GPIO%d_", banknum);
+ else
+ sprintf(name, "GPIO%d_", banknum + 1);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ bank->regs = plat->regs;
+
+ return 0;
+}
+
+static int mxc_gpio_of_to_plat(struct udevice *dev)
+{
+ struct mxc_gpio_plat *plat = dev_get_plat(dev);
+ if (!CONFIG_IS_ENABLED(OF_PLATDATA)) {
+ fdt_addr_t addr;
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->regs = (struct gpio_regs *)addr;
+ }
+ plat->bank_index = dev_seq(dev);
+
+ return 0;
+}
+
+static int mxc_gpio_bind(struct udevice *dev)
+{
+ return 0;
+}
+
+static const struct udevice_id mxc_gpio_ids[] = {
+ { .compatible = "fsl,imx35-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_mxc) = {
+ .name = "gpio_mxc",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mxc_ops,
+ .probe = mxc_gpio_probe,
+ .of_to_plat = mxc_gpio_of_to_plat,
+ .plat_auto = sizeof(struct mxc_gpio_plat),
+ .priv_auto = sizeof(struct mxc_bank_info),
+ .of_match = mxc_gpio_ids,
+ .bind = mxc_gpio_bind,
+};
+
+DM_DRIVER_ALIAS(gpio_mxc, fsl_imx6q_gpio)
+
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+static const struct mxc_gpio_plat mxc_plat[] = {
+ { 0, (struct gpio_regs *)GPIO1_BASE_ADDR },
+ { 1, (struct gpio_regs *)GPIO2_BASE_ADDR },
+ { 2, (struct gpio_regs *)GPIO3_BASE_ADDR },
+#if defined(CONFIG_MX51) || \
+ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
+ { 3, (struct gpio_regs *)GPIO4_BASE_ADDR },
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
+ { 4, (struct gpio_regs *)GPIO5_BASE_ADDR },
+#ifndef CONFIG_IMX8M
+ { 5, (struct gpio_regs *)GPIO6_BASE_ADDR },
+#endif
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
+ { 6, (struct gpio_regs *)GPIO7_BASE_ADDR },
+#endif
+#if defined(CONFIG_ARCH_IMX8)
+ { 7, (struct gpio_regs *)GPIO8_BASE_ADDR },
+#endif
+};
+
+U_BOOT_DRVINFOS(mxc_gpios) = {
+ { "gpio_mxc", &mxc_plat[0] },
+ { "gpio_mxc", &mxc_plat[1] },
+ { "gpio_mxc", &mxc_plat[2] },
+#if defined(CONFIG_MX51) || \
+ defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
+ { "gpio_mxc", &mxc_plat[3] },
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
+ defined(CONFIG_IMX8M) || defined(CONFIG_ARCH_IMX8)
+ { "gpio_mxc", &mxc_plat[4] },
+#ifndef CONFIG_IMX8M
+ { "gpio_mxc", &mxc_plat[5] },
+#endif
+#endif
+#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
+ { "gpio_mxc", &mxc_plat[6] },
+#endif
+#if defined(CONFIG_ARCH_IMX8)
+ { "gpio_mxc", &mxc_plat[7] },
+#endif
+};
+#endif
+#endif
diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c
new file mode 100644
index 00000000000..1356f89ac2f
--- /dev/null
+++ b/drivers/gpio/mxs_gpio.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Freescale i.MX28 GPIO control code
+ *
+ * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
+ * on behalf of DENX Software Engineering GmbH
+ */
+
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/arch/iomux.h>
+#include <asm/arch/imx-regs.h>
+
+#if defined(CONFIG_MX23)
+#define PINCTRL_BANKS 3
+#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10))
+#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10))
+#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10))
+#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10))
+#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10))
+#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10))
+#elif defined(CONFIG_MX28)
+#define PINCTRL_BANKS 5
+#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10))
+#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10))
+#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10))
+#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10))
+#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10))
+#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10))
+#else
+#error "Please select CONFIG_MX23 or CONFIG_MX28"
+#endif
+
+#define GPIO_INT_FALL_EDGE 0x0
+#define GPIO_INT_LOW_LEV 0x1
+#define GPIO_INT_RISE_EDGE 0x2
+#define GPIO_INT_HIGH_LEV 0x3
+#define GPIO_INT_LEV_MASK (1 << 0)
+#define GPIO_INT_POL_MASK (1 << 1)
+
+void mxs_gpio_init(void)
+{
+ int i;
+
+ for (i = 0; i < PINCTRL_BANKS; i++) {
+ writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i));
+ writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i));
+ /* Use SCT address here to clear the IRQSTAT bits */
+ writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8);
+ }
+}
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+int gpio_get_value(unsigned gpio)
+{
+ uint32_t bank = PAD_BANK(gpio);
+ uint32_t offset = PINCTRL_DIN(bank);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
+
+ return (readl(&reg->reg) >> PAD_PIN(gpio)) & 1;
+}
+
+void gpio_set_value(unsigned gpio, int value)
+{
+ uint32_t bank = PAD_BANK(gpio);
+ uint32_t offset = PINCTRL_DOUT(bank);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
+
+ if (value)
+ writel(1 << PAD_PIN(gpio), &reg->reg_set);
+ else
+ writel(1 << PAD_PIN(gpio), &reg->reg_clr);
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ uint32_t bank = PAD_BANK(gpio);
+ uint32_t offset = PINCTRL_DOE(bank);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
+
+ writel(1 << PAD_PIN(gpio), &reg->reg_clr);
+
+ return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ uint32_t bank = PAD_BANK(gpio);
+ uint32_t offset = PINCTRL_DOE(bank);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
+
+ gpio_set_value(gpio, value);
+
+ writel(1 << PAD_PIN(gpio), &reg->reg_set);
+
+ return 0;
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ if (PAD_BANK(gpio) >= PINCTRL_BANKS)
+ return -1;
+
+ return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+int name_to_gpio(const char *name)
+{
+ unsigned bank, pin;
+ char *end;
+
+ bank = dectoul(name, &end);
+
+ if (!*end || *end != ':')
+ return bank;
+
+ pin = dectoul(end + 1, NULL);
+
+ return (bank << MXS_PAD_BANK_SHIFT) | (pin << MXS_PAD_PIN_SHIFT);
+}
+#else /* DM_GPIO */
+#include <dm.h>
+#include <asm/gpio.h>
+#include <dt-structs.h>
+#include <asm/arch/gpio.h>
+#define MXS_MAX_GPIO_PER_BANK 32
+
+DECLARE_GLOBAL_DATA_PTR;
+/*
+ * According to i.MX28 Reference Manual:
+ * 'i.MX28 Applications Processor Reference Manual, Rev. 1, 2010'
+ * The i.MX28 has following number of GPIOs available:
+ * Bank 0: 0-28 -> 29 PINS
+ * Bank 1: 0-31 -> 32 PINS
+ * Bank 2: 0-27 -> 28 PINS
+ * Bank 3: 0-30 -> 31 PINS
+ * Bank 4: 0-20 -> 21 PINS
+ */
+
+struct mxs_gpio_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dtd_fsl_imx23_gpio dtplat;
+#endif
+ unsigned int bank;
+ int gpio_ranges;
+};
+
+struct mxs_gpio_priv {
+ unsigned int bank;
+};
+
+static int mxs_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DIN(priv->bank));
+
+ return (readl(&reg->reg) >> offset) & 1;
+}
+
+static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOUT(priv->bank));
+ if (value)
+ writel(BIT(offset), &reg->reg_set);
+ else
+ writel(BIT(offset), &reg->reg_clr);
+
+ return 0;
+}
+
+static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+
+ writel(BIT(offset), &reg->reg_clr);
+
+ return 0;
+}
+
+static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+
+ mxs_gpio_set_value(dev, offset, value);
+
+ writel(BIT(offset), &reg->reg_set);
+
+ return 0;
+}
+
+static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct mxs_register_32 *reg =
+ (struct mxs_register_32 *)(MXS_PINCTRL_BASE +
+ PINCTRL_DOE(priv->bank));
+ bool is_output = !!(readl(&reg->reg) >> offset);
+
+ return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_mxs_ops = {
+ .direction_input = mxs_gpio_direction_input,
+ .direction_output = mxs_gpio_direction_output,
+ .get_value = mxs_gpio_get_value,
+ .set_value = mxs_gpio_set_value,
+ .get_function = mxs_gpio_get_function,
+};
+
+static int mxs_gpio_probe(struct udevice *dev)
+{
+ struct mxs_gpio_plat *plat = dev_get_plat(dev);
+ struct mxs_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[16], *str;
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+ struct dtd_fsl_imx23_gpio *dtplat = &plat->dtplat;
+ priv->bank = (unsigned int)dtplat->reg[0];
+ uc_priv->gpio_count = dtplat->gpio_ranges[3];
+#else
+ priv->bank = (unsigned int)plat->bank;
+ uc_priv->gpio_count = plat->gpio_ranges;
+#endif
+ snprintf(name, sizeof(name), "GPIO%d_", priv->bank);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+
+ debug("%s: %s: %d pins base: 0x%x\n", __func__, uc_priv->bank_name,
+ uc_priv->gpio_count, priv->bank);
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static int mxs_of_to_plat(struct udevice *dev)
+{
+ struct mxs_gpio_plat *plat = dev_get_plat(dev);
+ struct fdtdec_phandle_args args;
+ int node = dev_of_offset(dev);
+ int ret;
+
+ plat->bank = dev_read_addr(dev);
+ if (plat->bank == FDT_ADDR_T_NONE) {
+ printf("%s: No 'reg' property defined!\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, node, "gpio-ranges",
+ NULL, 3, 0, &args);
+ if (ret)
+ printf("%s: 'gpio-ranges' not defined - using default!\n",
+ __func__);
+
+ plat->gpio_ranges = ret == 0 ? args.args[2] : MXS_MAX_GPIO_PER_BANK;
+
+ return 0;
+}
+
+static const struct udevice_id mxs_gpio_ids[] = {
+ { .compatible = "fsl,imx23-gpio" },
+ { .compatible = "fsl,imx28-gpio" },
+ { }
+};
+#endif
+
+U_BOOT_DRIVER(fsl_imx23_gpio) = {
+ .name = "fsl_imx23_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_mxs_ops,
+ .probe = mxs_gpio_probe,
+ .priv_auto = sizeof(struct mxs_gpio_priv),
+ .plat_auto = sizeof(struct mxs_gpio_plat),
+#if CONFIG_IS_ENABLED(OF_REAL)
+ .of_match = mxs_gpio_ids,
+ .of_to_plat = mxs_of_to_plat,
+#endif
+};
+
+DM_DRIVER_ALIAS(fsl_imx23_gpio, fsl_imx28_gpio)
+#endif /* DM_GPIO */
diff --git a/drivers/gpio/nmk_gpio.c b/drivers/gpio/nmk_gpio.c
new file mode 100644
index 00000000000..e1bb41b196c
--- /dev/null
+++ b/drivers/gpio/nmk_gpio.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2019 Stephan Gerhold */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+struct nmk_gpio_regs {
+ u32 dat; /* data */
+ u32 dats; /* data set */
+ u32 datc; /* data clear */
+ u32 pdis; /* pull disable */
+ u32 dir; /* direction */
+ u32 dirs; /* direction set */
+ u32 dirc; /* direction clear */
+ u32 slpm; /* sleep mode */
+ u32 afsla; /* alternate function select A */
+ u32 afslb; /* alternate function select B */
+ u32 lowemi; /* low EMI mode */
+};
+
+struct nmk_gpio {
+ struct nmk_gpio_regs *regs;
+};
+
+static int nmk_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+
+ return !!(readl(&priv->regs->dat) & BIT(offset));
+}
+
+static int nmk_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+
+ if (value)
+ writel(BIT(offset), &priv->regs->dats);
+ else
+ writel(BIT(offset), &priv->regs->datc);
+
+ return 0;
+}
+
+static int nmk_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+
+ if (readl(&priv->regs->afsla) & BIT(offset) ||
+ readl(&priv->regs->afslb) & BIT(offset))
+ return GPIOF_FUNC;
+
+ if (readl(&priv->regs->dir) & BIT(offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int nmk_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+
+ writel(BIT(offset), &priv->regs->dirc);
+ return 0;
+}
+
+static int nmk_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+
+ writel(BIT(offset), &priv->regs->dirs);
+ return nmk_gpio_set_value(dev, offset, value);
+}
+
+static const struct dm_gpio_ops nmk_gpio_ops = {
+ .get_value = nmk_gpio_get_value,
+ .set_value = nmk_gpio_set_value,
+ .get_function = nmk_gpio_get_function,
+ .direction_input = nmk_gpio_direction_input,
+ .direction_output = nmk_gpio_direction_output,
+};
+
+static int nmk_gpio_probe(struct udevice *dev)
+{
+ struct nmk_gpio *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char buf[20];
+ u32 bank;
+ int ret;
+
+ priv->regs = dev_read_addr_ptr(dev);
+ if (!priv->regs)
+ return -EINVAL;
+
+ ret = dev_read_u32(dev, "gpio-bank", &bank);
+ if (ret < 0) {
+ printf("nmk_gpio(%s): Failed to read gpio-bank\n", dev->name);
+ return ret;
+ }
+
+ sprintf(buf, "nmk%u-gpio", bank);
+ uc_priv->bank_name = strdup(buf);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+
+ uc_priv->gpio_count = sizeof(priv->regs->dat) * BITS_PER_BYTE;
+
+ return 0;
+}
+
+static const struct udevice_id nmk_gpio_ids[] = {
+ { .compatible = "st,nomadik-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_nmk) = {
+ .name = "nmk_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = nmk_gpio_ids,
+ .probe = nmk_gpio_probe,
+ .ops = &nmk_gpio_ops,
+ .priv_auto = sizeof(struct nmk_gpio),
+};
diff --git a/drivers/gpio/npcm_gpio.c b/drivers/gpio/npcm_gpio.c
new file mode 100644
index 00000000000..98e5dc79c1c
--- /dev/null
+++ b/drivers/gpio/npcm_gpio.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2022 Nuvoton Technology Corp.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <linux/io.h>
+
+#define NPCM_GPIOS_PER_BANK 32
+
+/* Register offsets */
+#define GPIO_DIN 0x4 /* RO - Data In */
+#define GPIO_DOUT 0xC /* RW - Data Out */
+#define GPIO_OE 0x10 /* RW - Output Enable */
+#define GPIO_IEM 0x58 /* RW - Input Enable Mask */
+#define GPIO_OES 0x70 /* WO - Output Enable Register Set */
+#define GPIO_OEC 0x74 /* WO - Output Enable Register Clear */
+
+struct npcm_gpio_priv {
+ void __iomem *base;
+};
+
+static int npcm_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+
+ writel(BIT(offset), priv->base + GPIO_OEC);
+ setbits_le32(priv->base + GPIO_IEM, BIT(offset));
+
+ return 0;
+}
+
+static int npcm_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+
+ if (value)
+ setbits_le32(priv->base + GPIO_DOUT, BIT(offset));
+ else
+ clrbits_le32(priv->base + GPIO_DOUT, BIT(offset));
+
+ clrbits_le32(priv->base + GPIO_IEM, BIT(offset));
+ writel(BIT(offset), priv->base + GPIO_OES);
+
+ return 0;
+}
+
+static int npcm_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+
+ if (readl(priv->base + GPIO_IEM) & BIT(offset))
+ return !!(readl(priv->base + GPIO_DIN) & BIT(offset));
+
+ if (readl(priv->base + GPIO_OE) & BIT(offset))
+ return !!(readl(priv->base + GPIO_DOUT) & BIT(offset));
+
+ return -EINVAL;
+}
+
+static int npcm_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+
+ if (value)
+ setbits_le32(priv->base + GPIO_DOUT, BIT(offset));
+ else
+ clrbits_le32(priv->base + GPIO_DOUT, BIT(offset));
+
+ return 0;
+}
+
+static int npcm_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+
+ if (readl(priv->base + GPIO_IEM) & BIT(offset))
+ return GPIOF_INPUT;
+
+ if (readl(priv->base + GPIO_OE) & BIT(offset))
+ return GPIOF_OUTPUT;
+
+ return GPIOF_FUNC;
+}
+
+static const struct dm_gpio_ops npcm_gpio_ops = {
+ .direction_input = npcm_gpio_direction_input,
+ .direction_output = npcm_gpio_direction_output,
+ .get_value = npcm_gpio_get_value,
+ .set_value = npcm_gpio_set_value,
+ .get_function = npcm_gpio_get_function,
+};
+
+static int npcm_gpio_probe(struct udevice *dev)
+{
+ struct npcm_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ priv->base = dev_read_addr_ptr(dev);
+ uc_priv->gpio_count = NPCM_GPIOS_PER_BANK;
+ uc_priv->bank_name = dev->name;
+
+ return 0;
+}
+
+static const struct udevice_id npcm_gpio_match[] = {
+ { .compatible = "nuvoton,npcm845-gpio" },
+ { .compatible = "nuvoton,npcm750-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(npcm_gpio) = {
+ .name = "npcm_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = npcm_gpio_match,
+ .probe = npcm_gpio_probe,
+ .priv_auto = sizeof(struct npcm_gpio_priv),
+ .ops = &npcm_gpio_ops,
+};
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c
new file mode 100644
index 00000000000..e2565d70953
--- /dev/null
+++ b/drivers/gpio/nx_gpio.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016 Nexell
+ * DeokJin, Lee <truevirtue@nexell.co.kr>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct nx_gpio_regs {
+ u32 data; /* Data register */
+ u32 outputenb; /* Output Enable register */
+ u32 detmode[2]; /* Detect Mode Register */
+ u32 intenb; /* Interrupt Enable Register */
+ u32 det; /* Event Detect Register */
+ u32 pad; /* Pad Status Register */
+};
+
+struct nx_alive_gpio_regs {
+ u32 pwrgate; /* Power Gating Register */
+ u32 reserved0[28]; /* Reserved0 */
+ u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */
+ u32 outputenb; /* Alive GPIO Output Enable Register */
+ u32 outputenb_read; /* Alive GPIO Output Read Register */
+ u32 reserved1[3]; /* Reserved1 */
+ u32 pad_reset; /* Alive GPIO Output Reset Register */
+ u32 data; /* Alive GPIO Output Register */
+ u32 pad_read; /* Alive GPIO Pad Read Register */
+ u32 reserved2[33]; /* Reserved2 */
+ u32 pad; /* Alive GPIO Input Value Register */
+};
+
+struct nx_gpio_plat {
+ void *regs;
+ int gpio_count;
+ const char *bank_name;
+};
+
+static int nx_alive_gpio_is_check(struct udevice *dev)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ const char *bank_name = plat->bank_name;
+
+ if (!strcmp(bank_name, "gpio_alv"))
+ return 1;
+
+ return 0;
+}
+
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_alive_gpio_regs *const regs = plat->regs;
+
+ setbits_le32(&regs->outputenb_reset, 1 << pin);
+
+ return 0;
+}
+
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int pin,
+ int val)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_alive_gpio_regs *const regs = plat->regs;
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ setbits_le32(&regs->pad_reset, 1 << pin);
+
+ setbits_le32(&regs->outputenb, 1 << pin);
+
+ return 0;
+}
+
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_alive_gpio_regs *const regs = plat->regs;
+ unsigned int mask = 1UL << pin;
+ unsigned int value;
+
+ value = (readl(&regs->pad_read) & mask) >> pin;
+
+ return value;
+}
+
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin,
+ int val)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_alive_gpio_regs *const regs = plat->regs;
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ clrbits_le32(&regs->pad_reset, 1 << pin);
+
+ return 0;
+}
+
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_alive_gpio_regs *const regs = plat->regs;
+ unsigned int mask = (1UL << pin);
+ unsigned int output;
+
+ output = readl(&regs->outputenb_read) & mask;
+
+ if (output)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_gpio_regs *const regs = plat->regs;
+
+ if (nx_alive_gpio_is_check(dev))
+ return nx_alive_gpio_direction_input(dev, pin);
+
+ clrbits_le32(&regs->outputenb, 1 << pin);
+
+ return 0;
+}
+
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin,
+ int val)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_gpio_regs *const regs = plat->regs;
+
+ if (nx_alive_gpio_is_check(dev))
+ return nx_alive_gpio_direction_output(dev, pin, val);
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ clrbits_le32(&regs->data, 1 << pin);
+
+ setbits_le32(&regs->outputenb, 1 << pin);
+
+ return 0;
+}
+
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_gpio_regs *const regs = plat->regs;
+ unsigned int mask = 1UL << pin;
+ unsigned int value;
+
+ if (nx_alive_gpio_is_check(dev))
+ return nx_alive_gpio_get_value(dev, pin);
+
+ value = (readl(&regs->pad) & mask) >> pin;
+
+ return value;
+}
+
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_gpio_regs *const regs = plat->regs;
+
+ if (nx_alive_gpio_is_check(dev))
+ return nx_alive_gpio_set_value(dev, pin, val);
+
+ if (val)
+ setbits_le32(&regs->data, 1 << pin);
+ else
+ clrbits_le32(&regs->data, 1 << pin);
+
+ return 0;
+}
+
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin)
+{
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+ struct nx_gpio_regs *const regs = plat->regs;
+ unsigned int mask = (1UL << pin);
+ unsigned int output;
+
+ if (nx_alive_gpio_is_check(dev))
+ return nx_alive_gpio_get_function(dev, pin);
+
+ output = readl(&regs->outputenb) & mask;
+
+ if (output)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int nx_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct nx_gpio_plat *plat = dev_get_plat(dev);
+
+ uc_priv->gpio_count = plat->gpio_count;
+ uc_priv->bank_name = plat->bank_name;
+
+ return 0;
+}
+
+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->gpio_count = dev_read_s32_default(dev, "nexell,gpio-bank-width",
+ 32);
+ plat->bank_name = dev_read_string(dev, "gpio-bank-name");
+
+ return 0;
+}
+
+static const struct dm_gpio_ops nx_gpio_ops = {
+ .direction_input = nx_gpio_direction_input,
+ .direction_output = nx_gpio_direction_output,
+ .get_value = nx_gpio_get_value,
+ .set_value = nx_gpio_set_value,
+ .get_function = nx_gpio_get_function,
+};
+
+static const struct udevice_id nx_gpio_ids[] = {
+ { .compatible = "nexell,nexell-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(nx_gpio) = {
+ .name = "nx_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = nx_gpio_ids,
+ .ops = &nx_gpio_ops,
+ .of_to_plat = nx_gpio_of_to_plat,
+ .plat_auto = sizeof(struct nx_gpio_plat),
+ .probe = nx_gpio_probe,
+};
diff --git a/drivers/gpio/octeon_gpio.c b/drivers/gpio/octeon_gpio.c
new file mode 100644
index 00000000000..2b2465b1bcd
--- /dev/null
+++ b/drivers/gpio/octeon_gpio.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * (C) Copyright 2011
+ * eInfochips Ltd. <www.einfochips.com>
+ * Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
+ *
+ * (C) Copyright 2010
+ * Marvell Semiconductor <www.marvell.com>
+ */
+
+#include <dm.h>
+#include <pci.h>
+#include <pci_ids.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitfield.h>
+#include <linux/compat.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/* Returns the bit value to write or read based on the offset */
+#define GPIO_BIT(x) BIT_ULL((x) & 0x3f)
+
+#define GPIO_RX_DAT 0x00
+#define GPIO_TX_SET 0x08
+#define GPIO_TX_CLR 0x10
+#define GPIO_CONST 0x90 /* OcteonTX only */
+
+/* Offset to register-set for 2nd GPIOs (> 63), OcteonTX only */
+#define GPIO1_OFFSET 0x1400
+
+/* GPIO_CONST register bits */
+#define GPIO_CONST_GPIOS_MASK GENMASK_ULL(7, 0)
+
+/* GPIO_BIT_CFG register bits */
+#define GPIO_BIT_CFG_TX_OE BIT_ULL(0)
+#define GPIO_BIT_CFG_PIN_XOR BIT_ULL(1)
+#define GPIO_BIT_CFG_INT_EN BIT_ULL(2)
+#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK_ULL(26, 16)
+
+enum {
+ PROBE_PCI = 0, /* PCI based probing */
+ PROBE_DT, /* DT based probing */
+};
+
+struct octeon_gpio_data {
+ int probe;
+ u32 reg_offs;
+ u32 gpio_bit_cfg_offs;
+};
+
+struct octeon_gpio {
+ void __iomem *base;
+ const struct octeon_gpio_data *data;
+};
+
+/* Returns the offset to the output register based on the offset and value */
+static u32 gpio_tx_reg(int offset, int value)
+{
+ u32 ret;
+
+ ret = value ? GPIO_TX_SET : GPIO_TX_CLR;
+ if (offset > 63)
+ ret += GPIO1_OFFSET;
+
+ return ret;
+}
+
+/* Returns the offset to the input data register based on the offset */
+static u32 gpio_rx_dat_reg(int offset)
+{
+ u32 ret;
+
+ ret = GPIO_RX_DAT;
+ if (offset > 63)
+ ret += GPIO1_OFFSET;
+
+ return ret;
+}
+
+static int octeon_gpio_dir_input(struct udevice *dev, unsigned int offset)
+{
+ struct octeon_gpio *gpio = dev_get_priv(dev);
+
+ debug("%s(%s, %u)\n", __func__, dev->name, offset);
+ clrbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
+ GPIO_BIT_CFG_TX_OE | GPIO_BIT_CFG_PIN_XOR |
+ GPIO_BIT_CFG_INT_EN | GPIO_BIT_CFG_PIN_SEL_MASK);
+
+ return 0;
+}
+
+static int octeon_gpio_dir_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct octeon_gpio *gpio = dev_get_priv(dev);
+
+ debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
+ writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
+ gpio_tx_reg(offset, value));
+
+ clrsetbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
+ GPIO_BIT_CFG_PIN_SEL_MASK | GPIO_BIT_CFG_INT_EN,
+ GPIO_BIT_CFG_TX_OE);
+
+ return 0;
+}
+
+static int octeon_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct octeon_gpio *gpio = dev_get_priv(dev);
+ u64 reg = readq(gpio->base + gpio->data->reg_offs +
+ gpio_rx_dat_reg(offset));
+
+ debug("%s(%s, %u): value: %d\n", __func__, dev->name, offset,
+ !!(reg & GPIO_BIT(offset)));
+
+ return !!(reg & GPIO_BIT(offset));
+}
+
+static int octeon_gpio_set_value(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct octeon_gpio *gpio = dev_get_priv(dev);
+
+ debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
+ writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
+ gpio_tx_reg(offset, value));
+
+ return 0;
+}
+
+static int octeon_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct octeon_gpio *gpio = dev_get_priv(dev);
+ u64 val = readq(gpio->base + gpio->data->gpio_bit_cfg_offs +
+ 8 * offset);
+ int pin_sel;
+
+ debug("%s(%s, %u): GPIO_BIT_CFG: 0x%llx\n", __func__, dev->name,
+ offset, val);
+ pin_sel = FIELD_GET(GPIO_BIT_CFG_PIN_SEL_MASK, val);
+ if (pin_sel)
+ return GPIOF_FUNC;
+ else if (val & GPIO_BIT_CFG_TX_OE)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int octeon_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ if (args->args_count < 1)
+ return -EINVAL;
+
+ desc->offset = args->args[0];
+ desc->flags = 0;
+ if (args->args_count > 1) {
+ if (args->args[1] & GPIO_ACTIVE_LOW)
+ desc->flags |= GPIOD_ACTIVE_LOW;
+ /* In the future add tri-state flag support */
+ }
+ return 0;
+}
+
+static const struct dm_gpio_ops octeon_gpio_ops = {
+ .direction_input = octeon_gpio_dir_input,
+ .direction_output = octeon_gpio_dir_output,
+ .get_value = octeon_gpio_get_value,
+ .set_value = octeon_gpio_set_value,
+ .get_function = octeon_gpio_get_function,
+ .xlate = octeon_gpio_xlate,
+};
+
+static int octeon_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct octeon_gpio *priv = dev_get_priv(dev);
+ char *end;
+
+ priv->data = (const struct octeon_gpio_data *)dev_get_driver_data(dev);
+
+ if (priv->data->probe == PROBE_PCI) {
+ priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE,
+ PCI_REGION_MEM);
+ uc_priv->gpio_count = readq(priv->base +
+ priv->data->reg_offs + GPIO_CONST) &
+ GPIO_CONST_GPIOS_MASK;
+ } else {
+ priv->base = dev_remap_addr(dev);
+ uc_priv->gpio_count = ofnode_read_u32_default(dev_ofnode(dev),
+ "nr-gpios", 32);
+ }
+
+ if (!priv->base) {
+ debug("%s(%s): Could not get base address\n",
+ __func__, dev->name);
+ return -ENODEV;
+ }
+
+ uc_priv->bank_name = strdup(dev->name);
+ end = strchr(uc_priv->bank_name, '@');
+ end[0] = 'A' + dev_seq(dev);
+ end[1] = '\0';
+
+ debug("%s(%s): base address: %p, pin count: %d\n",
+ __func__, dev->name, priv->base, uc_priv->gpio_count);
+
+ return 0;
+}
+
+static const struct octeon_gpio_data gpio_octeon_data = {
+ .probe = PROBE_DT,
+ .reg_offs = 0x80,
+ .gpio_bit_cfg_offs = 0x100,
+};
+
+static const struct octeon_gpio_data gpio_octeontx_data = {
+ .probe = PROBE_PCI,
+ .reg_offs = 0x00,
+ .gpio_bit_cfg_offs = 0x400,
+};
+
+static const struct udevice_id octeon_gpio_ids[] = {
+ { .compatible = "cavium,thunder-8890-gpio",
+ .data = (ulong)&gpio_octeontx_data },
+ { .compatible = "cavium,octeon-7890-gpio",
+ .data = (ulong)&gpio_octeon_data },
+ { }
+};
+
+U_BOOT_DRIVER(octeon_gpio) = {
+ .name = "octeon_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = of_match_ptr(octeon_gpio_ids),
+ .probe = octeon_gpio_probe,
+ .priv_auto = sizeof(struct octeon_gpio),
+ .ops = &octeon_gpio_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c
new file mode 100644
index 00000000000..50c4f75ddf5
--- /dev/null
+++ b/drivers/gpio/omap_gpio.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2009 Wind River Systems, Inc.
+ * Tom Rix <Tom.Rix@windriver.com>
+ *
+ * This work is derived from the linux 2.6.27 kernel source
+ * To fetch, use the kernel repository
+ * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+ * Use the v2.6.27 tag.
+ *
+ * Below is the original's header including its copyright
+ *
+ * linux/arch/arm/plat-omap/gpio.c
+ *
+ * Support functions for OMAP GPIO
+ *
+ * Copyright (C) 2003-2005 Nokia Corporation
+ * Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ */
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <linux/errno.h>
+#include <malloc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define OMAP_GPIO_DIR_OUT 0
+#define OMAP_GPIO_DIR_IN 1
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+
+#define GPIO_PER_BANK 32
+
+struct gpio_bank {
+ /* TODO(sjg@chromium.org): Can we use a struct here? */
+ void *base; /* address of registers in physical memory */
+};
+
+#endif
+
+int gpio_is_valid(int gpio)
+{
+ return (gpio >= 0) && (gpio < OMAP_MAX_GPIO);
+}
+
+static void _set_gpio_direction(const struct gpio_bank *bank, int gpio,
+ int is_input)
+{
+ void *reg = bank->base;
+ u32 l;
+
+ reg += OMAP_GPIO_OE;
+
+ l = __raw_readl(reg);
+ if (is_input)
+ l |= 1 << gpio;
+ else
+ l &= ~(1 << gpio);
+ __raw_writel(l, reg);
+}
+
+/**
+ * Get the direction of the GPIO by reading the GPIO_OE register
+ * corresponding to the specified bank.
+ */
+static int _get_gpio_direction(const struct gpio_bank *bank, int gpio)
+{
+ void *reg = bank->base;
+ u32 v;
+
+ reg += OMAP_GPIO_OE;
+
+ v = __raw_readl(reg);
+
+ if (v & (1 << gpio))
+ return OMAP_GPIO_DIR_IN;
+ else
+ return OMAP_GPIO_DIR_OUT;
+}
+
+static void _set_gpio_dataout(const struct gpio_bank *bank, int gpio,
+ int enable)
+{
+ void *reg = bank->base;
+ u32 l = 0;
+
+ if (enable)
+ reg += OMAP_GPIO_SETDATAOUT;
+ else
+ reg += OMAP_GPIO_CLEARDATAOUT;
+
+ l = 1 << gpio;
+ __raw_writel(l, reg);
+}
+
+static int _get_gpio_value(const struct gpio_bank *bank, int gpio)
+{
+ void *reg = bank->base;
+ int input;
+
+ input = _get_gpio_direction(bank, gpio);
+ switch (input) {
+ case OMAP_GPIO_DIR_IN:
+ reg += OMAP_GPIO_DATAIN;
+ break;
+ case OMAP_GPIO_DIR_OUT:
+ reg += OMAP_GPIO_DATAOUT;
+ break;
+ default:
+ return -1;
+ }
+
+ return (__raw_readl(reg) & (1 << gpio)) != 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+static inline int get_gpio_index(int gpio)
+{
+ return gpio & 0x1f;
+}
+
+static inline const struct gpio_bank *get_gpio_bank(int gpio)
+{
+ return &omap_gpio_bank[gpio >> 5];
+}
+
+static int check_gpio(int gpio)
+{
+ if (!gpio_is_valid(gpio)) {
+ printf("ERROR : check_gpio: invalid GPIO %d\n", gpio);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * Set value of the specified gpio
+ */
+int gpio_set_value(unsigned gpio, int value)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+ bank = get_gpio_bank(gpio);
+ _set_gpio_dataout(bank, get_gpio_index(gpio), value);
+
+ return 0;
+}
+
+/**
+ * Get value of the specified gpio
+ */
+int gpio_get_value(unsigned gpio)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+ bank = get_gpio_bank(gpio);
+
+ return _get_gpio_value(bank, get_gpio_index(gpio));
+}
+
+/**
+ * Set gpio direction as input
+ */
+int gpio_direction_input(unsigned gpio)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ bank = get_gpio_bank(gpio);
+ _set_gpio_direction(bank, get_gpio_index(gpio), 1);
+
+ return 0;
+}
+
+/**
+ * Set gpio direction as output
+ */
+int gpio_direction_output(unsigned gpio, int value)
+{
+ const struct gpio_bank *bank;
+
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ bank = get_gpio_bank(gpio);
+ _set_gpio_dataout(bank, get_gpio_index(gpio), value);
+ _set_gpio_direction(bank, get_gpio_index(gpio), 0);
+
+ return 0;
+}
+
+/**
+ * Request a gpio before using it.
+ *
+ * NOTE: Argument 'label' is unused.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+ if (check_gpio(gpio) < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Reset and free the gpio after using it.
+ */
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+#else /* new driver model interface CONFIG_DM_GPIO */
+
+/* set GPIO pin 'gpio' as an input */
+static int omap_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ /* Configure GPIO direction as input. */
+ _set_gpio_direction(bank, offset, 1);
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int omap_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ _set_gpio_dataout(bank, offset, value);
+ _set_gpio_direction(bank, offset, 0);
+
+ return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int omap_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ return _get_gpio_value(bank, offset);
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int omap_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ _set_gpio_dataout(bank, offset, value);
+
+ return 0;
+}
+
+static int omap_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+
+ /* GPIOF_FUNC is not implemented yet */
+ if (_get_gpio_direction(bank, offset) == OMAP_GPIO_DIR_OUT)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_omap_ops = {
+ .direction_input = omap_gpio_direction_input,
+ .direction_output = omap_gpio_direction_output,
+ .get_value = omap_gpio_get_value,
+ .set_value = omap_gpio_set_value,
+ .get_function = omap_gpio_get_function,
+};
+
+static int omap_gpio_probe(struct udevice *dev)
+{
+ struct gpio_bank *bank = dev_get_priv(dev);
+ struct omap_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[18], *str;
+
+ sprintf(name, "gpio@%4x_", (unsigned int)plat->base);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ bank->base = (void *)plat->base;
+ return 0;
+}
+
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+static int omap_gpio_bind(struct udevice *dev)
+{
+ struct omap_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t base_addr;
+
+ if (plat)
+ return 0;
+
+ base_addr = dev_read_addr(dev);
+ if (base_addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /*
+ * TODO:
+ * When every board is converted to driver model and DT is
+ * supported, this can be done by auto-alloc feature, but
+ * not using calloc to alloc memory for plat.
+ *
+ * For example am33xx_gpio uses platform data rather than device tree.
+ *
+ * NOTE: DO NOT COPY this code if you are using device tree.
+ */
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+
+ plat->base = base_addr;
+ plat->port_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL);
+ dev_set_plat(dev, plat);
+
+ return 0;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(OF_REAL)
+static const struct udevice_id omap_gpio_ids[] = {
+ { .compatible = "ti,omap3-gpio" },
+ { .compatible = "ti,omap4-gpio" },
+ { .compatible = "ti,am4372-gpio" },
+ { }
+};
+
+static int omap_gpio_of_to_plat(struct udevice *dev)
+{
+ struct omap_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t addr;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->base = addr;
+ return 0;
+}
+#endif
+
+U_BOOT_DRIVER(gpio_omap) = {
+ .name = "gpio_omap",
+ .id = UCLASS_GPIO,
+#if CONFIG_IS_ENABLED(OF_CONTROL)
+#if CONFIG_IS_ENABLED(OF_REAL)
+ .of_match = omap_gpio_ids,
+ .of_to_plat = of_match_ptr(omap_gpio_of_to_plat),
+ .plat_auto = sizeof(struct omap_gpio_plat),
+#endif
+#else
+ .bind = omap_gpio_bind,
+#endif
+ .ops = &gpio_omap_ops,
+ .probe = omap_gpio_probe,
+ .priv_auto = sizeof(struct gpio_bank),
+#if !CONFIG_IS_ENABLED(OF_CONTROL)
+ .flags = DM_FLAG_PRE_RELOC,
+#endif
+};
+
+#endif /* !DM_GPIO */
diff --git a/drivers/gpio/palmas_gpio.c b/drivers/gpio/palmas_gpio.c
new file mode 100644
index 00000000000..15039351dd5
--- /dev/null
+++ b/drivers/gpio/palmas_gpio.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Based on mainline Linux palmas GPIO driver
+ * Copyright(C) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <power/palmas.h>
+
+#define NUM_GPIOS 8
+
+static int palmas_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct palmas_priv *priv = dev_get_priv(dev->parent);
+ u32 reg;
+ int ret;
+
+ reg = (value) ? PALMAS_GPIO_SET_DATA_OUT : PALMAS_GPIO_CLEAR_DATA_OUT;
+
+ ret = dm_i2c_reg_write(priv->chip2, reg, BIT(offset));
+ if (ret < 0)
+ log_debug("%s: Reg 0x%02x write failed, %d\n", __func__, reg, ret);
+
+ return ret;
+}
+
+static int palmas_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct palmas_priv *priv = dev_get_priv(dev->parent);
+ u32 reg;
+ int ret;
+
+ ret = dm_i2c_reg_read(priv->chip2, PALMAS_GPIO_DATA_DIR);
+ if (ret < 0) {
+ log_debug("%s: GPIO_DATA_DIR read failed, %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (ret & BIT(offset))
+ reg = PALMAS_GPIO_DATA_OUT;
+ else
+ reg = PALMAS_GPIO_DATA_IN;
+
+ ret = dm_i2c_reg_read(priv->chip2, reg);
+ if (ret < 0) {
+ log_debug("%s: Reg 0x%02x read failed, %d\n", __func__, reg, ret);
+ return ret;
+ }
+
+ return !!(ret & BIT(offset));
+}
+
+static int palmas_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct palmas_priv *priv = dev_get_priv(dev->parent);
+ int ret;
+
+ ret = dm_i2c_reg_clrset(priv->chip2, PALMAS_GPIO_DATA_DIR,
+ BIT(offset), 0);
+ if (ret < 0)
+ log_debug("%s: GPIO_DATA_DIR val update failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int palmas_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct palmas_priv *priv = dev_get_priv(dev->parent);
+ int ret;
+
+ /* Set the initial value */
+ palmas_gpio_set_value(dev, offset, value);
+
+ ret = dm_i2c_reg_clrset(priv->chip2, PALMAS_GPIO_DATA_DIR,
+ BIT(offset), BIT(offset));
+ if (ret < 0)
+ log_debug("%s: GPIO_DATA_DIR val update failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int palmas_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct palmas_priv *priv = dev_get_priv(dev->parent);
+ int ret;
+
+ ret = dm_i2c_reg_read(priv->chip2, PALMAS_GPIO_DATA_DIR);
+ if (ret < 0) {
+ log_debug("%s: GPIO_DATA_DIR read failed, %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (ret & BIT(offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops palmas_gpio_ops = {
+ .direction_input = palmas_gpio_direction_input,
+ .direction_output = palmas_gpio_direction_output,
+ .get_value = palmas_gpio_get_value,
+ .set_value = palmas_gpio_set_value,
+ .get_function = palmas_gpio_get_function,
+};
+
+static int palmas_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = NUM_GPIOS;
+ uc_priv->bank_name = "GPIO";
+
+ return 0;
+}
+
+static const struct udevice_id palmas_ids[] = {
+ { .compatible = "ti,palmas-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(palmas_gpio) = {
+ .name = PALMAS_GPIO_DRIVER,
+ .id = UCLASS_GPIO,
+ .of_match = palmas_ids,
+ .probe = palmas_gpio_probe,
+ .ops = &palmas_gpio_ops,
+};
diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c
new file mode 100644
index 00000000000..b5ed35256ee
--- /dev/null
+++ b/drivers/gpio/pca953x.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2008 Extreme Engineering Solutions, Inc.
+ */
+
+/*
+ * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
+ * pca9539, etc)
+ */
+
+#include <common.h>
+#include <command.h>
+#include <i2c.h>
+#include <pca953x.h>
+
+/* Default to an address that hopefully won't corrupt other i2c devices */
+#ifndef CFG_SYS_I2C_PCA953X_ADDR
+#define CFG_SYS_I2C_PCA953X_ADDR (~0)
+#endif
+
+enum {
+ PCA953X_CMD_INFO,
+ PCA953X_CMD_DEVICE,
+ PCA953X_CMD_OUTPUT,
+ PCA953X_CMD_INPUT,
+ PCA953X_CMD_INVERT,
+};
+
+#ifdef CFG_SYS_I2C_PCA953X_WIDTH
+struct pca953x_chip_ngpio {
+ uint8_t chip;
+ uint8_t ngpio;
+};
+
+static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
+ CFG_SYS_I2C_PCA953X_WIDTH;
+
+/*
+ * Determine the number of GPIO pins supported. If we don't know we assume
+ * 8 pins.
+ */
+static int pca953x_ngpio(uint8_t chip)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pca953x_chip_ngpios); i++)
+ if (pca953x_chip_ngpios[i].chip == chip)
+ return pca953x_chip_ngpios[i].ngpio;
+
+ return 8;
+}
+#else
+static int pca953x_ngpio(uint8_t chip)
+{
+ return 8;
+}
+#endif
+
+/*
+ * Modify masked bits in register
+ */
+static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
+{
+ uint8_t valb;
+ uint16_t valw;
+
+ if (pca953x_ngpio(chip) <= 8) {
+ if (i2c_read(chip, addr, 1, &valb, 1))
+ return -1;
+
+ valb &= ~mask;
+ valb |= data;
+
+ return i2c_write(chip, addr, 1, &valb, 1);
+ } else {
+ if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
+ return -1;
+
+ valw = le16_to_cpu(valw);
+ valw &= ~mask;
+ valw |= data;
+ valw = cpu_to_le16(valw);
+
+ return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
+ }
+}
+
+static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
+{
+ uint8_t valb;
+ uint16_t valw;
+
+ if (pca953x_ngpio(chip) <= 8) {
+ if (i2c_read(chip, addr, 1, &valb, 1))
+ return -1;
+ *data = (int)valb;
+ } else {
+ if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
+ return -1;
+ *data = (uint)le16_to_cpu(valw);
+ }
+ return 0;
+}
+
+/*
+ * Set output value of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = low, 1 = high
+ */
+int pca953x_set_val(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
+}
+
+/*
+ * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = read pin value, 1 = read inverted pin value
+ */
+int pca953x_set_pol(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_POL, mask, data);
+}
+
+/*
+ * Set direction of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = output, 1 = input
+ */
+int pca953x_set_dir(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
+}
+
+/*
+ * Read current logic level of all IO pins
+ */
+int pca953x_get_val(uint8_t chip)
+{
+ uint val;
+
+ if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
+ return -1;
+
+ return (int)val;
+}
+
+#if defined(CONFIG_CMD_PCA953X) && !defined(CONFIG_SPL_BUILD)
+/*
+ * Display pca953x information
+ */
+static int pca953x_info(uint8_t chip)
+{
+ int i;
+ uint data;
+ int nr_gpio = pca953x_ngpio(chip);
+ int msb = nr_gpio - 1;
+
+ printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
+ printf("gpio pins: ");
+ for (i = msb; i >= 0; i--)
+ printf("%x", i);
+ printf("\n");
+ for (i = 11 + nr_gpio; i > 0; i--)
+ printf("-");
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
+ return -1;
+ printf("conf: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? 'i' : 'o');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
+ return -1;
+ printf("invert: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
+ return -1;
+ printf("input: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
+ return -1;
+ printf("output: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ return 0;
+}
+
+static struct cmd_tbl cmd_pca953x[] = {
+ U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
+ U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
+ U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
+ U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
+ U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
+};
+
+static int do_pca953x(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ static uint8_t chip = CFG_SYS_I2C_PCA953X_ADDR;
+ int ret = CMD_RET_USAGE, val;
+ ulong ul_arg2 = 0;
+ ulong ul_arg3 = 0;
+ struct cmd_tbl *c;
+
+ c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
+
+ /* All commands but "device" require 'maxargs' arguments */
+ if (!c || !((argc == (c->maxargs)) ||
+ (((long)c->cmd == PCA953X_CMD_DEVICE) &&
+ (argc == (c->maxargs - 1))))) {
+ return CMD_RET_USAGE;
+ }
+
+ /* arg2 used as chip number or pin number */
+ if (argc > 2)
+ ul_arg2 = hextoul(argv[2], NULL);
+
+ /* arg3 used as pin or invert value */
+ if (argc > 3)
+ ul_arg3 = hextoul(argv[3], NULL) & 0x1;
+
+ switch ((long)c->cmd) {
+ case PCA953X_CMD_INFO:
+ ret = pca953x_info(chip);
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+
+ case PCA953X_CMD_DEVICE:
+ if (argc == 3)
+ chip = (uint8_t)ul_arg2;
+ printf("Current device address: 0x%x\n", chip);
+ ret = CMD_RET_SUCCESS;
+ break;
+
+ case PCA953X_CMD_INPUT:
+ ret = pca953x_set_dir(chip, (1 << ul_arg2),
+ PCA953X_DIR_IN << ul_arg2);
+ val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
+
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ else
+ printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2,
+ val);
+ break;
+
+ case PCA953X_CMD_OUTPUT:
+ ret = pca953x_set_dir(chip, (1 << ul_arg2),
+ (PCA953X_DIR_OUT << ul_arg2));
+ if (!ret)
+ ret = pca953x_set_val(chip, (1 << ul_arg2),
+ (ul_arg3 << ul_arg2));
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+
+ case PCA953X_CMD_INVERT:
+ ret = pca953x_set_pol(chip, (1 << ul_arg2),
+ (ul_arg3 << ul_arg2));
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+ }
+
+ if (ret == CMD_RET_FAILURE)
+ eprintf("Error talking to chip at 0x%x\n", chip);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ pca953x, 5, 1, do_pca953x,
+ "pca953x gpio access",
+ "device [dev]\n"
+ " - show or set current device address\n"
+ "pca953x info\n"
+ " - display info for current chip\n"
+ "pca953x output pin 0|1\n"
+ " - set pin as output and drive low or high\n"
+ "pca953x invert pin 0|1\n"
+ " - disable/enable polarity inversion for reads\n"
+ "pca953x input pin\n"
+ " - set pin as input and read value"
+);
+
+#endif /* CONFIG_CMD_PCA953X */
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
new file mode 100644
index 00000000000..b0c66d18317
--- /dev/null
+++ b/drivers/gpio/pca953x_gpio.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Take linux kernel driver drivers/gpio/gpio-pca953x.c for reference.
+ *
+ * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
+ *
+ */
+
+/*
+ * Note:
+ * The driver's compatible table is borrowed from Linux Kernel,
+ * but now max supported gpio pins is 24 and only PCA953X_TYPE
+ * is supported. PCA957X_TYPE is not supported now.
+ * Also the Polarity Inversion feature is not supported now.
+ *
+ * TODO:
+ * 1. Support PCA957X_TYPE
+ * 2. Support Polarity Inversion
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <malloc.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <linux/bitops.h>
+
+#define PCA953X_INPUT 0
+#define PCA953X_OUTPUT 1
+#define PCA953X_INVERT 2
+#define PCA953X_DIRECTION 3
+
+#define PCA957X_INPUT 0
+#define PCA957X_OUTPUT 5
+#define PCA957X_INVERT 1
+#define PCA957X_DIRECTION 4
+
+
+#define PCA_GPIO_MASK 0x00FF
+#define PCA_INT 0x0100
+#define PCA_PCAL BIT(9)
+#define PCA_LATCH_INT (PCA_PCAL | PCA_INT)
+#define PCA953X_TYPE 0x1000
+#define PCA957X_TYPE 0x2000
+#define PCA_TYPE_MASK 0xF000
+#define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK)
+
+enum {
+ PCA953X_DIRECTION_IN,
+ PCA953X_DIRECTION_OUT,
+};
+
+#define MAX_BANK 5
+#define BANK_SZ 8
+
+struct pca95xx_reg {
+ int input;
+ int output;
+ int invert;
+ int direction;
+};
+
+static const struct pca95xx_reg pca953x_regs = {
+ .direction = PCA953X_DIRECTION,
+ .output = PCA953X_OUTPUT,
+ .input = PCA953X_INPUT,
+ .invert = PCA953X_INVERT,
+};
+
+static const struct pca95xx_reg pca957x_regs = {
+ .direction = PCA957X_DIRECTION,
+ .output = PCA957X_OUTPUT,
+ .input = PCA957X_INPUT,
+ .invert = PCA957X_INVERT,
+};
+
+/*
+ * struct pca953x_info - Data for pca953x/pca957x
+ *
+ * @dev: udevice structure for the device
+ * @addr: i2c slave address
+ * @invert: Polarity inversion or not
+ * @gpio_count: the number of gpio pins that the device supports
+ * @chip_type: indicate the chip type,PCA953X or PCA957X
+ * @bank_count: the number of banks that the device supports
+ * @reg_output: array to hold the value of output registers
+ * @reg_direction: array to hold the value of direction registers
+ * @regs: struct to hold the registers addresses
+ */
+struct pca953x_info {
+ struct udevice *dev;
+ int addr;
+ int invert;
+ int gpio_count;
+ int chip_type;
+ int bank_count;
+ u8 reg_output[MAX_BANK];
+ u8 reg_direction[MAX_BANK];
+ const struct pca95xx_reg *regs;
+};
+
+static int pca953x_write_single(struct udevice *dev, int reg, u8 val,
+ int offset)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
+ int off = offset / BANK_SZ;
+ int ret = 0;
+
+ ret = dm_i2c_write(dev, (reg << bank_shift) + off, &val, 1);
+ if (ret) {
+ dev_err(dev, "%s error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_read_single(struct udevice *dev, int reg, u8 *val,
+ int offset)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
+ int off = offset / BANK_SZ;
+ int ret;
+ u8 byte;
+
+ ret = dm_i2c_read(dev, (reg << bank_shift) + off, &byte, 1);
+ if (ret) {
+ dev_err(dev, "%s error\n", __func__);
+ return ret;
+ }
+
+ *val = byte;
+
+ return 0;
+}
+
+static int pca953x_read_regs(struct udevice *dev, int reg, u8 *val)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int ret = 0;
+
+ if (info->gpio_count <= 8) {
+ ret = dm_i2c_read(dev, reg, val, 1);
+ } else if (info->gpio_count <= 16) {
+ ret = dm_i2c_read(dev, reg << 1, val, info->bank_count);
+ } else if (info->gpio_count <= 24) {
+ /* Auto increment */
+ ret = dm_i2c_read(dev, (reg << 2) | 0x80, val,
+ info->bank_count);
+ } else if (info->gpio_count == 40) {
+ /* Auto increment */
+ ret = dm_i2c_read(dev, (reg << 3) | 0x80, val,
+ info->bank_count);
+ } else {
+ dev_err(dev, "Unsupported now\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int pca953x_write_regs(struct udevice *dev, int reg, u8 *val)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int ret = 0;
+
+ if (info->gpio_count <= 8) {
+ ret = dm_i2c_write(dev, reg, val, 1);
+ } else if (info->gpio_count <= 16) {
+ ret = dm_i2c_write(dev, reg << 1, val, info->bank_count);
+ } else if (info->gpio_count <= 24) {
+ /* Auto increment */
+ ret = dm_i2c_write(dev, (reg << 2) | 0x80, val,
+ info->bank_count);
+ } else if (info->gpio_count == 40) {
+ /* Auto increment */
+ ret = dm_i2c_write(dev, (reg << 3) | 0x80, val, info->bank_count);
+ } else {
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int pca953x_is_output(struct udevice *dev, int offset)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+
+ /*0: output; 1: input */
+ return !(info->reg_direction[bank] & (1 << off));
+}
+
+static int pca953x_get_value(struct udevice *dev, uint offset)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int ret;
+ u8 val = 0;
+
+ int off = offset % BANK_SZ;
+
+ ret = pca953x_read_single(dev, info->regs->input, &val, offset);
+ if (ret)
+ return ret;
+
+ return (val >> off) & 0x1;
+}
+
+static int pca953x_set_value(struct udevice *dev, uint offset, int value)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+ u8 val;
+ int ret;
+
+ if (value)
+ val = info->reg_output[bank] | (1 << off);
+ else
+ val = info->reg_output[bank] & ~(1 << off);
+
+ ret = pca953x_write_single(dev, info->regs->output, val, offset);
+ if (ret)
+ return ret;
+
+ info->reg_output[bank] = val;
+
+ return 0;
+}
+
+static int pca953x_set_direction(struct udevice *dev, uint offset, int dir)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+ u8 val;
+ int ret;
+
+ if (dir == PCA953X_DIRECTION_IN)
+ val = info->reg_direction[bank] | (1 << off);
+ else
+ val = info->reg_direction[bank] & ~(1 << off);
+
+ ret = pca953x_write_single(dev, info->regs->direction, val, offset);
+ if (ret)
+ return ret;
+
+ info->reg_direction[bank] = val;
+
+ return 0;
+}
+
+static int pca953x_direction_input(struct udevice *dev, uint offset)
+{
+ return pca953x_set_direction(dev, offset, PCA953X_DIRECTION_IN);
+}
+
+static int pca953x_direction_output(struct udevice *dev, uint offset, int value)
+{
+ /* Configure output value. */
+ pca953x_set_value(dev, offset, value);
+
+ /* Configure direction as output. */
+ pca953x_set_direction(dev, offset, PCA953X_DIRECTION_OUT);
+
+ return 0;
+}
+
+static int pca953x_get_function(struct udevice *dev, uint offset)
+{
+ if (pca953x_is_output(dev, offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int pca953x_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops pca953x_ops = {
+ .direction_input = pca953x_direction_input,
+ .direction_output = pca953x_direction_output,
+ .get_value = pca953x_get_value,
+ .set_value = pca953x_set_value,
+ .get_function = pca953x_get_function,
+ .xlate = pca953x_xlate,
+};
+
+static int pca953x_probe(struct udevice *dev)
+{
+ struct pca953x_info *info = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[32], label[8], *str;
+ int addr;
+ ulong driver_data;
+ int ret;
+ int size;
+ const u8 *tmp;
+ u8 val[MAX_BANK];
+
+ addr = dev_read_addr(dev);
+ if (addr == 0)
+ return -ENODEV;
+
+ info->addr = addr;
+
+ driver_data = dev_get_driver_data(dev);
+
+ info->gpio_count = driver_data & PCA_GPIO_MASK;
+ if (info->gpio_count > MAX_BANK * BANK_SZ) {
+ dev_err(dev, "Max support %d pins now\n", MAX_BANK * BANK_SZ);
+ return -EINVAL;
+ }
+
+ info->chip_type = PCA_CHIP_TYPE(driver_data);
+ if (info->chip_type == PCA953X_TYPE)
+ info->regs = &pca953x_regs;
+ else
+ info->regs = &pca957x_regs;
+
+ info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ);
+
+ ret = pca953x_read_regs(dev, info->regs->output, info->reg_output);
+ if (ret) {
+ dev_err(dev, "Error reading output register\n");
+ return ret;
+ }
+
+ ret = pca953x_read_regs(dev, PCA953X_DIRECTION, info->reg_direction);
+ if (ret) {
+ dev_err(dev, "Error reading direction register\n");
+ return ret;
+ }
+
+ tmp = dev_read_prop(dev, "label", &size);
+
+ if (tmp) {
+ memcpy(label, tmp, sizeof(label) - 1);
+ label[sizeof(label) - 1] = '\0';
+ snprintf(name, sizeof(name), "%s@%x_", label, info->addr);
+ } else {
+ snprintf(name, sizeof(name), "gpio@%x_", info->addr);
+ }
+
+ /* Clear the polarity registers to no invert */
+ memset(val, 0, MAX_BANK);
+ ret = pca953x_write_regs(dev, info->regs->invert, val);
+ if (ret < 0) {
+ dev_err(dev, "Error writing invert register\n");
+ return ret;
+ }
+
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = info->gpio_count;
+
+ dev_dbg(dev, "%s is ready\n", str);
+
+ return 0;
+}
+
+#define OF_953X(__nrgpio, __int) (ulong)(__nrgpio | PCA953X_TYPE | __int)
+#define OF_957X(__nrgpio, __int) (ulong)(__nrgpio | PCA957X_TYPE | __int)
+
+static const struct udevice_id pca953x_ids[] = {
+ { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
+ { .compatible = "nxp,pca9534", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9536", .data = OF_953X(4, 0), },
+ { .compatible = "nxp,pca9537", .data = OF_953X(4, PCA_INT), },
+ { .compatible = "nxp,pca9538", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9539", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9554", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9556", .data = OF_953X(8, 0), },
+ { .compatible = "nxp,pca9557", .data = OF_953X(8, 0), },
+ { .compatible = "nxp,pca9574", .data = OF_957X(8, PCA_INT), },
+ { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
+ { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+
+ { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
+
+ { .compatible = "maxim,max7310", .data = OF_953X(8, 0), },
+ { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "maxim,max7315", .data = OF_953X(8, PCA_INT), },
+
+ { .compatible = "ti,pca6107", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "ti,tca6408", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
+ { .compatible = "ti,tca9539", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "ti,tca9554", .data = OF_953X(8, PCA_INT), },
+
+ { .compatible = "onsemi,pca9654", .data = OF_953X(8, PCA_INT), },
+
+ { .compatible = "exar,xra1202", .data = OF_953X(8, 0), },
+ { }
+};
+
+U_BOOT_DRIVER(pca953x) = {
+ .name = "pca953x",
+ .id = UCLASS_GPIO,
+ .ops = &pca953x_ops,
+ .probe = pca953x_probe,
+ .plat_auto = sizeof(struct pca953x_info),
+ .of_match = pca953x_ids,
+};
diff --git a/drivers/gpio/pcf8575_gpio.c b/drivers/gpio/pcf8575_gpio.c
new file mode 100644
index 00000000000..f38e215c4d6
--- /dev/null
+++ b/drivers/gpio/pcf8575_gpio.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCF8575 I2C GPIO EXPANDER DRIVER
+ *
+ * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Vignesh R <vigneshr@ti.com>
+ *
+ *
+ * Driver for TI PCF-8575 16-bit I2C gpio expander. Based on
+ * gpio-pcf857x Linux Kernel(v4.7) driver.
+ *
+ * Copyright (C) 2007 David Brownell
+ *
+ * Add support for 8 bit expanders - like pca8574
+ * Copyright (C) 2021 Lukasz Majewski - DENX Software Engineering
+ *
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <asm-generic/gpio.h>
+#include <asm/global_data.h>
+#include <linux/bitops.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pcf8575_chip {
+ /* NOTE: these chips have strange "quasi-bidirectional" I/O pins.
+ * We can't actually know whether a pin is configured (a) as output
+ * and driving the signal low, or (b) as input and reporting a low
+ * value ... without knowing the last value written since the chip
+ * came out of reset (if any). We can't read the latched output.
+ * In short, the only reliable solution for setting up pin direction
+ * is to do it explicitly.
+ *
+ * Using "out" avoids that trouble. When left initialized to zero,
+ * our software copy of the "latch" then matches the chip's all-ones
+ * reset state. Otherwise it flags pins to be driven low.
+ */
+ unsigned int out; /* software latch */
+};
+
+/* Read/Write to I/O expander */
+
+static int pcf8575_i2c_write(struct udevice *dev, unsigned int word)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
+ u8 buf[2] = { word & 0xff, word >> 8, };
+ int ret;
+
+ ret = dm_i2c_write(dev, 0, buf, dev_get_driver_data(dev));
+ if (ret)
+ printf("%s i2c write failed to addr %x\n", __func__,
+ chip->chip_addr);
+
+ return ret;
+}
+
+static int pcf8575_i2c_read(struct udevice *dev)
+{
+ struct dm_i2c_chip *chip = dev_get_parent_plat(dev);
+ u8 buf[2] = {0x00, 0x00};
+ int ret;
+
+ ret = dm_i2c_read(dev, 0, buf, dev_get_driver_data(dev));
+ if (ret) {
+ printf("%s i2c read failed from addr %x\n", __func__,
+ chip->chip_addr);
+ return ret;
+ }
+
+ return (buf[1] << 8) | buf[0];
+}
+
+static int pcf8575_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct pcf8575_chip *plat = dev_get_plat(dev);
+ int status;
+
+ plat->out |= BIT(offset);
+ status = pcf8575_i2c_write(dev, plat->out);
+
+ return status;
+}
+
+static int pcf8575_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct pcf8575_chip *plat = dev_get_plat(dev);
+ int ret;
+
+ if (value)
+ plat->out |= BIT(offset);
+ else
+ plat->out &= ~BIT(offset);
+
+ ret = pcf8575_i2c_write(dev, plat->out);
+
+ return ret;
+}
+
+static int pcf8575_get_value(struct udevice *dev, unsigned int offset)
+{
+ int value;
+
+ value = pcf8575_i2c_read(dev);
+
+ return (value < 0) ? value : ((value & BIT(offset)) >> offset);
+}
+
+static int pcf8575_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ return pcf8575_direction_output(dev, offset, value);
+}
+
+static int pcf8575_ofdata_plat(struct udevice *dev)
+{
+ struct pcf8575_chip *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ int n_latch;
+
+ /*
+ * Number of pins depends on the expander device and is specified
+ * in the struct udevice_id (as in the Linue kernel).
+ */
+ uc_priv->gpio_count = dev_get_driver_data(dev) * 8;
+ uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
+ "gpio-bank-name", NULL);
+ if (!uc_priv->bank_name)
+ uc_priv->bank_name = fdt_get_name(gd->fdt_blob,
+ dev_of_offset(dev), NULL);
+
+ n_latch = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
+ "lines-initial-states", 0);
+ plat->out = ~n_latch;
+
+ return 0;
+}
+
+static int pcf8575_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ debug("%s GPIO controller with %d gpios probed\n",
+ uc_priv->bank_name, uc_priv->gpio_count);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops pcf8575_gpio_ops = {
+ .direction_input = pcf8575_direction_input,
+ .direction_output = pcf8575_direction_output,
+ .get_value = pcf8575_get_value,
+ .set_value = pcf8575_set_value,
+};
+
+static const struct udevice_id pcf8575_gpio_ids[] = {
+ { .compatible = "nxp,pcf8575", .data = 2 },
+ { .compatible = "ti,pcf8575", .data = 2 },
+ { .compatible = "nxp,pca8574", .data = 1 },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_pcf8575) = {
+ .name = "gpio_pcf8575",
+ .id = UCLASS_GPIO,
+ .ops = &pcf8575_gpio_ops,
+ .of_match = pcf8575_gpio_ids,
+ .of_to_plat = pcf8575_ofdata_plat,
+ .probe = pcf8575_gpio_probe,
+ .plat_auto = sizeof(struct pcf8575_chip),
+};
diff --git a/drivers/gpio/pic32_gpio.c b/drivers/gpio/pic32_gpio.c
new file mode 100644
index 00000000000..975a2af3ccb
--- /dev/null
+++ b/drivers/gpio/pic32_gpio.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Microchip Technology Inc
+ * Purna Chandra Mandal <purna.mandal@microchip.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <mach/pic32.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Peripheral Pin Control */
+struct pic32_reg_port {
+ struct pic32_reg_atomic ansel;
+ struct pic32_reg_atomic tris;
+ struct pic32_reg_atomic port;
+ struct pic32_reg_atomic lat;
+ struct pic32_reg_atomic open_drain;
+ struct pic32_reg_atomic cnpu;
+ struct pic32_reg_atomic cnpd;
+ struct pic32_reg_atomic cncon;
+};
+
+enum {
+ MICROCHIP_GPIO_DIR_OUT,
+ MICROCHIP_GPIO_DIR_IN,
+ MICROCHIP_GPIOS_PER_BANK = 16,
+};
+
+struct pic32_gpio_priv {
+ struct pic32_reg_port *regs;
+ char name[2];
+};
+
+static int pic32_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(readl(&priv->regs->port.raw) & BIT(offset));
+}
+
+static int pic32_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ if (value)
+ writel(mask, &priv->regs->port.set);
+ else
+ writel(mask, &priv->regs->port.clr);
+
+ return 0;
+}
+
+static int pic32_gpio_direction(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+
+ /* pin in analog mode ? */
+ if (readl(&priv->regs->ansel.raw) & BIT(offset))
+ return -EPERM;
+
+ if (readl(&priv->regs->tris.raw) & BIT(offset))
+ return MICROCHIP_GPIO_DIR_IN;
+ else
+ return MICROCHIP_GPIO_DIR_OUT;
+}
+
+static int pic32_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ writel(mask, &priv->regs->ansel.clr);
+ writel(mask, &priv->regs->tris.set);
+
+ return 0;
+}
+
+static int pic32_gpio_direction_output(struct udevice *dev,
+ unsigned offset, int value)
+{
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ int mask = BIT(offset);
+
+ writel(mask, &priv->regs->ansel.clr);
+ writel(mask, &priv->regs->tris.clr);
+
+ pic32_gpio_set_value(dev, offset, value);
+ return 0;
+}
+
+static int pic32_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ int ret = GPIOF_UNUSED;
+
+ switch (pic32_gpio_direction(dev, offset)) {
+ case MICROCHIP_GPIO_DIR_OUT:
+ ret = GPIOF_OUTPUT;
+ break;
+ case MICROCHIP_GPIO_DIR_IN:
+ ret = GPIOF_INPUT;
+ break;
+ default:
+ ret = GPIOF_UNUSED;
+ break;
+ }
+ return ret;
+}
+
+static const struct dm_gpio_ops gpio_pic32_ops = {
+ .direction_input = pic32_gpio_direction_input,
+ .direction_output = pic32_gpio_direction_output,
+ .get_value = pic32_gpio_get_value,
+ .set_value = pic32_gpio_set_value,
+ .get_function = pic32_gpio_get_function,
+};
+
+static int pic32_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct pic32_gpio_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+ fdt_size_t size;
+ char *end;
+ int bank;
+
+ addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
+ &size);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = ioremap(addr, size);
+
+ uc_priv->gpio_count = MICROCHIP_GPIOS_PER_BANK;
+ /* extract bank name */
+ end = strrchr(dev->name, '@');
+ bank = trailing_strtoln(dev->name, end);
+ priv->name[0] = 'A' + bank;
+ uc_priv->bank_name = priv->name;
+
+ return 0;
+}
+
+static const struct udevice_id pic32_gpio_ids[] = {
+ { .compatible = "microchip,pic32mzda-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_pic32) = {
+ .name = "gpio_pic32",
+ .id = UCLASS_GPIO,
+ .of_match = pic32_gpio_ids,
+ .ops = &gpio_pic32_ops,
+ .probe = pic32_gpio_probe,
+ .priv_auto = sizeof(struct pic32_gpio_priv),
+};
diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c
new file mode 100644
index 00000000000..6167c841167
--- /dev/null
+++ b/drivers/gpio/qcom_pmic_gpio.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm generic pmic gpio driver
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <spmi/spmi.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+
+/* Register offset for each gpio */
+#define REG_OFFSET(x) ((x) * 0x100)
+
+/* Register maps */
+
+/* Type and subtype are shared for all PMIC peripherals */
+#define REG_TYPE 0x4
+#define REG_SUBTYPE 0x5
+
+/* GPIO peripheral type and subtype out_values */
+#define REG_TYPE_VAL 0x10
+#define REG_SUBTYPE_GPIO_4CH 0x1
+#define REG_SUBTYPE_GPIOC_4CH 0x5
+#define REG_SUBTYPE_GPIO_8CH 0x9
+#define REG_SUBTYPE_GPIOC_8CH 0xd
+#define REG_SUBTYPE_GPIO_LV 0x10
+#define REG_SUBTYPE_GPIO_MV 0x11
+
+#define REG_STATUS 0x08
+#define REG_STATUS_VAL_MASK 0x1
+
+/* MODE_CTL */
+#define REG_CTL 0x40
+#define REG_CTL_MODE_MASK 0x70
+#define REG_CTL_MODE_INPUT 0x00
+#define REG_CTL_MODE_INOUT 0x20
+#define REG_CTL_MODE_OUTPUT 0x10
+#define REG_CTL_OUTPUT_MASK 0x0F
+#define REG_CTL_LV_MV_MODE_MASK 0x3
+#define REG_CTL_LV_MV_MODE_INPUT 0x0
+#define REG_CTL_LV_MV_MODE_INOUT 0x2
+#define REG_CTL_LV_MV_MODE_OUTPUT 0x1
+
+#define REG_DIG_VIN_CTL 0x41
+#define REG_DIG_VIN_VIN0 0
+
+#define REG_DIG_PULL_CTL 0x42
+#define REG_DIG_PULL_NO_PU 0x5
+
+#define REG_LV_MV_OUTPUT_CTL 0x44
+#define REG_LV_MV_OUTPUT_CTL_MASK 0x80
+#define REG_LV_MV_OUTPUT_CTL_SHIFT 7
+
+#define REG_DIG_OUT_CTL 0x45
+#define REG_DIG_OUT_CTL_CMOS (0x0 << 4)
+#define REG_DIG_OUT_CTL_DRIVE_L 0x1
+
+#define REG_EN_CTL 0x46
+#define REG_EN_CTL_ENABLE (1 << 7)
+
+struct qcom_gpio_bank {
+ uint32_t pid; /* Peripheral ID on SPMI bus */
+ bool lv_mv_type; /* If subtype is GPIO_LV(0x10) or GPIO_MV(0x11) */
+};
+
+static int qcom_gpio_set_direction(struct udevice *dev, unsigned offset,
+ bool input, int value)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ uint32_t reg_ctl_val;
+ int ret;
+
+ /* Disable the GPIO */
+ ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL,
+ REG_EN_CTL_ENABLE, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Select the mode and output */
+ if (priv->lv_mv_type) {
+ if (input)
+ reg_ctl_val = REG_CTL_LV_MV_MODE_INPUT;
+ else
+ reg_ctl_val = REG_CTL_LV_MV_MODE_INOUT;
+ } else {
+ if (input)
+ reg_ctl_val = REG_CTL_MODE_INPUT;
+ else
+ reg_ctl_val = REG_CTL_MODE_INOUT | !!value;
+ }
+
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, reg_ctl_val);
+ if (ret < 0)
+ return ret;
+
+ if (priv->lv_mv_type && !input) {
+ ret = pmic_reg_write(dev->parent,
+ gpio_base + REG_LV_MV_OUTPUT_CTL,
+ !!value << REG_LV_MV_OUTPUT_CTL_SHIFT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set the right pull (no pull) */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL,
+ REG_DIG_PULL_NO_PU);
+ if (ret < 0)
+ return ret;
+
+ /* Configure output pin drivers if needed */
+ if (!input) {
+ /* Select the VIN - VIN0, pin is input so it doesn't matter */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL,
+ REG_DIG_VIN_VIN0);
+ if (ret < 0)
+ return ret;
+
+ /* Set the right dig out control */
+ ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL,
+ REG_DIG_OUT_CTL_CMOS |
+ REG_DIG_OUT_CTL_DRIVE_L);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Enable the GPIO */
+ return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0,
+ REG_EN_CTL_ENABLE);
+}
+
+static int qcom_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ return qcom_gpio_set_direction(dev, offset, true, 0);
+}
+
+static int qcom_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return qcom_gpio_set_direction(dev, offset, false, value);
+}
+
+static int qcom_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ int reg;
+
+ reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL);
+ if (reg < 0)
+ return reg;
+
+ if (priv->lv_mv_type) {
+ switch (reg & REG_CTL_LV_MV_MODE_MASK) {
+ case REG_CTL_LV_MV_MODE_INPUT:
+ return GPIOF_INPUT;
+ case REG_CTL_LV_MV_MODE_INOUT: /* Fallthrough */
+ case REG_CTL_LV_MV_MODE_OUTPUT:
+ return GPIOF_OUTPUT;
+ default:
+ return GPIOF_UNKNOWN;
+ }
+ } else {
+ switch (reg & REG_CTL_MODE_MASK) {
+ case REG_CTL_MODE_INPUT:
+ return GPIOF_INPUT;
+ case REG_CTL_MODE_INOUT: /* Fallthrough */
+ case REG_CTL_MODE_OUTPUT:
+ return GPIOF_OUTPUT;
+ default:
+ return GPIOF_UNKNOWN;
+ }
+ }
+}
+
+static int qcom_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+ int reg;
+
+ reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS);
+ if (reg < 0)
+ return reg;
+
+ return !!(reg & REG_STATUS_VAL_MASK);
+}
+
+static int qcom_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ uint32_t gpio_base = priv->pid + REG_OFFSET(offset);
+
+ /* Set the output value of the gpio */
+ if (priv->lv_mv_type)
+ return pmic_clrsetbits(dev->parent,
+ gpio_base + REG_LV_MV_OUTPUT_CTL,
+ REG_LV_MV_OUTPUT_CTL_MASK,
+ !!value << REG_LV_MV_OUTPUT_CTL_SHIFT);
+ else
+ return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL,
+ REG_CTL_OUTPUT_MASK, !!value);
+}
+
+static const struct dm_gpio_ops qcom_gpio_ops = {
+ .direction_input = qcom_gpio_direction_input,
+ .direction_output = qcom_gpio_direction_output,
+ .get_value = qcom_gpio_get_value,
+ .set_value = qcom_gpio_set_value,
+ .get_function = qcom_gpio_get_function,
+};
+
+static int qcom_gpio_probe(struct udevice *dev)
+{
+ struct qcom_gpio_bank *priv = dev_get_priv(dev);
+ int reg;
+ u64 pid;
+
+ pid = dev_read_addr(dev);
+ if (pid == FDT_ADDR_T_NONE)
+ return log_msg_ret("bad address", -EINVAL);
+
+ priv->pid = pid;
+
+ /* Do a sanity check */
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE);
+ if (reg != REG_TYPE_VAL)
+ return log_msg_ret("bad type", -ENXIO);
+
+ reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE);
+ if (reg != REG_SUBTYPE_GPIO_4CH && reg != REG_SUBTYPE_GPIOC_4CH &&
+ reg != REG_SUBTYPE_GPIO_LV && reg != REG_SUBTYPE_GPIO_MV)
+ return log_msg_ret("bad subtype", -ENXIO);
+
+ priv->lv_mv_type = reg == REG_SUBTYPE_GPIO_LV ||
+ reg == REG_SUBTYPE_GPIO_MV;
+
+ return 0;
+}
+
+/*
+ * Parse basic GPIO count specified via the gpio-ranges property
+ * as specified in Linux devicetrees
+ * Returns < 0 on error, otherwise gpio count
+ */
+static int qcom_gpio_of_parse_ranges(struct udevice *dev)
+{
+ int ret;
+ struct ofnode_phandle_args args;
+
+ ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges",
+ NULL, 3, 0, &args);
+ if (ret)
+ return log_msg_ret("gpio-ranges", ret);
+
+ return args.args[2];
+}
+
+static int qcom_gpio_of_to_plat(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ ret = qcom_gpio_of_parse_ranges(dev);
+ if (ret > 0)
+ uc_priv->gpio_count = ret;
+ else
+ return ret;
+
+ uc_priv->bank_name = "pmic";
+
+ return 0;
+}
+
+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" },
+ { }
+};
+
+U_BOOT_DRIVER(qcom_pmic_gpio) = {
+ .name = "qcom_pmic_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = qcom_gpio_ids,
+ .of_to_plat = qcom_gpio_of_to_plat,
+ .probe = qcom_gpio_probe,
+ .ops = &qcom_gpio_ops,
+ .priv_auto = sizeof(struct qcom_gpio_bank),
+};
+
diff --git a/drivers/gpio/qe_gpio.c b/drivers/gpio/qe_gpio.c
new file mode 100644
index 00000000000..16e8d1eae6e
--- /dev/null
+++ b/drivers/gpio/qe_gpio.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 CR GROUP France
+ * Christophe Leroy <christophe.leroy@csgroup.eu>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <asm/gpio.h>
+#include <asm/immap_83xx.h>
+#include <asm/io.h>
+#include <dm/of_access.h>
+
+#define QE_DIR_NONE 0
+#define QE_DIR_OUT 1
+#define QE_DIR_IN 2
+#define QE_DIR_IN_OUT 3
+
+struct qe_gpio_data {
+ /* The bank's register base in memory */
+ struct gpio_n __iomem *base;
+ /* The address of the registers; used to identify the bank */
+ phys_addr_t addr;
+};
+
+static inline u32 gpio_mask(uint gpio)
+{
+ return 1U << (31 - (gpio));
+}
+
+static inline u32 gpio_mask2(uint gpio)
+{
+ return 1U << (30 - ((gpio & 15) << 1));
+}
+
+static int qe_gpio_direction_input(struct udevice *dev, uint gpio)
+{
+ struct qe_gpio_data *data = dev_get_priv(dev);
+ struct gpio_n __iomem *base = data->base;
+ u32 mask2 = gpio_mask2(gpio);
+
+ if (gpio < 16)
+ clrsetbits_be32(&base->dir1, mask2 * QE_DIR_OUT, mask2 * QE_DIR_IN);
+ else
+ clrsetbits_be32(&base->dir2, mask2 * QE_DIR_OUT, mask2 * QE_DIR_IN);
+
+ return 0;
+}
+
+static int qe_gpio_set_value(struct udevice *dev, uint gpio, int value)
+{
+ struct qe_gpio_data *data = dev_get_priv(dev);
+ struct gpio_n __iomem *base = data->base;
+ u32 mask = gpio_mask(gpio);
+ u32 mask2 = gpio_mask2(gpio);
+
+ if (gpio < 16)
+ clrsetbits_be32(&base->dir1, mask2 * QE_DIR_IN, mask2 * QE_DIR_OUT);
+ else
+ clrsetbits_be32(&base->dir2, mask2 * QE_DIR_IN, mask2 * QE_DIR_OUT);
+
+ if (value)
+ setbits_be32(&base->pdat, mask);
+ else
+ clrbits_be32(&base->pdat, mask);
+
+ return 0;
+}
+
+static int qe_gpio_get_value(struct udevice *dev, uint gpio)
+{
+ struct qe_gpio_data *data = dev_get_priv(dev);
+ struct gpio_n __iomem *base = data->base;
+ u32 mask = gpio_mask(gpio);
+
+ return !!(in_be32(&base->pdat) & mask);
+}
+
+static int qe_gpio_get_function(struct udevice *dev, uint gpio)
+{
+ struct qe_gpio_data *data = dev_get_priv(dev);
+ struct gpio_n __iomem *base = data->base;
+ u32 mask2 = gpio_mask2(gpio);
+ int dir;
+
+ if (gpio < 16)
+ dir = in_be32(&base->dir1);
+ else
+ dir = in_be32(&base->dir2);
+
+ if ((dir & (mask2 * QE_DIR_IN_OUT)) == (mask2 & QE_DIR_IN))
+ return GPIOF_INPUT;
+ else if ((dir & (mask2 * QE_DIR_IN_OUT)) == (mask2 & QE_DIR_OUT))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_UNKNOWN;
+}
+
+static int qe_gpio_of_to_plat(struct udevice *dev)
+{
+ struct qe_gpio_plat *plat = dev_get_plat(dev);
+
+ plat->addr = dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat->size);
+
+ return 0;
+}
+
+static int qe_gpio_plat_to_priv(struct udevice *dev)
+{
+ struct qe_gpio_data *priv = dev_get_priv(dev);
+ struct qe_gpio_plat *plat = dev_get_plat(dev);
+ unsigned long size = plat->size;
+
+ if (size == 0)
+ size = sizeof(struct gpio_n);
+
+ priv->addr = plat->addr;
+ priv->base = (void __iomem *)plat->addr;
+
+ if (!priv->base)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int qe_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct qe_gpio_data *data = dev_get_priv(dev);
+ char name[32], *str;
+
+ qe_gpio_plat_to_priv(dev);
+
+ snprintf(name, sizeof(name), "QE@%.8llx",
+ (unsigned long long)data->addr);
+ str = strdup(name);
+
+ if (!str)
+ return -ENOMEM;
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = 32;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_qe_ops = {
+ .direction_input = qe_gpio_direction_input,
+ .direction_output = qe_gpio_set_value,
+ .get_value = qe_gpio_get_value,
+ .set_value = qe_gpio_set_value,
+ .get_function = qe_gpio_get_function,
+};
+
+static const struct udevice_id qe_gpio_ids[] = {
+ { .compatible = "fsl,mpc8323-qe-pario-bank"},
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_qe) = {
+ .name = "gpio_qe",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_qe_ops,
+ .of_to_plat = qe_gpio_of_to_plat,
+ .plat_auto = sizeof(struct qe_gpio_plat),
+ .of_match = qe_gpio_ids,
+ .probe = qe_gpio_probe,
+ .priv_auto = sizeof(struct qe_gpio_data),
+};
diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c
new file mode 100644
index 00000000000..4a6ae554bf7
--- /dev/null
+++ b/drivers/gpio/rk_gpio.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * (C) Copyright 2008-2014 Rockchip Electronics
+ * Peter, Software Engineering, <superpeter.cai@gmail.com>.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <syscon.h>
+#include <linux/errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <asm/arch-rockchip/gpio.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <dt-bindings/pinctrl/rockchip.h>
+
+#define SWPORT_DR 0x0000
+#define SWPORT_DDR 0x0004
+#define EXT_PORT 0x0050
+#define SWPORT_DR_L 0x0000
+#define SWPORT_DR_H 0x0004
+#define SWPORT_DDR_L 0x0008
+#define SWPORT_DDR_H 0x000C
+#define EXT_PORT_V2 0x0070
+#define VER_ID_V2 0x0078
+
+enum {
+ ROCKCHIP_GPIOS_PER_BANK = 32,
+};
+
+struct rockchip_gpio_priv {
+ void __iomem *regs;
+ struct udevice *pinctrl;
+ int bank;
+ char name[2];
+ u32 version;
+};
+
+static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ u32 mask = BIT(offset), data;
+
+ if (priv->version)
+ data = readl(priv->regs + EXT_PORT_V2);
+ else
+ data = readl(priv->regs + EXT_PORT);
+
+ return (data & mask) ? 1 : 0;
+}
+
+static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ u32 mask = BIT(offset), data = value ? mask : 0;
+
+ if (priv->version && offset >= 16)
+ rk_clrsetreg(priv->regs + SWPORT_DR_H, mask >> 16, data >> 16);
+ else if (priv->version)
+ rk_clrsetreg(priv->regs + SWPORT_DR_L, mask, data);
+ else
+ clrsetbits_le32(priv->regs + SWPORT_DR, mask, data);
+
+ return 0;
+}
+
+static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ u32 mask = BIT(offset);
+
+ if (priv->version && offset >= 16)
+ rk_clrreg(priv->regs + SWPORT_DDR_H, mask >> 16);
+ else if (priv->version)
+ rk_clrreg(priv->regs + SWPORT_DDR_L, mask);
+ else
+ clrbits_le32(priv->regs + SWPORT_DDR, mask);
+
+ return 0;
+}
+
+static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ u32 mask = BIT(offset);
+
+ rockchip_gpio_set_value(dev, offset, value);
+
+ if (priv->version && offset >= 16)
+ rk_setreg(priv->regs + SWPORT_DDR_H, mask >> 16);
+ else if (priv->version)
+ rk_setreg(priv->regs + SWPORT_DDR_L, mask);
+ else
+ setbits_le32(priv->regs + SWPORT_DDR, mask);
+
+ return 0;
+}
+
+static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ u32 mask = BIT(offset), data;
+ int ret;
+
+ if (CONFIG_IS_ENABLED(PINCTRL)) {
+ ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset);
+ if (ret < 0)
+ return ret;
+ else if (ret != RK_FUNC_GPIO)
+ return GPIOF_FUNC;
+ }
+
+ if (priv->version && offset >= 16)
+ data = readl(priv->regs + SWPORT_DDR_H) << 16;
+ else if (priv->version)
+ data = readl(priv->regs + SWPORT_DDR_L);
+ else
+ data = readl(priv->regs + SWPORT_DDR);
+
+ return (data & mask) ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+/* Simple SPL interface to GPIOs */
+#ifdef CONFIG_SPL_BUILD
+
+enum {
+ PULL_NONE_1V8 = 0,
+ PULL_DOWN_1V8 = 1,
+ PULL_UP_1V8 = 3,
+};
+
+int spl_gpio_set_pull(void *vregs, uint gpio, int pull)
+{
+ u32 *regs = vregs;
+ uint val;
+
+ regs += gpio >> GPIO_BANK_SHIFT;
+ gpio &= GPIO_OFFSET_MASK;
+ switch (pull) {
+ case GPIO_PULL_UP:
+ val = PULL_UP_1V8;
+ break;
+ case GPIO_PULL_DOWN:
+ val = PULL_DOWN_1V8;
+ break;
+ case GPIO_PULL_NORMAL:
+ default:
+ val = PULL_NONE_1V8;
+ break;
+ }
+ clrsetbits_le32(regs, 3 << (gpio * 2), val << (gpio * 2));
+
+ return 0;
+}
+
+int spl_gpio_output(void *vregs, uint gpio, int value)
+{
+ struct rockchip_gpio_regs * const regs = vregs;
+
+ clrsetbits_le32(&regs->swport_dr, 1 << gpio, value << gpio);
+
+ /* Set direction */
+ clrsetbits_le32(&regs->swport_ddr, 1 << gpio, 1 << gpio);
+
+ return 0;
+}
+#endif /* CONFIG_SPL_BUILD */
+
+static int rockchip_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ struct ofnode_phandle_args args;
+ char *end;
+ int ret;
+
+ priv->regs = dev_read_addr_ptr(dev);
+
+ if (CONFIG_IS_ENABLED(PINCTRL)) {
+ ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * If "gpio-ranges" is present in the devicetree use it to parse
+ * the GPIO bank ID, otherwise use the legacy method.
+ */
+ ret = ofnode_parse_phandle_with_args(dev_ofnode(dev),
+ "gpio-ranges", NULL, 3,
+ 0, &args);
+ if (!ret || ret != -ENOENT) {
+ uc_priv->gpio_count = args.args[2];
+ priv->bank = args.args[1] / ROCKCHIP_GPIOS_PER_BANK;
+ } else {
+ uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK;
+ end = strrchr(dev->name, '@');
+ priv->bank = trailing_strtoln(dev->name, end);
+ }
+
+ priv->name[0] = 'A' + priv->bank;
+ uc_priv->bank_name = priv->name;
+
+ priv->version = readl(priv->regs + VER_ID_V2);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_rockchip_ops = {
+ .direction_input = rockchip_gpio_direction_input,
+ .direction_output = rockchip_gpio_direction_output,
+ .get_value = rockchip_gpio_get_value,
+ .set_value = rockchip_gpio_set_value,
+ .get_function = rockchip_gpio_get_function,
+};
+
+static const struct udevice_id rockchip_gpio_ids[] = {
+ { .compatible = "rockchip,gpio-bank" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_gpio_bank) = {
+ .name = "rockchip_gpio_bank",
+ .id = UCLASS_GPIO,
+ .of_match = rockchip_gpio_ids,
+ .ops = &gpio_rockchip_ops,
+ .priv_auto = sizeof(struct rockchip_gpio_priv),
+ .probe = rockchip_gpio_probe,
+};
diff --git a/drivers/gpio/rzg2l-gpio.c b/drivers/gpio/rzg2l-gpio.c
new file mode 100644
index 00000000000..2477af7874b
--- /dev/null
+++ b/drivers/gpio/rzg2l-gpio.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/G2L Pin Function Controller
+ *
+ * Copyright (C) 2021-2023 Renesas Electronics Corp.
+ */
+
+#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <renesas/rzg2l-pfc.h>
+
+static void rzg2l_gpio_set(const struct rzg2l_pfc_data *data, u32 port, u8 pin,
+ bool value)
+{
+ if (value)
+ setbits_8(data->base + P(port), BIT(pin));
+ else
+ clrbits_8(data->base + P(port), BIT(pin));
+}
+
+static int rzg2l_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+ u16 pm_state;
+
+ pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK;
+ switch (pm_state) {
+ case PM_INPUT:
+ return !!(readb(data->base + PIN(port)) & BIT(pin));
+ case PM_OUTPUT:
+ case PM_OUTPUT_IEN:
+ return !!(readb(data->base + P(port)) & BIT(pin));
+ default: /* PM_HIGH_Z */
+ return 0;
+ }
+}
+
+static int rzg2l_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+
+ rzg2l_gpio_set(data, port, pin, (bool)value);
+ return 0;
+}
+
+static void rzg2l_gpio_set_direction(const struct rzg2l_pfc_data *data,
+ u32 port, u8 pin, bool output)
+{
+ clrsetbits_le16(data->base + PM(port), PM_MASK << (pin * 2),
+ (output ? PM_OUTPUT : PM_INPUT) << (pin * 2));
+}
+
+static int rzg2l_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+
+ rzg2l_gpio_set_direction(data, port, pin, false);
+ return 0;
+}
+
+static int rzg2l_gpio_direction_output(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+
+ rzg2l_gpio_set(data, port, pin, (bool)value);
+ rzg2l_gpio_set_direction(data, port, pin, true);
+ return 0;
+}
+
+static int rzg2l_gpio_request(struct udevice *dev, unsigned int offset,
+ const char *label)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+
+ if (!rzg2l_port_validate(data, port, pin)) {
+ dev_err(dev, "Invalid GPIO %u:%u\n", port, pin);
+ return -EINVAL;
+ }
+
+ /* Select GPIO mode in PMC Register */
+ clrbits_8(data->base + PMC(port), BIT(pin));
+
+ return 0;
+}
+
+static int rzg2l_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ const struct rzg2l_pfc_data *data =
+ (const struct rzg2l_pfc_data *)dev_get_driver_data(dev);
+ const u32 port = RZG2L_PINMUX_TO_PORT(offset);
+ const u8 pin = RZG2L_PINMUX_TO_PIN(offset);
+ u16 pm_state;
+ u8 pmc_state;
+
+ if (!rzg2l_port_validate(data, port, pin)) {
+ /* This offset does not correspond to a valid GPIO pin. */
+ return -ENOENT;
+ }
+
+ /* Check if the pin is in GPIO or function mode. */
+ pmc_state = readb(data->base + PMC(port)) & BIT(pin);
+ if (pmc_state)
+ return GPIOF_FUNC;
+
+ /* Check the pin direction. */
+ pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK;
+ switch (pm_state) {
+ case PM_INPUT:
+ return GPIOF_INPUT;
+ case PM_OUTPUT:
+ case PM_OUTPUT_IEN:
+ return GPIOF_OUTPUT;
+ default: /* PM_HIGH_Z */
+ return GPIOF_UNUSED;
+ }
+}
+
+static const struct dm_gpio_ops rzg2l_gpio_ops = {
+ .direction_input = rzg2l_gpio_direction_input,
+ .direction_output = rzg2l_gpio_direction_output,
+ .get_value = rzg2l_gpio_get_value,
+ .set_value = rzg2l_gpio_set_value,
+ .request = rzg2l_gpio_request,
+ .get_function = rzg2l_gpio_get_function,
+};
+
+static int rzg2l_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct ofnode_phandle_args args;
+ int ret;
+
+ uc_priv->bank_name = "rzg2l-pfc-gpio";
+ ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges",
+ NULL, 3, 0, &args);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse gpio-ranges: %d\n", ret);
+ return -EINVAL;
+ }
+
+ uc_priv->gpio_count = args.args[2];
+ return rzg2l_pfc_enable(dev);
+}
+
+U_BOOT_DRIVER(rzg2l_pfc_gpio) = {
+ .name = "rzg2l-pfc-gpio",
+ .id = UCLASS_GPIO,
+ .ops = &rzg2l_gpio_ops,
+ .probe = rzg2l_gpio_probe,
+};
diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c
new file mode 100644
index 00000000000..06ed585f3d6
--- /dev/null
+++ b/drivers/gpio/s5p_gpio.c
@@ -0,0 +1,373 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define S5P_GPIO_GET_PIN(x) (x % GPIO_PER_BANK)
+
+#define CON_MASK(val) (0xf << ((val) << 2))
+#define CON_SFR(gpio, cfg) ((cfg) << ((gpio) << 2))
+#define CON_SFR_UNSHIFT(val, gpio) ((val) >> ((gpio) << 2))
+
+#define DAT_MASK(gpio) (0x1 << (gpio))
+#define DAT_SET(gpio) (0x1 << (gpio))
+
+#define PULL_MASK(gpio) (0x3 << ((gpio) << 1))
+#define PULL_MODE(gpio, pull) ((pull) << ((gpio) << 1))
+
+#define DRV_MASK(gpio) (0x3 << ((gpio) << 1))
+#define DRV_SET(gpio, mode) ((mode) << ((gpio) << 1))
+#define RATE_MASK(gpio) (0x1 << (gpio + 16))
+#define RATE_SET(gpio) (0x1 << (gpio + 16))
+
+/* Platform data for each bank */
+struct exynos_gpio_plat {
+ struct s5p_gpio_bank *bank;
+ const char *bank_name; /* Name of port, e.g. 'gpa0" */
+};
+
+/* Information about each bank at run-time */
+struct exynos_bank_info {
+ struct s5p_gpio_bank *bank;
+};
+
+static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned int gpio)
+{
+ const struct gpio_info *data;
+ unsigned int upto;
+ int i, count;
+
+ data = get_gpio_data();
+ count = get_bank_num();
+ upto = 0;
+
+ for (i = 0; i < count; i++) {
+ debug("i=%d, upto=%d\n", i, upto);
+ if (gpio < data->max_gpio) {
+ struct s5p_gpio_bank *bank;
+ bank = (struct s5p_gpio_bank *)data->reg_addr;
+ bank += (gpio - upto) / GPIO_PER_BANK;
+ debug("gpio=%d, bank=%p\n", gpio, bank);
+ return bank;
+ }
+
+ upto = data->max_gpio;
+ data++;
+ }
+
+ return NULL;
+}
+
+static void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
+{
+ unsigned int value;
+
+ value = readl(&bank->con);
+ value &= ~CON_MASK(gpio);
+ value |= CON_SFR(gpio, cfg);
+ writel(value, &bank->con);
+}
+
+static void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
+{
+ unsigned int value;
+
+ value = readl(&bank->dat);
+ value &= ~DAT_MASK(gpio);
+ if (en)
+ value |= DAT_SET(gpio);
+ writel(value, &bank->dat);
+}
+
+#ifdef CONFIG_SPL_BUILD
+/* Common GPIO API - SPL does not support driver model yet */
+int gpio_set_value(unsigned gpio, int value)
+{
+ s5p_gpio_set_value(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), value);
+
+ return 0;
+}
+#else
+static int s5p_gpio_get_cfg_pin(struct s5p_gpio_bank *bank, int gpio)
+{
+ unsigned int value;
+
+ value = readl(&bank->con);
+ value &= CON_MASK(gpio);
+ return CON_SFR_UNSHIFT(value, gpio);
+}
+
+static unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
+{
+ unsigned int value;
+
+ value = readl(&bank->dat);
+ return !!(value & DAT_MASK(gpio));
+}
+#endif /* CONFIG_SPL_BUILD */
+
+static void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->pull);
+ value &= ~PULL_MASK(gpio);
+
+ switch (mode) {
+ case S5P_GPIO_PULL_DOWN:
+ case S5P_GPIO_PULL_UP:
+ value |= PULL_MODE(gpio, mode);
+ break;
+ default:
+ break;
+ }
+
+ writel(value, &bank->pull);
+}
+
+static void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->drv);
+ value &= ~DRV_MASK(gpio);
+
+ switch (mode) {
+ case S5P_GPIO_DRV_1X:
+ case S5P_GPIO_DRV_2X:
+ case S5P_GPIO_DRV_3X:
+ case S5P_GPIO_DRV_4X:
+ value |= DRV_SET(gpio, mode);
+ break;
+ default:
+ return;
+ }
+
+ writel(value, &bank->drv);
+}
+
+static void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->drv);
+ value &= ~RATE_MASK(gpio);
+
+ switch (mode) {
+ case S5P_GPIO_DRV_FAST:
+ case S5P_GPIO_DRV_SLOW:
+ value |= RATE_SET(gpio);
+ break;
+ default:
+ return;
+ }
+
+ writel(value, &bank->drv);
+}
+
+int s5p_gpio_get_pin(unsigned gpio)
+{
+ return S5P_GPIO_GET_PIN(gpio);
+}
+
+/* Driver model interface */
+#ifndef CONFIG_SPL_BUILD
+/* set GPIO pin 'gpio' as an input */
+static int exynos_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct exynos_bank_info *state = dev_get_priv(dev);
+
+ /* Configure GPIO direction as input. */
+ s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_INPUT);
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int exynos_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct exynos_bank_info *state = dev_get_priv(dev);
+
+ /* Configure GPIO output value. */
+ s5p_gpio_set_value(state->bank, offset, value);
+
+ /* Configure GPIO direction as output. */
+ s5p_gpio_cfg_pin(state->bank, offset, S5P_GPIO_OUTPUT);
+
+ return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int exynos_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct exynos_bank_info *state = dev_get_priv(dev);
+
+ return s5p_gpio_get_value(state->bank, offset);
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int exynos_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct exynos_bank_info *state = dev_get_priv(dev);
+
+ s5p_gpio_set_value(state->bank, offset, value);
+
+ return 0;
+}
+#endif /* nCONFIG_SPL_BUILD */
+
+/*
+ * There is no common GPIO API for pull, drv, pin, rate (yet). These
+ * functions are kept here to preserve function ordering for review.
+ */
+void gpio_set_pull(int gpio, int mode)
+{
+ s5p_gpio_set_pull(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
+
+void gpio_set_drv(int gpio, int mode)
+{
+ s5p_gpio_set_drv(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
+
+void gpio_cfg_pin(int gpio, int cfg)
+{
+ s5p_gpio_cfg_pin(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), cfg);
+}
+
+void gpio_set_rate(int gpio, int mode)
+{
+ s5p_gpio_set_rate(s5p_gpio_get_bank(gpio),
+ s5p_gpio_get_pin(gpio), mode);
+}
+
+#ifndef CONFIG_SPL_BUILD
+static int exynos_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct exynos_bank_info *state = dev_get_priv(dev);
+ int cfg;
+
+ cfg = s5p_gpio_get_cfg_pin(state->bank, offset);
+ if (cfg == S5P_GPIO_OUTPUT)
+ return GPIOF_OUTPUT;
+ else if (cfg == S5P_GPIO_INPUT)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_FUNC;
+}
+
+static const struct dm_gpio_ops gpio_exynos_ops = {
+ .direction_input = exynos_gpio_direction_input,
+ .direction_output = exynos_gpio_direction_output,
+ .get_value = exynos_gpio_get_value,
+ .set_value = exynos_gpio_set_value,
+ .get_function = exynos_gpio_get_function,
+};
+
+static int gpio_exynos_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct exynos_bank_info *priv = dev_get_priv(dev);
+ struct exynos_gpio_plat *plat = dev_get_plat(dev);
+
+ /* Only child devices have ports */
+ if (!plat)
+ return 0;
+
+ priv->bank = plat->bank;
+
+ uc_priv->gpio_count = GPIO_PER_BANK;
+ uc_priv->bank_name = plat->bank_name;
+
+ return 0;
+}
+
+/**
+ * We have a top-level GPIO device with no actual GPIOs. It has a child
+ * device for each Exynos GPIO bank.
+ */
+static int gpio_exynos_bind(struct udevice *parent)
+{
+ struct exynos_gpio_plat *plat = dev_get_plat(parent);
+ struct s5p_gpio_bank *bank, *base;
+ const void *blob = gd->fdt_blob;
+ int node;
+
+ /* If this is a child device, there is nothing to do here */
+ if (plat)
+ return 0;
+
+ base = dev_read_addr_ptr(parent);
+ for (node = fdt_first_subnode(blob, dev_of_offset(parent)), bank = base;
+ node > 0;
+ node = fdt_next_subnode(blob, node), bank++) {
+ struct exynos_gpio_plat *plat;
+ struct udevice *dev;
+ fdt_addr_t reg;
+ int ret;
+
+ if (!fdtdec_get_bool(blob, node, "gpio-controller"))
+ continue;
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+
+ plat->bank_name = fdt_get_name(blob, node, NULL);
+ ret = device_bind(parent, parent->driver, plat->bank_name, plat,
+ offset_to_ofnode(node), &dev);
+ if (ret)
+ return ret;
+
+ reg = dev_read_addr(dev);
+ if (reg != FDT_ADDR_T_NONE)
+ bank = (struct s5p_gpio_bank *)((ulong)base + reg);
+
+ plat->bank = bank;
+
+ debug("dev at %p: %s\n", bank, plat->bank_name);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id exynos_gpio_ids[] = {
+ { .compatible = "samsung,s5pc100-pinctrl" },
+ { .compatible = "samsung,s5pc110-pinctrl" },
+ { .compatible = "samsung,exynos4210-pinctrl" },
+ { .compatible = "samsung,exynos4x12-pinctrl" },
+ { .compatible = "samsung,exynos5250-pinctrl" },
+ { .compatible = "samsung,exynos5420-pinctrl" },
+ { .compatible = "samsung,exynos78x0-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_exynos) = {
+ .name = "gpio_exynos",
+ .id = UCLASS_GPIO,
+ .of_match = exynos_gpio_ids,
+ .bind = gpio_exynos_bind,
+ .probe = gpio_exynos_probe,
+ .priv_auto = sizeof(struct exynos_bank_info),
+ .ops = &gpio_exynos_ops,
+};
+#endif
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
new file mode 100644
index 00000000000..305f9a6ff62
--- /dev/null
+++ b/drivers/gpio/sandbox.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <acpi/acpi_device.h>
+#include <asm/gpio.h>
+#include <dm/acpi.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <dm/of.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/gpio/sandbox-gpio.h>
+
+struct gpio_state {
+ const char *label; /* label given by requester */
+ ulong flags; /* flags (GPIOD_...) */
+};
+
+/* Access routines for GPIO info */
+static struct gpio_state *get_gpio_state(struct udevice *dev, uint offset)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct gpio_state *state = dev_get_priv(dev);
+
+ if (offset >= uc_priv->gpio_count) {
+ printf("sandbox_gpio: error: invalid gpio %u\n", offset);
+ return NULL;
+ }
+
+ return &state[offset];
+}
+
+/* Access routines for GPIO flags */
+static ulong *get_gpio_flags(struct udevice *dev, unsigned int offset)
+{
+ struct gpio_state *state = get_gpio_state(dev, offset);
+
+ if (!state)
+ return NULL;
+
+ return &state->flags;
+
+}
+
+static int get_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag)
+{
+ return (*get_gpio_flags(dev, offset) & flag) != 0;
+}
+
+static int set_gpio_flag(struct udevice *dev, unsigned int offset, ulong flag,
+ int value)
+{
+ struct gpio_state *state = get_gpio_state(dev, offset);
+
+ if (value)
+ state->flags |= flag;
+ else
+ state->flags &= ~flag;
+
+ return 0;
+}
+
+/*
+ * Back-channel sandbox-internal-only access to GPIO state
+ */
+
+int sandbox_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct gpio_state *state = get_gpio_state(dev, offset);
+ bool val;
+
+ if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
+ debug("sandbox_gpio: get_value on output gpio %u\n", offset);
+
+ if (state->flags & GPIOD_EXT_DRIVEN) {
+ val = state->flags & GPIOD_EXT_HIGH;
+ } else {
+ if (state->flags & GPIOD_EXT_PULL_UP)
+ val = true;
+ else if (state->flags & GPIOD_EXT_PULL_DOWN)
+ val = false;
+ else
+ val = state->flags & GPIOD_PULL_UP;
+ }
+
+ return val;
+}
+
+int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+ set_gpio_flag(dev, offset, GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+
+ return 0;
+}
+
+int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
+{
+ return get_gpio_flag(dev, offset, GPIOD_IS_OUT);
+}
+
+int sandbox_gpio_set_direction(struct udevice *dev, unsigned offset, int output)
+{
+ set_gpio_flag(dev, offset, GPIOD_IS_OUT, output);
+ set_gpio_flag(dev, offset, GPIOD_IS_IN, !output);
+
+ return 0;
+}
+
+ulong sandbox_gpio_get_flags(struct udevice *dev, uint offset)
+{
+ ulong flags = *get_gpio_flags(dev, offset);
+
+ return flags & ~GPIOD_SANDBOX_MASK;
+}
+
+int sandbox_gpio_set_flags(struct udevice *dev, uint offset, ulong flags)
+{
+ struct gpio_state *state = get_gpio_state(dev, offset);
+
+ state->flags = flags;
+
+ return 0;
+}
+
+/*
+ * These functions implement the public interface within U-Boot
+ */
+
+/* set GPIO port 'offset' as an input */
+static int sb_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ debug("%s: offset:%u\n", __func__, offset);
+
+ return sandbox_gpio_set_direction(dev, offset, 0);
+}
+
+/* set GPIO port 'offset' as an output, with polarity 'value' */
+static int sb_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ int ret;
+
+ debug("%s: offset:%u, value = %d\n", __func__, offset, value);
+
+ ret = sandbox_gpio_set_direction(dev, offset, 1);
+ if (ret)
+ return ret;
+ ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
+ GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* read GPIO IN value of port 'offset' */
+static int sb_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ debug("%s: offset:%u\n", __func__, offset);
+
+ return sandbox_gpio_get_value(dev, offset);
+}
+
+/* write GPIO OUT value to port 'offset' */
+static int sb_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+ int ret;
+
+ debug("%s: offset:%u, value = %d\n", __func__, offset, value);
+
+ if (!sandbox_gpio_get_direction(dev, offset)) {
+ printf("sandbox_gpio: error: set_value on input gpio %u\n",
+ offset);
+ return -1;
+ }
+
+ ret = set_gpio_flag(dev, offset, GPIOD_IS_OUT_ACTIVE |
+ GPIOD_EXT_DRIVEN | GPIOD_EXT_HIGH, value);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ if (get_gpio_flag(dev, offset, GPIOD_IS_OUT))
+ return GPIOF_OUTPUT;
+ if (get_gpio_flag(dev, offset, GPIOD_IS_IN))
+ return GPIOF_INPUT;
+ if (get_gpio_flag(dev, offset, GPIOD_IS_AF))
+ return GPIOF_FUNC;
+
+ return GPIOF_INPUT; /*GPIO is not configurated */
+}
+
+static int sb_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ if (args->args_count < 2)
+ return 0;
+ /* treat generic binding with gpio uclass */
+ gpio_xlate_offs_flags(dev, desc, args);
+
+ /* sandbox test specific, not defined in gpio.h */
+ if (args->args[1] & GPIO_IN)
+ desc->flags |= GPIOD_IS_IN;
+
+ if (args->args[1] & GPIO_OUT)
+ desc->flags |= GPIOD_IS_OUT;
+
+ if (args->args[1] & GPIO_OUT_ACTIVE)
+ desc->flags |= GPIOD_IS_OUT_ACTIVE;
+
+ if (args->args[1] & GPIO_AF)
+ desc->flags |= GPIOD_IS_AF;
+
+ return 0;
+}
+
+static int sb_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ debug("%s: offset:%u, flags = %lx\n", __func__, offset, flags);
+ struct gpio_state *state = get_gpio_state(dev, offset);
+
+ if (flags & GPIOD_IS_OUT) {
+ flags |= GPIOD_EXT_DRIVEN;
+ if (flags & GPIOD_IS_OUT_ACTIVE)
+ flags |= GPIOD_EXT_HIGH;
+ else
+ flags &= ~GPIOD_EXT_HIGH;
+ } else {
+ flags |= state->flags & GPIOD_SANDBOX_MASK;
+ }
+ state->flags = flags;
+
+ return 0;
+}
+
+static int sb_gpio_get_flags(struct udevice *dev, uint offset, ulong *flagsp)
+{
+ debug("%s: offset:%u\n", __func__, offset);
+ *flagsp = *get_gpio_flags(dev, offset) & ~GPIOD_SANDBOX_MASK;
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_gpio_get_acpi(const struct gpio_desc *desc,
+ struct acpi_gpio *gpio)
+{
+ int ret;
+
+ /* Note that gpio_get_acpi() zeroes *gpio before calling here */
+ gpio->pin_count = 1;
+ gpio->pins[0] = desc->offset;
+ ret = acpi_device_scope(desc->dev, gpio->resource,
+ sizeof(gpio->resource));
+ if (ret)
+ return log_ret(ret);
+
+ /* All of these values are just used for testing */
+ if (desc->flags & GPIOD_ACTIVE_LOW) {
+ gpio->pin0_addr = 0x80012 + desc->offset;
+ gpio->type = ACPI_GPIO_TYPE_INTERRUPT;
+ gpio->pull = ACPI_GPIO_PULL_DOWN;
+ gpio->interrupt_debounce_timeout = 4321;
+
+ /* We use the GpioInt part */
+ gpio->irq.pin = desc->offset;
+ gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
+ gpio->irq.shared = ACPI_IRQ_SHARED;
+ gpio->irq.wake = ACPI_IRQ_WAKE;
+
+ /* The GpioIo part is only used for testing */
+ gpio->polarity = ACPI_GPIO_ACTIVE_LOW;
+ } else {
+ gpio->pin0_addr = 0xc00dc + desc->offset;
+ gpio->type = ACPI_GPIO_TYPE_IO;
+ gpio->pull = ACPI_GPIO_PULL_UP;
+ gpio->interrupt_debounce_timeout = 0;
+
+ /* The GpioInt part is not used */
+
+ /* We use the GpioIo part */
+ gpio->output_drive_strength = 1234;
+ gpio->io_shared = true;
+ gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT;
+ gpio->polarity = 0;
+ }
+
+ return 0;
+}
+
+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 = {
+ .get_name = sb_gpio_get_name,
+};
+#endif /* ACPIGEN */
+
+static const struct dm_gpio_ops gpio_sandbox_ops = {
+ .direction_input = sb_gpio_direction_input,
+ .direction_output = sb_gpio_direction_output,
+ .get_value = sb_gpio_get_value,
+ .set_value = sb_gpio_set_value,
+ .get_function = sb_gpio_get_function,
+ .xlate = sb_gpio_xlate,
+ .set_flags = sb_gpio_set_flags,
+ .get_flags = sb_gpio_get_flags,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+ .get_acpi = sb_gpio_get_acpi,
+#endif
+};
+
+static int sandbox_gpio_of_to_plat(struct udevice *dev)
+{
+ if (CONFIG_IS_ENABLED(OF_REAL)) {
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count =
+ dev_read_u32_default(dev, "sandbox,gpio-count", 0);
+ uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name");
+ }
+
+ return 0;
+}
+
+static int gpio_sandbox_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (!dev_has_ofnode(dev))
+ /* Tell the uclass how many GPIOs we have */
+ uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT;
+
+ dev_set_priv(dev,
+ calloc(sizeof(struct gpio_state), uc_priv->gpio_count));
+
+ return 0;
+}
+
+static int gpio_sandbox_remove(struct udevice *dev)
+{
+ free(dev_get_priv(dev));
+
+ return 0;
+}
+
+static const struct udevice_id sandbox_gpio_ids[] = {
+ { .compatible = "sandbox,gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_gpio) = {
+ .name = "sandbox_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = sandbox_gpio_ids,
+ .of_to_plat = sandbox_gpio_of_to_plat,
+ .probe = gpio_sandbox_probe,
+ .remove = gpio_sandbox_remove,
+ .ops = &gpio_sandbox_ops,
+ ACPI_OPS_PTR(&gpio_sandbox_acpi_ops)
+};
+
+DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias)
+
+#if CONFIG_IS_ENABLED(PINCTRL)
+
+/* pincontrol: used only to check GPIO pin configuration (pinmux command) */
+
+struct sb_pinctrl_priv {
+ int pinctrl_ngpios;
+ struct list_head gpio_dev;
+};
+
+struct sb_gpio_bank {
+ struct udevice *gpio_dev;
+ struct list_head list;
+};
+
+static int sb_populate_gpio_dev_list(struct udevice *dev)
+{
+ struct sb_pinctrl_priv *priv = dev_get_priv(dev);
+ struct udevice *gpio_dev;
+ struct udevice *child;
+ struct sb_gpio_bank *gpio_bank;
+ int ret;
+
+ /*
+ * parse pin-controller sub-nodes (ie gpio bank nodes) and fill
+ * a list with all gpio device reference which belongs to the
+ * current pin-controller. This list is used to find pin_name and
+ * pin muxing
+ */
+ list_for_each_entry(child, &dev->child_head, sibling_node) {
+ ret = uclass_get_device_by_name(UCLASS_GPIO, child->name,
+ &gpio_dev);
+ if (ret < 0)
+ continue;
+
+ gpio_bank = malloc(sizeof(*gpio_bank));
+ if (!gpio_bank) {
+ dev_err(dev, "Not enough memory\n");
+ return -ENOMEM;
+ }
+
+ gpio_bank->gpio_dev = gpio_dev;
+ list_add_tail(&gpio_bank->list, &priv->gpio_dev);
+ }
+
+ return 0;
+}
+
+static int sb_pinctrl_get_pins_count(struct udevice *dev)
+{
+ struct sb_pinctrl_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv;
+ struct sb_gpio_bank *gpio_bank;
+
+ /*
+ * if get_pins_count has already been executed once on this
+ * pin-controller, no need to run it again
+ */
+ if (priv->pinctrl_ngpios)
+ return priv->pinctrl_ngpios;
+
+ if (list_empty(&priv->gpio_dev))
+ sb_populate_gpio_dev_list(dev);
+ /*
+ * walk through all banks to retrieve the pin-controller
+ * pins number
+ */
+ list_for_each_entry(gpio_bank, &priv->gpio_dev, list) {
+ uc_priv = dev_get_uclass_priv(gpio_bank->gpio_dev);
+
+ priv->pinctrl_ngpios += uc_priv->gpio_count;
+ }
+
+ return priv->pinctrl_ngpios;
+}
+
+static struct udevice *sb_pinctrl_get_gpio_dev(struct udevice *dev,
+ unsigned int selector,
+ unsigned int *idx)
+{
+ struct sb_pinctrl_priv *priv = dev_get_priv(dev);
+ struct sb_gpio_bank *gpio_bank;
+ struct gpio_dev_priv *uc_priv;
+ int pin_count = 0;
+
+ if (list_empty(&priv->gpio_dev))
+ sb_populate_gpio_dev_list(dev);
+
+ /* look up for the bank which owns the requested pin */
+ list_for_each_entry(gpio_bank, &priv->gpio_dev, list) {
+ uc_priv = dev_get_uclass_priv(gpio_bank->gpio_dev);
+
+ if (selector < (pin_count + uc_priv->gpio_count)) {
+ /*
+ * we found the bank, convert pin selector to
+ * gpio bank index
+ */
+ *idx = selector - pin_count;
+
+ return gpio_bank->gpio_dev;
+ }
+ pin_count += uc_priv->gpio_count;
+ }
+
+ return NULL;
+}
+
+static const char *sb_pinctrl_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct gpio_dev_priv *uc_priv;
+ struct udevice *gpio_dev;
+ unsigned int gpio_idx;
+ static char pin_name[PINNAME_SIZE];
+
+ /* look up for the bank which owns the requested pin */
+ gpio_dev = sb_pinctrl_get_gpio_dev(dev, selector, &gpio_idx);
+ if (!gpio_dev) {
+ snprintf(pin_name, PINNAME_SIZE, "Error");
+ } else {
+ uc_priv = dev_get_uclass_priv(gpio_dev);
+
+ snprintf(pin_name, PINNAME_SIZE, "%s%d",
+ uc_priv->bank_name,
+ gpio_idx);
+ }
+
+ return pin_name;
+}
+
+static char *get_flags_string(ulong flags)
+{
+ if (flags & GPIOD_OPEN_DRAIN)
+ return "drive-open-drain";
+ if (flags & GPIOD_OPEN_SOURCE)
+ return "drive-open-source";
+ if (flags & GPIOD_PULL_UP)
+ return "bias-pull-up";
+ if (flags & GPIOD_PULL_DOWN)
+ return "bias-pull-down";
+ return ".";
+}
+
+static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
+ unsigned int selector,
+ char *buf, int size)
+{
+ struct udevice *gpio_dev;
+ unsigned int gpio_idx;
+ ulong flags;
+ int function;
+
+ /* look up for the bank which owns the requested pin */
+ gpio_dev = sb_pinctrl_get_gpio_dev(dev, selector, &gpio_idx);
+ if (!gpio_dev) {
+ snprintf(buf, size, "Error");
+ } else {
+ function = sb_gpio_get_function(gpio_dev, gpio_idx);
+ flags = *get_gpio_flags(gpio_dev, gpio_idx);
+
+ snprintf(buf, size, "gpio %s %s",
+ function == GPIOF_OUTPUT ? "output" : "input",
+ get_flags_string(flags));
+ }
+
+ return 0;
+}
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name)
+{
+ return acpi_copy_name(out_name, "PINC");
+}
+#endif
+
+static int sandbox_pinctrl_probe(struct udevice *dev)
+{
+ struct sb_pinctrl_priv *priv = dev_get_priv(dev);
+
+ INIT_LIST_HEAD(&priv->gpio_dev);
+
+ return 0;
+}
+
+static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
+ .get_pin_name = sb_pinctrl_get_pin_name,
+ .get_pins_count = sb_pinctrl_get_pins_count,
+ .get_pin_muxing = sb_pinctrl_get_pin_muxing,
+};
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+struct acpi_ops pinctrl_sandbox_acpi_ops = {
+ .get_name = sb_pinctrl_get_name,
+};
+#endif
+
+static const struct udevice_id sandbox_pinctrl_gpio_match[] = {
+ { .compatible = "sandbox,pinctrl-gpio" },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sandbox_pinctrl_gpio) = {
+ .name = "sandbox_pinctrl_gpio",
+ .id = UCLASS_PINCTRL,
+ .of_match = sandbox_pinctrl_gpio_match,
+ .ops = &sandbox_pinctrl_gpio_ops,
+ .bind = dm_scan_fdt_dev,
+ .probe = sandbox_pinctrl_probe,
+ .priv_auto = sizeof(struct sb_pinctrl_priv),
+ ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops)
+};
+
+#endif /* PINCTRL */
diff --git a/drivers/gpio/sandbox_test.c b/drivers/gpio/sandbox_test.c
new file mode 100644
index 00000000000..c76e1997419
--- /dev/null
+++ b/drivers/gpio/sandbox_test.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sandbox driver for testing GPIOs with of-platdata
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm-generic/gpio.h>
+
+static const struct udevice_id sandbox_gpio_test_ids[] = {
+ { .compatible = "sandbox,gpio-test" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_gpio_test) = {
+ .name = "sandbox_gpio_test",
+ .id = UCLASS_MISC,
+ .of_match = sandbox_gpio_test_ids,
+};
diff --git a/drivers/gpio/sh_pfc.c b/drivers/gpio/sh_pfc.c
new file mode 100644
index 00000000000..2495d6c1c15
--- /dev/null
+++ b/drivers/gpio/sh_pfc.c
@@ -0,0 +1,639 @@
+/*
+ * Pinmuxed GPIO support for SuperH.
+ * Copy from linux kernel driver/sh/pfc.c
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <common.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <sh_pfc.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+
+static struct pinmux_info *gpioc;
+
+#define pfc_phys_to_virt(p, a) ((void *)a)
+
+static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r)
+{
+ if (enum_id < r->begin)
+ return 0;
+
+ if (enum_id > r->end)
+ return 0;
+
+ return 1;
+}
+
+static unsigned long gpio_read_raw_reg(void *mapped_reg,
+ unsigned long reg_width)
+{
+ switch (reg_width) {
+
+ case 8:
+ return readb(mapped_reg);
+ case 16:
+ return readw(mapped_reg);
+ case 32:
+ return readl(mapped_reg);
+ }
+
+ BUG();
+ return 0;
+}
+
+static void gpio_write_raw_reg(void *mapped_reg,
+ unsigned long reg_width,
+ unsigned long data)
+{
+ switch (reg_width) {
+ case 8:
+ writeb(data, mapped_reg);
+ return;
+ case 16:
+ writew(data, mapped_reg);
+ return;
+ case 32:
+ writel(data, mapped_reg);
+ return;
+ }
+
+ BUG();
+}
+
+static int gpio_read_bit(struct pinmux_data_reg *dr,
+ unsigned long offset,
+ unsigned long in_pos)
+{
+ unsigned long pos;
+
+ pos = dr->reg_width - (in_pos + 1);
+
+ debug("read_bit: addr = %lx, pos = %ld, r_width = %ld\n",
+ dr->reg + offset, pos, dr->reg_width);
+
+ return (gpio_read_raw_reg(dr->mapped_reg + offset,
+ dr->reg_width) >> pos) & 1;
+}
+
+static void gpio_write_bit(struct pinmux_data_reg *dr,
+ unsigned long in_pos, unsigned long value)
+{
+ unsigned long pos;
+
+ pos = dr->reg_width - (in_pos + 1);
+
+ debug("write_bit addr = %lx, value = %d, pos = %ld, "
+ "r_width = %ld\n",
+ dr->reg, !!value, pos, dr->reg_width);
+
+ if (value)
+ __set_bit(pos, &dr->reg_shadow);
+ else
+ __clear_bit(pos, &dr->reg_shadow);
+
+ gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow);
+}
+
+static void config_reg_helper(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ unsigned long in_pos,
+#if 0
+ void __iomem **mapped_regp,
+#else
+ void **mapped_regp,
+#endif
+ unsigned long *maskp,
+ unsigned long *posp)
+{
+ int k;
+
+ *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg);
+
+ if (crp->field_width) {
+ *maskp = (1 << crp->field_width) - 1;
+ *posp = crp->reg_width - ((in_pos + 1) * crp->field_width);
+ } else {
+ *maskp = (1 << crp->var_field_width[in_pos]) - 1;
+ *posp = crp->reg_width;
+ for (k = 0; k <= in_pos; k++)
+ *posp -= abs(crp->var_field_width[k]);
+ }
+}
+
+static int read_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ unsigned long field)
+{
+ void *mapped_reg;
+
+ unsigned long mask, pos;
+
+ config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos);
+
+ debug("read_reg: addr = %lx, field = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ crp->reg, field, crp->reg_width, crp->field_width);
+
+ return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask;
+}
+
+static void write_config_reg(struct pinmux_info *gpioc,
+ struct pinmux_cfg_reg *crp,
+ unsigned long field, unsigned long value)
+{
+ void *mapped_reg;
+ unsigned long mask, pos, data;
+
+ config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos);
+
+ debug("write_reg addr = %lx, value = %ld, field = %ld, "
+ "r_width = %ld, f_width = %ld\n",
+ crp->reg, value, field, crp->reg_width, crp->field_width);
+
+ mask = ~(mask << pos);
+ value = value << pos;
+
+ data = gpio_read_raw_reg(mapped_reg, crp->reg_width);
+ data &= mask;
+ data |= value;
+
+ if (gpioc->unlock_reg)
+ gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg),
+ 32, ~data);
+
+ gpio_write_raw_reg(mapped_reg, crp->reg_width, data);
+}
+
+static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ struct pinmux_data_reg *data_reg;
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = 0;
+ while (1) {
+ data_reg = gpioc->data_regs + k;
+
+ if (!data_reg->reg_width)
+ break;
+
+ data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg);
+
+ for (n = 0; n < data_reg->reg_width; n++) {
+ if (data_reg->enum_ids[n] == gpiop->enum_id) {
+ gpiop->flags &= ~PINMUX_FLAG_DREG;
+ gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT);
+ gpiop->flags &= ~PINMUX_FLAG_DBIT;
+ gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT);
+ return 0;
+ }
+ }
+ k++;
+ }
+
+ BUG();
+
+ return -1;
+}
+
+static void setup_data_regs(struct pinmux_info *gpioc)
+{
+ struct pinmux_data_reg *drp;
+ int k;
+
+ for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++)
+ setup_data_reg(gpioc, k);
+
+ k = 0;
+ while (1) {
+ drp = gpioc->data_regs + k;
+
+ if (!drp->reg_width)
+ break;
+
+ drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg,
+ drp->reg_width);
+ k++;
+ }
+}
+
+static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
+ struct pinmux_data_reg **drp, int *bitp)
+{
+ struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
+ int k, n;
+
+ if (!enum_in_range(gpiop->enum_id, &gpioc->data))
+ return -1;
+
+ k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
+ n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
+ *drp = gpioc->data_regs + k;
+ *bitp = n;
+ return 0;
+}
+
+static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
+ struct pinmux_cfg_reg **crp,
+ int *fieldp, int *valuep,
+ unsigned long **cntp)
+{
+ struct pinmux_cfg_reg *config_reg;
+ unsigned long r_width, f_width, curr_width, ncomb;
+ int k, m, n, pos, bit_pos;
+
+ k = 0;
+ while (1) {
+ config_reg = gpioc->cfg_regs + k;
+
+ r_width = config_reg->reg_width;
+ f_width = config_reg->field_width;
+
+ if (!r_width)
+ break;
+
+ pos = 0;
+ m = 0;
+ for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) {
+ if (f_width)
+ curr_width = f_width;
+ else
+ curr_width = config_reg->var_field_width[m];
+
+ ncomb = 1 << curr_width;
+ for (n = 0; n < ncomb; n++) {
+ if (config_reg->enum_ids[pos + n] == enum_id) {
+ *crp = config_reg;
+ *fieldp = m;
+ *valuep = n;
+ *cntp = &config_reg->cnt[m];
+ return 0;
+ }
+ }
+ pos += ncomb;
+ m++;
+ }
+ k++;
+ }
+
+ return -1;
+}
+
+static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
+ int pos, pinmux_enum_t *enum_idp)
+{
+ pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id;
+ pinmux_enum_t *data = gpioc->gpio_data;
+ int k;
+
+ if (!enum_in_range(enum_id, &gpioc->data)) {
+ if (!enum_in_range(enum_id, &gpioc->mark)) {
+ debug("non data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+ }
+ }
+
+ if (pos) {
+ *enum_idp = data[pos + 1];
+ return pos + 1;
+ }
+
+ for (k = 0; k < gpioc->gpio_data_size; k++) {
+ if (data[k] == enum_id) {
+ *enum_idp = data[k + 1];
+ return k + 1;
+ }
+ }
+
+ debug("cannot locate data/mark enum_id for gpio %d\n", gpio);
+ return -1;
+}
+
+enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE };
+
+static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
+ int pinmux_type, int cfg_mode)
+{
+ struct pinmux_cfg_reg *cr = NULL;
+ pinmux_enum_t enum_id;
+ struct pinmux_range *range;
+ int in_range, pos, field, value;
+ unsigned long *cntp;
+
+ switch (pinmux_type) {
+
+ case PINMUX_TYPE_FUNCTION:
+ range = NULL;
+ break;
+
+ case PINMUX_TYPE_OUTPUT:
+ range = &gpioc->output;
+ break;
+
+ case PINMUX_TYPE_INPUT:
+ range = &gpioc->input;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLUP:
+ range = &gpioc->input_pu;
+ break;
+
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ range = &gpioc->input_pd;
+ break;
+
+ default:
+ goto out_err;
+ }
+
+ pos = 0;
+ enum_id = 0;
+ field = 0;
+ value = 0;
+ while (1) {
+ pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id);
+ if (pos <= 0)
+ goto out_err;
+
+ if (!enum_id)
+ break;
+
+ /* first check if this is a function enum */
+ in_range = enum_in_range(enum_id, &gpioc->function);
+ if (!in_range) {
+ /* not a function enum */
+ if (range) {
+ /*
+ * other range exists, so this pin is
+ * a regular GPIO pin that now is being
+ * bound to a specific direction.
+ *
+ * for this case we only allow function enums
+ * and the enums that match the other range.
+ */
+ in_range = enum_in_range(enum_id, range);
+
+ /*
+ * special case pass through for fixed
+ * input-only or output-only pins without
+ * function enum register association.
+ */
+ if (in_range && enum_id == range->force)
+ continue;
+ } else {
+ /*
+ * no other range exists, so this pin
+ * must then be of the function type.
+ *
+ * allow function type pins to select
+ * any combination of function/in/out
+ * in their MARK lists.
+ */
+ in_range = 1;
+ }
+ }
+
+ if (!in_range)
+ continue;
+
+ if (get_config_reg(gpioc, enum_id, &cr,
+ &field, &value, &cntp) != 0)
+ goto out_err;
+
+ switch (cfg_mode) {
+ case GPIO_CFG_DRYRUN:
+ if (!*cntp ||
+ (read_config_reg(gpioc, cr, field) != value))
+ continue;
+ break;
+
+ case GPIO_CFG_REQ:
+ write_config_reg(gpioc, cr, field, value);
+ *cntp = *cntp + 1;
+ break;
+
+ case GPIO_CFG_FREE:
+ *cntp = *cntp - 1;
+ break;
+ }
+ }
+
+ return 0;
+ out_err:
+ return -1;
+}
+
+#if 0
+static DEFINE_SPINLOCK(gpio_lock);
+static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pinmux_info, chip);
+}
+#endif
+
+static int sh_gpio_request(unsigned offset)
+{
+ struct pinmux_data_reg *dummy;
+ int i, ret, pinmux_type;
+
+ ret = -1;
+
+ if (!gpioc)
+ goto err_out;
+
+ if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
+ goto err_out;
+
+ /* setup pin function here if no data is associated with pin */
+
+ if (get_data_reg(gpioc, offset, &dummy, &i) != 0)
+ pinmux_type = PINMUX_TYPE_FUNCTION;
+ else
+ pinmux_type = PINMUX_TYPE_GPIO;
+
+ if (pinmux_type == PINMUX_TYPE_FUNCTION) {
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_out;
+
+ if (pinmux_config_gpio(gpioc, offset,
+ pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+ }
+
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= pinmux_type;
+
+ ret = 0;
+err_out:
+ return ret;
+}
+
+static void sh_gpio_free(unsigned offset)
+{
+ int pinmux_type;
+
+ if (!gpioc)
+ return;
+
+ pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE;
+ pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE);
+ gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE;
+}
+
+static int pinmux_direction(struct pinmux_info *gpioc,
+ unsigned gpio, int new_pinmux_type)
+{
+ int pinmux_type;
+ int ret = -1;
+
+ if (!gpioc)
+ goto err_out;
+
+ pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
+
+ switch (pinmux_type) {
+ case PINMUX_TYPE_GPIO:
+ break;
+ case PINMUX_TYPE_OUTPUT:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_INPUT_PULLUP:
+ case PINMUX_TYPE_INPUT_PULLDOWN:
+ pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE);
+ break;
+ default:
+ goto err_out;
+ }
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_DRYRUN) != 0)
+ goto err_out;
+
+ if (pinmux_config_gpio(gpioc, gpio,
+ new_pinmux_type,
+ GPIO_CFG_REQ) != 0)
+ BUG();
+
+ gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE;
+ gpioc->gpios[gpio].flags |= new_pinmux_type;
+
+ ret = 0;
+ err_out:
+ return ret;
+}
+
+static int sh_gpio_direction_input(unsigned offset)
+{
+ return pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT);
+}
+
+static void sh_gpio_set_value(struct pinmux_info *gpioc,
+ unsigned gpio, int value)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ BUG();
+ else
+ gpio_write_bit(dr, bit, value);
+}
+
+static int sh_gpio_direction_output(unsigned offset, int value)
+{
+ sh_gpio_set_value(gpioc, offset, value);
+ return pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT);
+}
+
+static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
+{
+ struct pinmux_data_reg *dr = NULL;
+ int bit = 0, offset = 0;
+
+ if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
+ return -1;
+
+ if (IS_ENABLED(CONFIG_RCAR_64) &&
+ ((gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE) == PINMUX_TYPE_INPUT))
+ offset += 4;
+
+ return gpio_read_bit(dr, offset, bit);
+}
+
+static int sh_gpio_get(unsigned offset)
+{
+ return sh_gpio_get_value(gpioc, offset);
+}
+
+static void sh_gpio_set(unsigned offset, int value)
+{
+ sh_gpio_set_value(gpioc, offset, value);
+}
+
+int register_pinmux(struct pinmux_info *pip)
+{
+ if (pip != NULL) {
+ gpioc = pip;
+ debug("%s deregistering\n", pip->name);
+ setup_data_regs(gpioc);
+ }
+ return 0;
+}
+
+int unregister_pinmux(struct pinmux_info *pip)
+{
+ debug("%s deregistering\n", pip->name);
+ if (gpioc != pip)
+ return -1;
+
+ gpioc = NULL;
+ return 0;
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ sh_gpio_request(gpio);
+ return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+ sh_gpio_free(gpio);
+ return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ return sh_gpio_direction_input(gpio);
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ return sh_gpio_direction_output(gpio, value);
+}
+
+void gpio_set_value(unsigned gpio, int value)
+{
+ sh_gpio_set(gpio, value);
+}
+
+int gpio_get_value(unsigned gpio)
+{
+ return sh_gpio_get(gpio);
+}
diff --git a/drivers/gpio/sifive-gpio.c b/drivers/gpio/sifive-gpio.c
new file mode 100644
index 00000000000..151f484e8fd
--- /dev/null
+++ b/drivers/gpio/sifive-gpio.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SiFive GPIO driver
+ *
+ * Copyright (C) 2019 SiFive, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/arch/gpio.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+
+static int sifive_gpio_probe(struct udevice *dev)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char name[18], *str;
+
+ sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+
+ /*
+ * Use the gpio count mentioned in device tree,
+ * if not specified in dt, set NR_GPIOS as default
+ */
+ uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", NR_GPIOS);
+
+ return 0;
+}
+
+static void sifive_update_gpio_reg(void *bptr, u32 offset, bool value)
+{
+ void __iomem *ptr = (void __iomem *)bptr;
+
+ u32 bit = BIT(offset);
+ u32 old = readl(ptr);
+
+ if (value)
+ writel(old | bit, ptr);
+ else
+ writel(old & ~bit, ptr);
+}
+
+static int sifive_gpio_direction_input(struct udevice *dev, u32 offset)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (offset > uc_priv->gpio_count)
+ return -EINVAL;
+
+ /* Configure gpio direction as input */
+ sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN, offset, true);
+ sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, false);
+
+ return 0;
+}
+
+static int sifive_gpio_direction_output(struct udevice *dev, u32 offset,
+ int value)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (offset > uc_priv->gpio_count)
+ return -EINVAL;
+
+ /* Configure gpio direction as output */
+ sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, true);
+ sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN, offset, false);
+
+ /* Set the output state of the pin */
+ sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
+
+ return 0;
+}
+
+static int sifive_gpio_get_value(struct udevice *dev, u32 offset)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int val;
+ int dir;
+
+ if (offset > uc_priv->gpio_count)
+ return -EINVAL;
+
+ /* Get direction of the pin */
+ dir = !(readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset));
+
+ if (dir)
+ val = readl(plat->base + GPIO_INPUT_VAL) & BIT(offset);
+ else
+ val = readl(plat->base + GPIO_OUTPUT_VAL) & BIT(offset);
+
+ return val ? HIGH : LOW;
+}
+
+static int sifive_gpio_set_value(struct udevice *dev, u32 offset, int value)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (offset > uc_priv->gpio_count)
+ return -EINVAL;
+
+ sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
+
+ return 0;
+}
+
+static int sifive_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+ u32 outdir, indir, val;
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (offset > uc_priv->gpio_count)
+ return -1;
+
+ /* Get direction of the pin */
+ outdir = readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset);
+ indir = readl(plat->base + GPIO_INPUT_EN) & BIT(offset);
+
+ if (outdir)
+ /* Pin at specified offset is configured as output */
+ val = GPIOF_OUTPUT;
+ else if (indir)
+ /* Pin at specified offset is configured as input */
+ val = GPIOF_INPUT;
+ else
+ /*The requested GPIO is not set as input or output */
+ val = GPIOF_UNUSED;
+
+ return val;
+}
+
+static const struct udevice_id sifive_gpio_match[] = {
+ { .compatible = "sifive,gpio0" },
+ { }
+};
+
+static const struct dm_gpio_ops sifive_gpio_ops = {
+ .direction_input = sifive_gpio_direction_input,
+ .direction_output = sifive_gpio_direction_output,
+ .get_value = sifive_gpio_get_value,
+ .set_value = sifive_gpio_set_value,
+ .get_function = sifive_gpio_get_function,
+};
+
+static int sifive_gpio_of_to_plat(struct udevice *dev)
+{
+ struct sifive_gpio_plat *plat = dev_get_plat(dev);
+
+ plat->base = dev_read_addr_ptr(dev);
+ if (!plat->base)
+ return -EINVAL;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_sifive) = {
+ .name = "gpio_sifive",
+ .id = UCLASS_GPIO,
+ .of_match = sifive_gpio_match,
+ .of_to_plat = of_match_ptr(sifive_gpio_of_to_plat),
+ .plat_auto = sizeof(struct sifive_gpio_plat),
+ .ops = &sifive_gpio_ops,
+ .probe = sifive_gpio_probe,
+};
diff --git a/drivers/gpio/sl28cpld-gpio.c b/drivers/gpio/sl28cpld-gpio.c
new file mode 100644
index 00000000000..700fc3df298
--- /dev/null
+++ b/drivers/gpio/sl28cpld-gpio.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPIO driver for the sl28cpld
+ *
+ * Copyright (c) 2021 Michael Walle <michael@walle.cc>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/gpio.h>
+#include <sl28cpld.h>
+
+/* GPIO flavor */
+#define SL28CPLD_GPIO_DIR 0x00
+#define SL28CPLD_GPIO_OUT 0x01
+#define SL28CPLD_GPIO_IN 0x02
+
+/* input-only flavor */
+#define SL28CPLD_GPI_IN 0x00
+
+/* output-only flavor */
+#define SL28CPLD_GPO_OUT 0x00
+
+enum {
+ SL28CPLD_GPIO,
+ SL28CPLD_GPI,
+ SL28CPLD_GPO,
+};
+
+static int sl28cpld_gpio_get_value(struct udevice *dev, unsigned int gpio)
+{
+ ulong type = dev_get_driver_data(dev);
+ int val, reg;
+
+ switch (type) {
+ case SL28CPLD_GPIO:
+ reg = SL28CPLD_GPIO_IN;
+ break;
+ case SL28CPLD_GPI:
+ reg = SL28CPLD_GPI_IN;
+ break;
+ case SL28CPLD_GPO:
+ /* we are output only, thus just return the output value */
+ reg = SL28CPLD_GPO_OUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = sl28cpld_read(dev, reg);
+
+ return val < 0 ? val : !!(val & BIT(gpio));
+}
+
+static int sl28cpld_gpio_set_value(struct udevice *dev, unsigned int gpio,
+ int value)
+{
+ ulong type = dev_get_driver_data(dev);
+ uint reg;
+
+ switch (type) {
+ case SL28CPLD_GPIO:
+ reg = SL28CPLD_GPIO_OUT;
+ break;
+ case SL28CPLD_GPO:
+ reg = SL28CPLD_GPO_OUT;
+ break;
+ case SL28CPLD_GPI:
+ default:
+ return -EINVAL;
+ }
+
+ if (value)
+ return sl28cpld_update(dev, reg, 0, BIT(gpio));
+ else
+ return sl28cpld_update(dev, reg, BIT(gpio), 0);
+}
+
+static int sl28cpld_gpio_direction_input(struct udevice *dev, unsigned int gpio)
+{
+ ulong type = dev_get_driver_data(dev);
+
+ switch (type) {
+ case SL28CPLD_GPI:
+ return 0;
+ case SL28CPLD_GPIO:
+ return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, BIT(gpio), 0);
+ case SL28CPLD_GPO:
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sl28cpld_gpio_direction_output(struct udevice *dev,
+ unsigned int gpio, int value)
+{
+ ulong type = dev_get_driver_data(dev);
+ int ret;
+
+ /* set_value() will report an error if we are input-only */
+ ret = sl28cpld_gpio_set_value(dev, gpio, value);
+ if (ret)
+ return ret;
+
+ if (type == SL28CPLD_GPIO)
+ return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, 0, BIT(gpio));
+
+ return 0;
+}
+
+static int sl28cpld_gpio_get_function(struct udevice *dev, unsigned int gpio)
+{
+ ulong type = dev_get_driver_data(dev);
+ int val;
+
+ switch (type) {
+ case SL28CPLD_GPIO:
+ val = sl28cpld_read(dev, SL28CPLD_GPIO_DIR);
+ if (val < 0)
+ return val;
+ if (val & BIT(gpio))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+ case SL28CPLD_GPI:
+ return GPIOF_INPUT;
+ case SL28CPLD_GPO:
+ return GPIOF_OUTPUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct dm_gpio_ops sl28cpld_gpio_ops = {
+ .direction_input = sl28cpld_gpio_direction_input,
+ .direction_output = sl28cpld_gpio_direction_output,
+ .get_value = sl28cpld_gpio_get_value,
+ .set_value = sl28cpld_gpio_set_value,
+ .get_function = sl28cpld_gpio_get_function,
+};
+
+static int sl28cpld_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->gpio_count = 8;
+ uc_priv->bank_name = dev_read_name(dev);
+
+ return 0;
+}
+
+static const struct udevice_id sl28cpld_gpio_ids[] = {
+ { .compatible = "kontron,sl28cpld-gpio", .data = SL28CPLD_GPIO},
+ { .compatible = "kontron,sl28cpld-gpo", .data = SL28CPLD_GPO},
+ { .compatible = "kontron,sl28cpld-gpi", .data = SL28CPLD_GPI},
+ { }
+};
+
+U_BOOT_DRIVER(sl28cpld_gpio) = {
+ .name = "sl28cpld_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = sl28cpld_gpio_ids,
+ .probe = sl28cpld_gpio_probe,
+ .ops = &sl28cpld_gpio_ops,
+};
diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c
new file mode 100644
index 00000000000..7a2ca91c769
--- /dev/null
+++ b/drivers/gpio/stm32_gpio.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
+ */
+
+#define LOG_CATEGORY UCLASS_GPIO
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <asm/arch/stm32.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include "stm32_gpio_priv.h"
+
+#define STM32_GPIOS_PER_BANK 16
+
+#define MODE_BITS(gpio_pin) ((gpio_pin) * 2)
+#define MODE_BITS_MASK 3
+#define BSRR_BIT(gpio_pin, value) BIT((gpio_pin) + (value ? 0 : 16))
+
+#define PUPD_BITS(gpio_pin) ((gpio_pin) * 2)
+#define PUPD_MASK 3
+
+#define OTYPE_BITS(gpio_pin) (gpio_pin)
+#define OTYPE_MSK 1
+
+static void stm32_gpio_set_moder(struct stm32_gpio_regs *regs,
+ int idx,
+ int mode)
+{
+ int bits_index;
+ int mask;
+
+ bits_index = MODE_BITS(idx);
+ mask = MODE_BITS_MASK << bits_index;
+
+ clrsetbits_le32(&regs->moder, mask, mode << bits_index);
+}
+
+static int stm32_gpio_get_moder(struct stm32_gpio_regs *regs, int idx)
+{
+ return (readl(&regs->moder) >> MODE_BITS(idx)) & MODE_BITS_MASK;
+}
+
+static void stm32_gpio_set_otype(struct stm32_gpio_regs *regs,
+ int idx,
+ enum stm32_gpio_otype otype)
+{
+ int bits;
+
+ bits = OTYPE_BITS(idx);
+ clrsetbits_le32(&regs->otyper, OTYPE_MSK << bits, otype << bits);
+}
+
+static enum stm32_gpio_otype stm32_gpio_get_otype(struct stm32_gpio_regs *regs,
+ int idx)
+{
+ return (readl(&regs->otyper) >> OTYPE_BITS(idx)) & OTYPE_MSK;
+}
+
+static void stm32_gpio_set_pupd(struct stm32_gpio_regs *regs,
+ int idx,
+ enum stm32_gpio_pupd pupd)
+{
+ int bits;
+
+ bits = PUPD_BITS(idx);
+ clrsetbits_le32(&regs->pupdr, PUPD_MASK << bits, pupd << bits);
+}
+
+static enum stm32_gpio_pupd stm32_gpio_get_pupd(struct stm32_gpio_regs *regs,
+ int idx)
+{
+ return (readl(&regs->pupdr) >> PUPD_BITS(idx)) & PUPD_MASK;
+}
+
+static bool stm32_gpio_is_mapped(struct udevice *dev, int offset)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+
+ return !!(priv->gpio_range & BIT(offset));
+}
+
+static int stm32_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ stm32_gpio_set_moder(regs, offset, STM32_GPIO_MODE_IN);
+
+ return 0;
+}
+
+static int stm32_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ stm32_gpio_set_moder(regs, offset, STM32_GPIO_MODE_OUT);
+
+ writel(BSRR_BIT(offset, value), &regs->bsrr);
+
+ return 0;
+}
+
+static int stm32_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ return readl(&regs->idr) & BIT(offset) ? 1 : 0;
+}
+
+static int stm32_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ writel(BSRR_BIT(offset, value), &regs->bsrr);
+
+ return 0;
+}
+
+static int stm32_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+ int bits_index;
+ int mask;
+ u32 mode;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return GPIOF_UNKNOWN;
+
+ bits_index = MODE_BITS(offset);
+ mask = MODE_BITS_MASK << bits_index;
+
+ mode = (readl(&regs->moder) & mask) >> bits_index;
+ if (mode == STM32_GPIO_MODE_OUT)
+ return GPIOF_OUTPUT;
+ if (mode == STM32_GPIO_MODE_IN)
+ return GPIOF_INPUT;
+ if (mode == STM32_GPIO_MODE_AN)
+ return GPIOF_UNUSED;
+
+ return GPIOF_FUNC;
+}
+
+static int stm32_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ if (flags & GPIOD_IS_OUT) {
+ bool value = flags & GPIOD_IS_OUT_ACTIVE;
+
+ if (flags & GPIOD_OPEN_DRAIN)
+ stm32_gpio_set_otype(regs, offset, STM32_GPIO_OTYPE_OD);
+ else
+ stm32_gpio_set_otype(regs, offset, STM32_GPIO_OTYPE_PP);
+
+ stm32_gpio_set_moder(regs, offset, STM32_GPIO_MODE_OUT);
+ writel(BSRR_BIT(offset, value), &regs->bsrr);
+
+ } else if (flags & GPIOD_IS_IN) {
+ stm32_gpio_set_moder(regs, offset, STM32_GPIO_MODE_IN);
+ }
+ if (flags & GPIOD_PULL_UP)
+ stm32_gpio_set_pupd(regs, offset, STM32_GPIO_PUPD_UP);
+ else if (flags & GPIOD_PULL_DOWN)
+ stm32_gpio_set_pupd(regs, offset, STM32_GPIO_PUPD_DOWN);
+
+ return 0;
+}
+
+static int stm32_gpio_get_flags(struct udevice *dev, unsigned int offset,
+ ulong *flagsp)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct stm32_gpio_regs *regs = priv->regs;
+ ulong dir_flags = 0;
+
+ if (!stm32_gpio_is_mapped(dev, offset))
+ return -ENXIO;
+
+ switch (stm32_gpio_get_moder(regs, offset)) {
+ case STM32_GPIO_MODE_OUT:
+ dir_flags |= GPIOD_IS_OUT;
+ if (stm32_gpio_get_otype(regs, offset) == STM32_GPIO_OTYPE_OD)
+ dir_flags |= GPIOD_OPEN_DRAIN;
+ if (readl(&regs->idr) & BIT(offset))
+ dir_flags |= GPIOD_IS_OUT_ACTIVE;
+ break;
+ case STM32_GPIO_MODE_IN:
+ dir_flags |= GPIOD_IS_IN;
+ break;
+ default:
+ break;
+ }
+ switch (stm32_gpio_get_pupd(regs, offset)) {
+ case STM32_GPIO_PUPD_UP:
+ dir_flags |= GPIOD_PULL_UP;
+ break;
+ case STM32_GPIO_PUPD_DOWN:
+ dir_flags |= GPIOD_PULL_DOWN;
+ break;
+ default:
+ break;
+ }
+ *flagsp = dir_flags;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_stm32_ops = {
+ .direction_input = stm32_gpio_direction_input,
+ .direction_output = stm32_gpio_direction_output,
+ .get_value = stm32_gpio_get_value,
+ .set_value = stm32_gpio_set_value,
+ .get_function = stm32_gpio_get_function,
+ .set_flags = stm32_gpio_set_flags,
+ .get_flags = stm32_gpio_get_flags,
+};
+
+static int gpio_stm32_probe(struct udevice *dev)
+{
+ struct stm32_gpio_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct ofnode_phandle_args args;
+ const char *name;
+ struct clk clk;
+ fdt_addr_t addr;
+ int ret, i;
+
+ addr = dev_read_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = (struct stm32_gpio_regs *)addr;
+
+ name = dev_read_string(dev, "st,bank-name");
+ if (!name)
+ return -EINVAL;
+ uc_priv->bank_name = name;
+
+ i = 0;
+ ret = dev_read_phandle_with_args(dev, "gpio-ranges",
+ NULL, 3, i, &args);
+
+ if (!ret && args.args_count < 3)
+ return -EINVAL;
+
+ uc_priv->gpio_count = STM32_GPIOS_PER_BANK;
+ if (ret == -ENOENT)
+ priv->gpio_range = GENMASK(STM32_GPIOS_PER_BANK - 1, 0);
+
+ while (ret != -ENOENT) {
+ priv->gpio_range |= GENMASK(args.args[2] + args.args[0] - 1,
+ args.args[0]);
+
+ ret = dev_read_phandle_with_args(dev, "gpio-ranges", NULL, 3,
+ ++i, &args);
+ if (!ret && args.args_count < 3)
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "addr = 0x%p bank_name = %s gpio_count = %d gpio_range = 0x%x\n",
+ (u32 *)priv->regs, uc_priv->bank_name, uc_priv->gpio_count,
+ priv->gpio_range);
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_enable(&clk);
+
+ if (ret) {
+ dev_err(dev, "failed to enable clock\n");
+ return ret;
+ }
+ dev_dbg(dev, "clock enabled\n");
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_stm32) = {
+ .name = "gpio_stm32",
+ .id = UCLASS_GPIO,
+ .probe = gpio_stm32_probe,
+ .ops = &gpio_stm32_ops,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .priv_auto = sizeof(struct stm32_gpio_priv),
+};
diff --git a/drivers/gpio/stm32_gpio_priv.h b/drivers/gpio/stm32_gpio_priv.h
new file mode 100644
index 00000000000..662a000fe73
--- /dev/null
+++ b/drivers/gpio/stm32_gpio_priv.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
+ */
+
+#ifndef _STM32_GPIO_PRIV_H_
+#define _STM32_GPIO_PRIV_H_
+
+enum stm32_gpio_mode {
+ STM32_GPIO_MODE_IN = 0,
+ STM32_GPIO_MODE_OUT,
+ STM32_GPIO_MODE_AF,
+ STM32_GPIO_MODE_AN
+};
+
+enum stm32_gpio_otype {
+ STM32_GPIO_OTYPE_PP = 0,
+ STM32_GPIO_OTYPE_OD
+};
+
+enum stm32_gpio_speed {
+ STM32_GPIO_SPEED_2M = 0,
+ STM32_GPIO_SPEED_25M,
+ STM32_GPIO_SPEED_50M,
+ STM32_GPIO_SPEED_100M
+};
+
+enum stm32_gpio_pupd {
+ STM32_GPIO_PUPD_NO = 0,
+ STM32_GPIO_PUPD_UP,
+ STM32_GPIO_PUPD_DOWN
+};
+
+enum stm32_gpio_af {
+ STM32_GPIO_AF0 = 0,
+ STM32_GPIO_AF1,
+ STM32_GPIO_AF2,
+ STM32_GPIO_AF3,
+ STM32_GPIO_AF4,
+ STM32_GPIO_AF5,
+ STM32_GPIO_AF6,
+ STM32_GPIO_AF7,
+ STM32_GPIO_AF8,
+ STM32_GPIO_AF9,
+ STM32_GPIO_AF10,
+ STM32_GPIO_AF11,
+ STM32_GPIO_AF12,
+ STM32_GPIO_AF13,
+ STM32_GPIO_AF14,
+ STM32_GPIO_AF15
+};
+
+struct stm32_gpio_dsc {
+ u8 port;
+ u8 pin;
+};
+
+struct stm32_gpio_ctl {
+ enum stm32_gpio_mode mode;
+ enum stm32_gpio_otype otype;
+ enum stm32_gpio_speed speed;
+ enum stm32_gpio_pupd pupd;
+ enum stm32_gpio_af af;
+};
+
+struct stm32_gpio_regs {
+ u32 moder; /* GPIO port mode */
+ u32 otyper; /* GPIO port output type */
+ u32 ospeedr; /* GPIO port output speed */
+ u32 pupdr; /* GPIO port pull-up/pull-down */
+ u32 idr; /* GPIO port input data */
+ u32 odr; /* GPIO port output data */
+ u32 bsrr; /* GPIO port bit set/reset */
+ u32 lckr; /* GPIO port configuration lock */
+ u32 afr[2]; /* GPIO alternate function */
+};
+
+struct stm32_gpio_priv {
+ struct stm32_gpio_regs *regs;
+ unsigned int gpio_range;
+};
+
+#endif /* _STM32_GPIO_PRIV_H_ */
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
new file mode 100644
index 00000000000..e4463a223f7
--- /dev/null
+++ b/drivers/gpio/sunxi_gpio.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
+ *
+ * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
+ *
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * Tom Cubie <tangliang@allwinnertech.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <sunxi_gpio.h>
+
+/*
+ * =======================================================================
+ * Low level GPIO/pin controller access functions, to be shared by non-DM
+ * SPL code and the DM pinctrl/GPIO drivers.
+ * The functions ending in "bank" take a base pointer to a GPIO bank, and
+ * the pin offset is relative to that bank.
+ * The functions without "bank" in their name take a linear GPIO number,
+ * covering all ports, and starting at 0 for PortA.
+ * =======================================================================
+ */
+
+#define GPIO_BANK(pin) ((pin) >> 5)
+#define GPIO_NUM(pin) ((pin) & 0x1f)
+
+#define GPIO_CFG_REG_OFFSET 0x00
+#define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3)
+#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2)
+
+#define GPIO_DAT_REG_OFFSET 0x10
+
+#define GPIO_DRV_REG_OFFSET 0x14
+
+/* Newer SoCs use a slightly different register layout */
+#ifdef CONFIG_SUNXI_NEW_PINCTRL
+/* pin drive strength: 4 bits per pin */
+#define GPIO_DRV_INDEX(pin) ((pin) / 8)
+#define GPIO_DRV_OFFSET(pin) (((pin) % 8) * 4)
+
+#define GPIO_PULL_REG_OFFSET 0x24
+
+#else /* older generation pin controllers */
+/* pin drive strength: 2 bits per pin */
+#define GPIO_DRV_INDEX(pin) ((pin) / 16)
+#define GPIO_DRV_OFFSET(pin) (((pin) % 16) * 2)
+
+#define GPIO_PULL_REG_OFFSET 0x1c
+#endif
+
+#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
+#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
+
+static void* BANK_TO_GPIO(int bank)
+{
+ void *pio_base;
+
+ if (bank < SUNXI_GPIO_L) {
+ pio_base = (void *)(uintptr_t)SUNXI_PIO_BASE;
+ } else {
+ pio_base = (void *)(uintptr_t)SUNXI_R_PIO_BASE;
+ bank -= SUNXI_GPIO_L;
+ }
+
+ return pio_base + bank * SUNXI_PINCTRL_BANK_SIZE;
+}
+
+void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val)
+{
+ u32 index = GPIO_CFG_INDEX(pin_offset);
+ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_CFG_REG_OFFSET + index * 4,
+ 0xfU << offset, val << offset);
+}
+
+void sunxi_gpio_set_cfgpin(u32 pin, u32 val)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *pio = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_cfgbank(pio, GPIO_NUM(pin), val);
+}
+
+int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset)
+{
+ u32 index = GPIO_CFG_INDEX(pin_offset);
+ u32 offset = GPIO_CFG_OFFSET(pin_offset);
+ u32 cfg;
+
+ cfg = readl(bank_base + GPIO_CFG_REG_OFFSET + index * 4);
+ cfg >>= offset;
+
+ return cfg & 0xf;
+}
+
+int sunxi_gpio_get_cfgpin(u32 pin)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ return sunxi_gpio_get_cfgbank(bank_base, GPIO_NUM(pin));
+}
+
+static void sunxi_gpio_set_value_bank(void *bank_base, int pin, bool set)
+{
+ u32 mask = 1U << pin;
+
+ clrsetbits_le32(bank_base + GPIO_DAT_REG_OFFSET,
+ set ? 0 : mask, set ? mask : 0);
+}
+
+static int sunxi_gpio_get_value_bank(void *bank_base, int pin)
+{
+ return !!(readl(bank_base + GPIO_DAT_REG_OFFSET) & (1U << pin));
+}
+
+void sunxi_gpio_set_drv(u32 pin, u32 val)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_drv_bank(bank_base, GPIO_NUM(pin), val);
+}
+
+void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val)
+{
+ u32 index = GPIO_DRV_INDEX(pin_offset);
+ u32 offset = GPIO_DRV_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_DRV_REG_OFFSET + index * 4,
+ 0x3U << offset, val << offset);
+}
+
+void sunxi_gpio_set_pull(u32 pin, u32 val)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *bank_base = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_pull_bank(bank_base, GPIO_NUM(pin), val);
+}
+
+void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val)
+{
+ u32 index = GPIO_PULL_INDEX(pin_offset);
+ u32 offset = GPIO_PULL_OFFSET(pin_offset);
+
+ clrsetbits_le32(bank_base + GPIO_PULL_REG_OFFSET + index * 4,
+ 0x3U << offset, val << offset);
+}
+
+
+/* =========== Non-DM code, used by the SPL. ============ */
+
+#if !CONFIG_IS_ENABLED(DM_GPIO)
+static void sunxi_gpio_set_value(u32 pin, bool set)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *pio = BANK_TO_GPIO(bank);
+
+ sunxi_gpio_set_value_bank(pio, GPIO_NUM(pin), set);
+}
+
+static int sunxi_gpio_get_value(u32 pin)
+{
+ u32 bank = GPIO_BANK(pin);
+ void *pio = BANK_TO_GPIO(bank);
+
+ return sunxi_gpio_get_value_bank(pio, GPIO_NUM(pin));
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+ return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+ return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+ sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
+
+ return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+ sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
+ sunxi_gpio_set_value(gpio, value);
+
+ return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+ return sunxi_gpio_get_value(gpio);
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+ sunxi_gpio_set_value(gpio, value);
+
+ return 0;
+}
+
+int sunxi_name_to_gpio(const char *name)
+{
+ int group = 0;
+ int groupsize = 9 * 32;
+ long pin;
+ char *eptr;
+
+ if (*name == 'P' || *name == 'p')
+ name++;
+ if (*name >= 'A') {
+ group = *name - (*name > 'a' ? 'a' : 'A');
+ groupsize = 32;
+ name++;
+ }
+
+ pin = simple_strtol(name, &eptr, 10);
+ if (!*name || *eptr)
+ return -1;
+ if (pin < 0 || pin > groupsize || group >= 9)
+ return -1;
+ return group * 32 + pin;
+}
+#endif /* !DM_GPIO */
+
+/* =========== DM code, used by U-Boot proper. ============ */
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+/* TODO(sjg@chromium.org): Remove this function and use device tree */
+int sunxi_name_to_gpio(const char *name)
+{
+ unsigned int gpio;
+ int ret;
+#if !defined CONFIG_SPL_BUILD && defined CONFIG_AXP_GPIO
+ char lookup[8];
+
+ if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) {
+ sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d",
+ SUNXI_GPIO_AXP0_VBUS_ENABLE);
+ name = lookup;
+ }
+#endif
+ ret = gpio_lookup_name(name, NULL, NULL, &gpio);
+
+ return ret ? ret : gpio;
+}
+
+static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct sunxi_gpio_plat *plat = dev_get_plat(dev);
+
+ return sunxi_gpio_get_value_bank(plat->regs, offset);
+}
+
+static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct sunxi_gpio_plat *plat = dev_get_plat(dev);
+ int func;
+
+ func = sunxi_gpio_get_cfgbank(plat->regs, offset);
+ if (func == SUNXI_GPIO_OUTPUT)
+ return GPIOF_OUTPUT;
+ else if (func == SUNXI_GPIO_INPUT)
+ return GPIOF_INPUT;
+ else
+ return GPIOF_FUNC;
+}
+
+static int sunxi_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ int ret;
+
+ ret = device_get_child(dev, args->args[0], &desc->dev);
+ if (ret)
+ return ret;
+ desc->offset = args->args[1];
+ desc->flags = gpio_flags_xlate(args->args[2]);
+
+ return 0;
+}
+
+static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset,
+ ulong flags)
+{
+ struct sunxi_gpio_plat *plat = dev_get_plat(dev);
+
+ if (flags & GPIOD_IS_OUT) {
+ u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE);
+
+ sunxi_gpio_set_value_bank(plat->regs, offset, value);
+ sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
+ } else if (flags & GPIOD_IS_IN) {
+ u32 pull = 0;
+
+ if (flags & GPIOD_PULL_UP)
+ pull = 1;
+ else if (flags & GPIOD_PULL_DOWN)
+ pull = 2;
+ sunxi_gpio_set_pull_bank(plat->regs, offset, pull);
+ sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT);
+ }
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_sunxi_ops = {
+ .get_value = sunxi_gpio_get_value,
+ .get_function = sunxi_gpio_get_function,
+ .xlate = sunxi_gpio_xlate,
+ .set_flags = sunxi_gpio_set_flags,
+};
+
+static int gpio_sunxi_probe(struct udevice *dev)
+{
+ struct sunxi_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Tell the uclass how many GPIOs we have */
+ if (plat) {
+ uc_priv->gpio_count = SUNXI_GPIOS_PER_BANK;
+ uc_priv->bank_name = plat->bank_name;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_sunxi) = {
+ .name = "gpio_sunxi",
+ .id = UCLASS_GPIO,
+ .probe = gpio_sunxi_probe,
+ .ops = &gpio_sunxi_ops,
+};
+#endif /* DM_GPIO */
diff --git a/drivers/gpio/tca642x.c b/drivers/gpio/tca642x.c
new file mode 100644
index 00000000000..b07496e6e49
--- /dev/null
+++ b/drivers/gpio/tca642x.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2013 Texas Instruments, Inc.
+ * Author: Dan Murphy <dmurphy@ti.com>
+ *
+ * Derived work from the pca953x.c driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <command.h>
+#include <i2c.h>
+#include <tca642x.h>
+
+/* tca642x register address definitions */
+struct tca642x_bank_info tca642x_regs[] = {
+ { .input_reg = 0x00,
+ .output_reg = 0x04,
+ .polarity_reg = 0x08,
+ .configuration_reg = 0x0c },
+ { .input_reg = 0x01,
+ .output_reg = 0x05,
+ .polarity_reg = 0x09,
+ .configuration_reg = 0x0d },
+ { .input_reg = 0x02,
+ .output_reg = 0x06,
+ .polarity_reg = 0x0a,
+ .configuration_reg = 0x0e },
+};
+
+/*
+ * Modify masked bits in register
+ */
+static int tca642x_reg_write(uchar chip, uint8_t addr,
+ uint8_t reg_bit, uint8_t data)
+{
+ uint8_t valw;
+ int org_bus_num;
+ int ret;
+
+ org_bus_num = i2c_get_bus_num();
+ i2c_set_bus_num(CFG_SYS_I2C_TCA642X_BUS_NUM);
+
+ if (i2c_read(chip, addr, 1, (uint8_t *)&valw, 1)) {
+ printf("Could not read before writing\n");
+ ret = -1;
+ goto error;
+ }
+ valw &= ~reg_bit;
+ valw |= data;
+
+ ret = i2c_write(chip, addr, 1, (u8 *)&valw, 1);
+
+error:
+ i2c_set_bus_num(org_bus_num);
+ return ret;
+}
+
+static int tca642x_reg_read(uchar chip, uint8_t addr, uint8_t *data)
+{
+ uint8_t valw;
+ int org_bus_num;
+ int ret = 0;
+
+ org_bus_num = i2c_get_bus_num();
+ i2c_set_bus_num(CFG_SYS_I2C_TCA642X_BUS_NUM);
+ if (i2c_read(chip, addr, 1, (u8 *)&valw, 1)) {
+ ret = -1;
+ goto error;
+ }
+
+ *data = valw;
+
+error:
+ i2c_set_bus_num(org_bus_num);
+ return ret;
+}
+
+/*
+ * Set output value of IO pins in 'reg_bit' to corresponding value in 'data'
+ * 0 = low, 1 = high
+ */
+int tca642x_set_val(uchar chip, uint8_t gpio_bank,
+ uint8_t reg_bit, uint8_t data)
+{
+ uint8_t out_reg = tca642x_regs[gpio_bank].output_reg;
+
+ return tca642x_reg_write(chip, out_reg, reg_bit, data);
+}
+
+/*
+ * Set read polarity of IO pins in 'reg_bit' to corresponding value in 'data'
+ * 0 = read pin value, 1 = read inverted pin value
+ */
+int tca642x_set_pol(uchar chip, uint8_t gpio_bank,
+ uint8_t reg_bit, uint8_t data)
+{
+ uint8_t pol_reg = tca642x_regs[gpio_bank].polarity_reg;
+
+ return tca642x_reg_write(chip, pol_reg, reg_bit, data);
+}
+
+/*
+ * Set direction of IO pins in 'reg_bit' to corresponding value in 'data'
+ * 0 = output, 1 = input
+ */
+int tca642x_set_dir(uchar chip, uint8_t gpio_bank,
+ uint8_t reg_bit, uint8_t data)
+{
+ uint8_t config_reg = tca642x_regs[gpio_bank].configuration_reg;
+
+ return tca642x_reg_write(chip, config_reg, reg_bit, data);
+}
+
+/*
+ * Read current logic level of all IO pins
+ */
+int tca642x_get_val(uchar chip, uint8_t gpio_bank)
+{
+ uint8_t val;
+ uint8_t in_reg = tca642x_regs[gpio_bank].input_reg;
+
+ if (tca642x_reg_read(chip, in_reg, &val) < 0)
+ return -1;
+
+ return (int)val;
+}
+
+/*
+ * Set the inital register states for the tca642x gpio expander
+ */
+int tca642x_set_inital_state(uchar chip, struct tca642x_bank_info init_data[])
+{
+ int i, ret;
+ uint8_t config_reg;
+ uint8_t polarity_reg;
+ uint8_t output_reg;
+
+ for (i = 0; i < 3; i++) {
+ config_reg = tca642x_regs[i].configuration_reg;
+ ret = tca642x_reg_write(chip, config_reg, 0xff,
+ init_data[i].configuration_reg);
+ polarity_reg = tca642x_regs[i].polarity_reg;
+ ret = tca642x_reg_write(chip, polarity_reg, 0xff,
+ init_data[i].polarity_reg);
+ output_reg = tca642x_regs[i].output_reg;
+ ret = tca642x_reg_write(chip, output_reg, 0xff,
+ init_data[i].output_reg);
+ }
+
+ return ret;
+}
+
+#if defined(CONFIG_CMD_TCA642X) && !defined(CONFIG_SPL_BUILD)
+/*
+ * Display tca642x information
+ */
+static int tca642x_info(uchar chip)
+{
+ int i, j;
+ uint8_t data;
+
+ printf("tca642x@ 0x%x (%d pins):\n", chip, 24);
+ for (i = 0; i < 3; i++) {
+ printf("Bank %i\n", i);
+ if (tca642x_reg_read(chip,
+ tca642x_regs[i].configuration_reg,
+ &data) < 0)
+ return -1;
+ printf("\tConfiguration: ");
+ for (j = 7; j >= 0; j--)
+ printf("%c", data & (1 << j) ? 'i' : 'o');
+ printf("\n");
+
+ if (tca642x_reg_read(chip,
+ tca642x_regs[i].polarity_reg, &data) < 0)
+ return -1;
+ printf("\tPolarity: ");
+ for (j = 7; j >= 0; j--)
+ printf("%c", data & (1 << j) ? '1' : '0');
+ printf("\n");
+
+ if (tca642x_reg_read(chip,
+ tca642x_regs[i].input_reg, &data) < 0)
+ return -1;
+ printf("\tInput value: ");
+ for (j = 7; j >= 0; j--)
+ printf("%c", data & (1 << j) ? '1' : '0');
+ printf("\n");
+
+ if (tca642x_reg_read(chip,
+ tca642x_regs[i].output_reg, &data) < 0)
+ return -1;
+ printf("\tOutput value: ");
+ for (j = 7; j >= 0; j--)
+ printf("%c", data & (1 << j) ? '1' : '0');
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int tca642x_get_bank(int pin)
+{
+ int gpio_bank;
+
+ if (pin <= 7) {
+ gpio_bank = 0;
+ } else if ((pin >= 10) && (pin <= 17)) {
+ gpio_bank = 1;
+ } else if ((pin >= 20) && (pin <= 27)) {
+ gpio_bank = 2;
+ } else {
+ printf("Requested pin is not available\n");
+ gpio_bank = -1;
+ }
+
+ return gpio_bank;
+}
+
+static struct cmd_tbl cmd_tca642x[] = {
+ U_BOOT_CMD_MKENT(device, 3, 0, (void *)TCA642X_CMD_DEVICE, "", ""),
+ U_BOOT_CMD_MKENT(output, 4, 0, (void *)TCA642X_CMD_OUTPUT, "", ""),
+ U_BOOT_CMD_MKENT(input, 3, 0, (void *)TCA642X_CMD_INPUT, "", ""),
+ U_BOOT_CMD_MKENT(invert, 4, 0, (void *)TCA642X_CMD_INVERT, "", ""),
+ U_BOOT_CMD_MKENT(info, 2, 0, (void *)TCA642X_CMD_INFO, "", ""),
+};
+
+static int do_tca642x(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ static uchar chip = CFG_SYS_I2C_TCA642X_ADDR;
+ int ret = CMD_RET_USAGE, val;
+ int gpio_bank = 0;
+ uint8_t bank_shift;
+ ulong ul_arg2 = 0;
+ ulong ul_arg3 = 0;
+ struct cmd_tbl *c;
+
+ c = find_cmd_tbl(argv[1], cmd_tca642x, ARRAY_SIZE(cmd_tca642x));
+
+ /* All commands but "device" require 'maxargs' arguments */
+ if (!c ||
+ !((argc == (c->maxargs)) ||
+ (((int)c->cmd == TCA642X_CMD_DEVICE) &&
+ (argc == (c->maxargs - 1))))) {
+ return CMD_RET_USAGE;
+ }
+
+ /* arg2 used as chip number or pin number */
+ if (argc > 2)
+ ul_arg2 = dectoul(argv[2], NULL);
+
+ /* arg3 used as pin or invert value */
+ if (argc > 3)
+ ul_arg3 = dectoul(argv[3], NULL) & 0x1;
+
+ switch ((int)c->cmd) {
+ case TCA642X_CMD_INFO:
+ ret = tca642x_info(chip);
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+
+ case TCA642X_CMD_DEVICE:
+ if (argc == 3)
+ chip = (uint8_t)ul_arg2;
+ printf("Current device address: 0x%x\n", chip);
+ ret = CMD_RET_SUCCESS;
+ break;
+
+ case TCA642X_CMD_INPUT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
+ bank_shift = ul_arg2 - (gpio_bank * 10);
+ ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
+ TCA642X_DIR_IN << bank_shift);
+ val = (tca642x_get_val(chip, gpio_bank) &
+ (1 << bank_shift)) != 0;
+
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ else
+ printf("chip 0x%02x, pin 0x%lx = %d\n", chip,
+ ul_arg2, val);
+ break;
+
+ case TCA642X_CMD_OUTPUT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
+ bank_shift = ul_arg2 - (gpio_bank * 10);
+ ret = tca642x_set_dir(chip, gpio_bank, (1 << bank_shift),
+ (TCA642X_DIR_OUT << bank_shift));
+ if (!ret)
+ ret = tca642x_set_val(chip,
+ gpio_bank, (1 << bank_shift),
+ (ul_arg3 << bank_shift));
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+
+ case TCA642X_CMD_INVERT:
+ gpio_bank = tca642x_get_bank(ul_arg2);
+ if (gpio_bank < 0) {
+ ret = CMD_RET_FAILURE;
+ goto error;
+ }
+ bank_shift = ul_arg2 - (gpio_bank * 10);
+ ret = tca642x_set_pol(chip, gpio_bank, (1 << bank_shift),
+ (ul_arg3 << bank_shift));
+ if (ret)
+ ret = CMD_RET_FAILURE;
+ break;
+ }
+error:
+ if (ret == CMD_RET_FAILURE)
+ eprintf("Error talking to chip at 0x%x\n", chip);
+
+ return ret;
+}
+
+U_BOOT_CMD(
+ tca642x, 5, 1, do_tca642x,
+ "tca642x gpio access",
+ "device [dev]\n"
+ " - show or set current device address\n"
+ "tca642x info\n"
+ " - display info for current chip\n"
+ "tca642x output pin 0|1\n"
+ " - set pin as output and drive low or high\n"
+ "tca642x invert pin 0|1\n"
+ " - disable/enable polarity inversion for reads\n"
+ "tca642x input pin\n"
+ " - set pin as input and read value"
+);
+
+#endif /* CONFIG_CMD_TCA642X */
diff --git a/drivers/gpio/tegra186_gpio.c b/drivers/gpio/tegra186_gpio.c
new file mode 100644
index 00000000000..94a20d143e1
--- /dev/null
+++ b/drivers/gpio/tegra186_gpio.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2010-2016, NVIDIA CORPORATION.
+ * (based on tegra_gpio.c)
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "tegra186_gpio_priv.h"
+
+struct tegra186_gpio_port_data {
+ const char *name;
+ uint32_t offset;
+};
+
+struct tegra186_gpio_ctlr_data {
+ const struct tegra186_gpio_port_data *ports;
+ uint32_t port_count;
+};
+
+struct tegra186_gpio_plat {
+ const char *name;
+ uint32_t *regs;
+};
+
+static uint32_t *tegra186_gpio_reg(struct udevice *dev, uint32_t reg,
+ uint32_t gpio)
+{
+ struct tegra186_gpio_plat *plat = dev_get_plat(dev);
+ uint32_t index = (reg + (gpio * TEGRA186_GPIO_PER_GPIO_STRIDE)) / 4;
+
+ return &(plat->regs[index]);
+}
+
+static int tegra186_gpio_set_out(struct udevice *dev, unsigned offset,
+ bool output)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_CONTROL, offset);
+ rval = readl(reg);
+ if (output)
+ rval &= ~TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
+ else
+ rval |= TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED;
+ writel(rval, reg);
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+ if (output)
+ rval |= TEGRA186_GPIO_ENABLE_CONFIG_OUT;
+ else
+ rval &= ~TEGRA186_GPIO_ENABLE_CONFIG_OUT;
+ rval |= TEGRA186_GPIO_ENABLE_CONFIG_ENABLE;
+ writel(rval, reg);
+
+ return 0;
+}
+
+static int tegra186_gpio_set_val(struct udevice *dev, unsigned offset, bool val)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE, offset);
+ rval = readl(reg);
+ if (val)
+ rval |= TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
+ else
+ rval &= ~TEGRA186_GPIO_OUTPUT_VALUE_HIGH;
+ writel(rval, reg);
+
+ return 0;
+}
+
+static int tegra186_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ return tegra186_gpio_set_out(dev, offset, false);
+}
+
+static int tegra186_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ int ret;
+
+ ret = tegra186_gpio_set_val(dev, offset, value != 0);
+ if (ret)
+ return ret;
+ return tegra186_gpio_set_out(dev, offset, true);
+}
+
+static int tegra186_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+
+ if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_OUTPUT_VALUE,
+ offset);
+ else
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_INPUT, offset);
+
+ rval = readl(reg);
+ return !!rval;
+}
+
+static int tegra186_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return tegra186_gpio_set_val(dev, offset, value != 0);
+}
+
+static int tegra186_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ uint32_t *reg;
+ uint32_t rval;
+
+ reg = tegra186_gpio_reg(dev, TEGRA186_GPIO_ENABLE_CONFIG, offset);
+ rval = readl(reg);
+ if (rval & TEGRA186_GPIO_ENABLE_CONFIG_OUT)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int tegra186_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ int gpio, port, ret;
+
+ gpio = args->args[0];
+ port = gpio / TEGRA186_GPIO_PER_GPIO_COUNT;
+ ret = device_get_child(dev, port, &desc->dev);
+ if (ret)
+ return ret;
+ desc->offset = gpio % TEGRA186_GPIO_PER_GPIO_COUNT;
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops tegra186_gpio_ops = {
+ .direction_input = tegra186_gpio_direction_input,
+ .direction_output = tegra186_gpio_direction_output,
+ .get_value = tegra186_gpio_get_value,
+ .set_value = tegra186_gpio_set_value,
+ .get_function = tegra186_gpio_get_function,
+ .xlate = tegra186_gpio_xlate,
+};
+
+/**
+ * We have a top-level GPIO device with no actual GPIOs. It has a child device
+ * for each port within the controller.
+ */
+static int tegra186_gpio_bind(struct udevice *parent)
+{
+ struct tegra186_gpio_plat *parent_plat = dev_get_plat(parent);
+ struct tegra186_gpio_ctlr_data *ctlr_data =
+ (struct tegra186_gpio_ctlr_data *)dev_get_driver_data(parent);
+ uint32_t *regs;
+ int port, ret;
+
+ /* If this is a child device, there is nothing to do here */
+ if (parent_plat)
+ return 0;
+
+ regs = dev_read_addr_name_ptr(parent, "gpio");
+ if (!regs)
+ return -EINVAL;
+
+ for (port = 0; port < ctlr_data->port_count; port++) {
+ struct tegra186_gpio_plat *plat;
+ struct udevice *dev;
+
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+ plat->name = ctlr_data->ports[port].name;
+ plat->regs = &(regs[ctlr_data->ports[port].offset / 4]);
+
+ ret = device_bind(parent, parent->driver, plat->name, plat,
+ dev_ofnode(parent), &dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra186_gpio_probe(struct udevice *dev)
+{
+ struct tegra186_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ /* Only child devices have ports */
+ if (!plat)
+ return 0;
+
+ uc_priv->gpio_count = TEGRA186_GPIO_PER_GPIO_COUNT;
+ uc_priv->bank_name = plat->name;
+
+ return 0;
+}
+
+static const struct tegra186_gpio_port_data tegra186_gpio_main_ports[] = {
+ {"A", 0x2000},
+ {"B", 0x3000},
+ {"C", 0x3200},
+ {"D", 0x3400},
+ {"E", 0x2200},
+ {"F", 0x2400},
+ {"G", 0x4200},
+ {"H", 0x1000},
+ {"I", 0x0800},
+ {"J", 0x5000},
+ {"K", 0x5200},
+ {"L", 0x1200},
+ {"M", 0x5600},
+ {"N", 0x0000},
+ {"O", 0x0200},
+ {"P", 0x4000},
+ {"Q", 0x0400},
+ {"R", 0x0a00},
+ {"T", 0x0600},
+ {"X", 0x1400},
+ {"Y", 0x1600},
+ {"BB", 0x2600},
+ {"CC", 0x5400},
+};
+
+static const struct tegra186_gpio_ctlr_data tegra186_gpio_main_data = {
+ .ports = tegra186_gpio_main_ports,
+ .port_count = ARRAY_SIZE(tegra186_gpio_main_ports),
+};
+
+static const struct tegra186_gpio_port_data tegra186_gpio_aon_ports[] = {
+ {"S", 0x0200},
+ {"U", 0x0400},
+ {"V", 0x0800},
+ {"W", 0x0a00},
+ {"Z", 0x0e00},
+ {"AA", 0x0c00},
+ {"EE", 0x0600},
+ {"FF", 0x0000},
+};
+
+static const struct tegra186_gpio_ctlr_data tegra186_gpio_aon_data = {
+ .ports = tegra186_gpio_aon_ports,
+ .port_count = ARRAY_SIZE(tegra186_gpio_aon_ports),
+};
+
+static const struct udevice_id tegra186_gpio_ids[] = {
+ {
+ .compatible = "nvidia,tegra186-gpio",
+ .data = (ulong)&tegra186_gpio_main_data,
+ },
+ {
+ .compatible = "nvidia,tegra186-gpio-aon",
+ .data = (ulong)&tegra186_gpio_aon_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(tegra186_gpio) = {
+ .name = "tegra186_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = tegra186_gpio_ids,
+ .bind = tegra186_gpio_bind,
+ .probe = tegra186_gpio_probe,
+ .ops = &tegra186_gpio_ops,
+};
diff --git a/drivers/gpio/tegra186_gpio_priv.h b/drivers/gpio/tegra186_gpio_priv.h
new file mode 100644
index 00000000000..3e686beedc1
--- /dev/null
+++ b/drivers/gpio/tegra186_gpio_priv.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#ifndef _TEGRA186_GPIO_PRIV_H_
+#define _TEGRA186_GPIO_PRIV_H_
+
+/*
+ * For each GPIO, there are a set of registers than affect it, all packed
+ * back-to-back.
+ */
+#include <linux/bitops.h>
+#define TEGRA186_GPIO_ENABLE_CONFIG 0x00
+#define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
+#define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1)
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SHIFT 2
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK 3
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_NONE 0
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_LEVEL 1
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE 2
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE 3
+#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL_HIGH_RISING BIT(4)
+#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE_ENABLE BIT(5)
+#define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT_ENABLE BIT(6)
+#define TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMPING_ENABLE BIT(7)
+
+#define TEGRA186_GPIO_DEBOUNCE_THRESHOLD 0x04
+
+#define TEGRA186_GPIO_INPUT 0x08
+
+#define TEGRA186_GPIO_OUTPUT_CONTROL 0x0c
+#define TEGRA186_GPIO_OUTPUT_CONTROL_FLOATED BIT(0)
+
+#define TEGRA186_GPIO_OUTPUT_VALUE 0x10
+#define TEGRA186_GPIO_OUTPUT_VALUE_HIGH 1
+
+#define TEGRA186_GPIO_INTERRUPT_CLEAR 0x14
+
+/*
+ * 8 GPIOs are packed into a port. Their registers appear back-to-back in the
+ * port's address space.
+ */
+#define TEGRA186_GPIO_PER_GPIO_STRIDE 0x20
+#define TEGRA186_GPIO_PER_GPIO_COUNT 8
+
+/*
+ * Per-port registers are packed immediately following all of a port's
+ * per-GPIO registers.
+ */
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G 0x100
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G_STRIDE 4
+#define TEGRA186_GPIO_INTERRUPT_STATUS_G_COUNT 8
+
+/*
+ * The registers for multiple ports are packed together back-to-back to form
+ * the overall controller.
+ */
+#define TEGRA186_GPIO_PER_PORT_STRIDE 0x200
+
+#endif
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
new file mode 100644
index 00000000000..55105f2802c
--- /dev/null
+++ b/drivers/gpio/tegra_gpio.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra20 GPIO handling.
+ * (C) Copyright 2010-2012,2015
+ * NVIDIA Corporation <www.nvidia.com>
+ */
+
+/*
+ * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver.
+ * Tom Warren (twarren@nvidia.com)
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/arch/tegra.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+#include <dt-bindings/gpio/gpio.h>
+
+static const int CFG_SFIO = 0;
+static const int CFG_GPIO = 1;
+static const int DIRECTION_INPUT = 0;
+static const int DIRECTION_OUTPUT = 1;
+
+struct tegra_gpio_plat {
+ struct gpio_ctlr_bank *bank;
+ const char *port_name; /* Name of port, e.g. "B" */
+ int base_gpio; /* Port number for this port (0, 1,.., n-1) */
+};
+
+/* Information about each port at run-time */
+struct tegra_port_info {
+ struct gpio_ctlr_bank *bank;
+ int base_gpio; /* Port number for this port (0, 1,.., n-1) */
+};
+
+/* Return config of pin 'gpio' as GPIO (1) or SFIO (0) */
+static int get_config(unsigned gpio)
+{
+ struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
+ u32 u;
+ int type;
+
+ u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
+ type = (u >> GPIO_BIT(gpio)) & 1;
+
+ debug("get_config: port = %d, bit = %d is %s\n",
+ GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
+
+ return type ? CFG_GPIO : CFG_SFIO;
+}
+
+/* Config pin 'gpio' as GPIO or SFIO, based on 'type' */
+static void set_config(unsigned gpio, int type)
+{
+ struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
+ u32 u;
+
+ debug("set_config: port = %d, bit = %d, %s\n",
+ GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
+
+ u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
+ if (type != CFG_SFIO)
+ u |= 1 << GPIO_BIT(gpio);
+ else
+ u &= ~(1 << GPIO_BIT(gpio));
+ writel(u, &bank->gpio_config[GPIO_PORT(gpio)]);
+}
+
+/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */
+static int get_direction(unsigned gpio)
+{
+ struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
+ u32 u;
+ int dir;
+
+ u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
+ dir = (u >> GPIO_BIT(gpio)) & 1;
+
+ debug("get_direction: port = %d, bit = %d, %s\n",
+ GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN");
+
+ return dir ? DIRECTION_OUTPUT : DIRECTION_INPUT;
+}
+
+/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */
+static void set_direction(unsigned gpio, int output)
+{
+ struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
+ u32 u;
+
+ debug("set_direction: port = %d, bit = %d, %s\n",
+ GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN");
+
+ u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
+ if (output != DIRECTION_INPUT)
+ u |= 1 << GPIO_BIT(gpio);
+ else
+ u &= ~(1 << GPIO_BIT(gpio));
+ writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]);
+}
+
+/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */
+static void set_level(unsigned gpio, int high)
+{
+ struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
+ u32 u;
+
+ debug("set_level: port = %d, bit %d == %d\n",
+ GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high);
+
+ u = readl(&bank->gpio_out[GPIO_PORT(gpio)]);
+ if (high)
+ u |= 1 << GPIO_BIT(gpio);
+ else
+ u &= ~(1 << GPIO_BIT(gpio));
+ writel(u, &bank->gpio_out[GPIO_PORT(gpio)]);
+}
+
+/*
+ * Generic_GPIO primitives.
+ */
+
+/* set GPIO pin 'gpio' as an input */
+static int tegra_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+
+ /* Configure GPIO direction as input. */
+ set_direction(state->base_gpio + offset, DIRECTION_INPUT);
+
+ /* Enable the pin as a GPIO */
+ set_config(state->base_gpio + offset, 1);
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+static int tegra_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+ int gpio = state->base_gpio + offset;
+
+ /* Configure GPIO output value. */
+ set_level(gpio, value);
+
+ /* Configure GPIO direction as output. */
+ set_direction(gpio, DIRECTION_OUTPUT);
+
+ /* Enable the pin as a GPIO */
+ set_config(state->base_gpio + offset, 1);
+
+ return 0;
+}
+
+/* read GPIO IN value of pin 'gpio' */
+static int tegra_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+ int gpio = state->base_gpio + offset;
+ int val;
+
+ debug("%s: pin = %d (port %d:bit %d)\n", __func__,
+ gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
+
+ if (get_direction(gpio) == DIRECTION_INPUT)
+ val = readl(&state->bank->gpio_in[GPIO_PORT(gpio)]);
+ else
+ val = readl(&state->bank->gpio_out[GPIO_PORT(gpio)]);
+
+ return (val >> GPIO_BIT(gpio)) & 1;
+}
+
+/* write GPIO OUT value to pin 'gpio' */
+static int tegra_gpio_set_value(struct udevice *dev, unsigned offset, int value)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+ int gpio = state->base_gpio + offset;
+
+ debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n",
+ gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value);
+
+ /* Configure GPIO output value. */
+ set_level(gpio, value);
+
+ return 0;
+}
+
+void gpio_config_table(const struct tegra_gpio_config *config, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ switch (config[i].init) {
+ case TEGRA_GPIO_INIT_IN:
+ set_direction(config[i].gpio, DIRECTION_INPUT);
+ break;
+ case TEGRA_GPIO_INIT_OUT0:
+ set_level(config[i].gpio, 0);
+ set_direction(config[i].gpio, DIRECTION_OUTPUT);
+ break;
+ case TEGRA_GPIO_INIT_OUT1:
+ set_level(config[i].gpio, 1);
+ set_direction(config[i].gpio, DIRECTION_OUTPUT);
+ break;
+ }
+ set_config(config[i].gpio, CFG_GPIO);
+ }
+}
+
+static int tegra_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+ int gpio = state->base_gpio + offset;
+
+ if (!get_config(gpio))
+ return GPIOF_FUNC;
+ else if (get_direction(gpio))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int tegra_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ int gpio, port, ret;
+
+ gpio = args->args[0];
+ port = gpio / TEGRA_GPIOS_PER_PORT;
+ ret = device_get_child(dev, port, &desc->dev);
+ if (ret)
+ return ret;
+ desc->offset = gpio % TEGRA_GPIOS_PER_PORT;
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_tegra_ops = {
+ .direction_input = tegra_gpio_direction_input,
+ .direction_output = tegra_gpio_direction_output,
+ .get_value = tegra_gpio_get_value,
+ .set_value = tegra_gpio_set_value,
+ .get_function = tegra_gpio_get_function,
+ .xlate = tegra_gpio_xlate,
+};
+
+/**
+ * Returns the name of a GPIO port
+ *
+ * GPIOs are named A, B, C, ..., Z, AA, BB, CC, ...
+ *
+ * @base_port: Base port number (0, 1..n-1)
+ * Return: allocated string containing the name
+ */
+static char *gpio_port_name(int base_port)
+{
+ char *name, *s;
+
+ name = malloc(3);
+ if (name) {
+ s = name;
+ *s++ = 'A' + (base_port % 26);
+ if (base_port >= 26)
+ *s++ = *name;
+ *s = '\0';
+ }
+
+ return name;
+}
+
+static const struct udevice_id tegra_gpio_ids[] = {
+ { .compatible = "nvidia,tegra30-gpio" },
+ { .compatible = "nvidia,tegra20-gpio" },
+ { }
+};
+
+static int gpio_tegra_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct tegra_port_info *priv = dev_get_priv(dev);
+ struct tegra_gpio_plat *plat = dev_get_plat(dev);
+
+ /* Only child devices have ports */
+ if (!plat)
+ return 0;
+
+ priv->bank = plat->bank;
+ priv->base_gpio = plat->base_gpio;
+
+ uc_priv->gpio_count = TEGRA_GPIOS_PER_PORT;
+ uc_priv->bank_name = plat->port_name;
+
+ return 0;
+}
+
+/**
+ * We have a top-level GPIO device with no actual GPIOs. It has a child
+ * device for each Tegra port.
+ */
+static int gpio_tegra_bind(struct udevice *parent)
+{
+ struct tegra_gpio_plat *plat = dev_get_plat(parent);
+ struct gpio_ctlr *ctlr;
+ int bank_count;
+ int bank;
+ int ret;
+
+ /* If this is a child device, there is nothing to do here */
+ if (plat)
+ return 0;
+
+ /* TODO(sjg@chromium.org): Remove once SPL supports device tree */
+#ifdef CONFIG_SPL_BUILD
+ ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+ bank_count = TEGRA_GPIO_BANKS;
+#else
+ {
+ int len;
+
+ /*
+ * This driver does not make use of interrupts, other than to figure
+ * out the number of GPIO banks
+ */
+ len = dev_read_size(parent, "interrupts");
+ if (len < 0)
+ return len;
+ bank_count = len / 3 / sizeof(u32);
+ ctlr = dev_read_addr_ptr(parent);
+ if (!ctlr)
+ return -EINVAL;
+ }
+#endif
+ for (bank = 0; bank < bank_count; bank++) {
+ int port;
+
+ for (port = 0; port < TEGRA_PORTS_PER_BANK; port++) {
+ struct tegra_gpio_plat *plat;
+ struct udevice *dev;
+ int base_port;
+
+ plat = calloc(1, sizeof(*plat));
+ if (!plat)
+ return -ENOMEM;
+ plat->bank = &ctlr->gpio_bank[bank];
+ base_port = bank * TEGRA_PORTS_PER_BANK + port;
+ plat->base_gpio = TEGRA_GPIOS_PER_PORT * base_port;
+ plat->port_name = gpio_port_name(base_port);
+
+ ret = device_bind(parent, parent->driver,
+ plat->port_name, plat,
+ dev_ofnode(parent), &dev);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_tegra) = {
+ .name = "gpio_tegra",
+ .id = UCLASS_GPIO,
+ .of_match = tegra_gpio_ids,
+ .bind = gpio_tegra_bind,
+ .probe = gpio_tegra_probe,
+ .priv_auto = sizeof(struct tegra_port_info),
+ .ops = &gpio_tegra_ops,
+};
diff --git a/drivers/gpio/turris_omnia_mcu.c b/drivers/gpio/turris_omnia_mcu.c
new file mode 100644
index 00000000000..2d2bf2d1dd6
--- /dev/null
+++ b/drivers/gpio/turris_omnia_mcu.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <common.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <linux/log2.h>
+
+enum commands_e {
+ CMD_GET_STATUS_WORD = 0x01,
+ CMD_GENERAL_CONTROL = 0x02,
+
+ /* available if STS_FEATURES_SUPPORTED bit set in status word */
+ CMD_GET_FEATURES = 0x10,
+
+ /* available if FEAT_EXT_CMDS bit is set in features */
+ CMD_GET_EXT_STATUS_DWORD = 0x11,
+
+ /* available if FEAT_EXT_CMDS and FEAT_PERIPH_MCU bits are set in featurs */
+ CMD_EXT_CONTROL = 0x12,
+ CMD_GET_EXT_CONTROL_STATUS = 0x13,
+};
+
+/* CMD_GET_STATUS_WORD */
+enum sts_word_e {
+ STS_MCU_TYPE_MASK = GENMASK(1, 0),
+ STS_MCU_TYPE_STM32 = 0,
+ STS_MCU_TYPE_GD32 = 1,
+ STS_MCU_TYPE_MKL = 2,
+ STS_FEATURES_SUPPORTED = BIT(2),
+ STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
+ STS_CARD_DET = BIT(4),
+ STS_MSATA_IND = BIT(5),
+ STS_USB30_OVC = BIT(6),
+ STS_USB31_OVC = BIT(7),
+ STS_USB30_PWRON = BIT(8),
+ STS_USB31_PWRON = BIT(9),
+ STS_ENABLE_4V5 = BIT(10),
+ STS_BUTTON_MODE = BIT(11),
+ STS_BUTTON_PRESSED = BIT(12),
+ STS_BUTTON_COUNTER_MASK = GENMASK(15, 13)
+};
+
+/* CMD_GENERAL_CONTROL */
+enum ctl_byte_e {
+ CTL_LIGHT_RST = BIT(0),
+ CTL_HARD_RST = BIT(1),
+ /*CTL_RESERVED = BIT(2),*/
+ CTL_USB30_PWRON = BIT(3),
+ CTL_USB31_PWRON = BIT(4),
+ CTL_ENABLE_4V5 = BIT(5),
+ CTL_BUTTON_MODE = BIT(6),
+ CTL_BOOTLOADER = BIT(7)
+};
+
+/* CMD_GET_FEATURES */
+enum features_e {
+ FEAT_PERIPH_MCU = BIT(0),
+ FEAT_EXT_CMDS = BIT(1),
+};
+
+struct turris_omnia_mcu_info {
+ u16 features;
+};
+
+static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
+{
+ struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+
+ switch (offset) {
+ /* bank 0 */
+ case 0 ... 15:
+ switch (offset) {
+ case ilog2(STS_USB30_PWRON):
+ case ilog2(STS_USB31_PWRON):
+ case ilog2(STS_ENABLE_4V5):
+ case ilog2(STS_BUTTON_MODE):
+ return GPIOF_OUTPUT;
+ default:
+ return GPIOF_INPUT;
+ }
+
+ /* bank 1 - supported only when FEAT_EXT_CMDS is set */
+ case (16 + 0) ... (16 + 31):
+ if (!(info->features & FEAT_EXT_CMDS))
+ return -EINVAL;
+ return GPIOF_INPUT;
+
+ /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
+ case (16 + 32 + 0) ... (16 + 32 + 15):
+ if (!(info->features & FEAT_EXT_CMDS))
+ return -EINVAL;
+ if (!(info->features & FEAT_PERIPH_MCU))
+ return -EINVAL;
+ return GPIOF_OUTPUT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
+{
+ struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+ u8 val16[2];
+ u8 val32[4];
+ int ret;
+
+ switch (offset) {
+ /* bank 0 */
+ case 0 ... 15:
+ ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
+ if (ret)
+ return ret;
+ return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
+
+ /* bank 1 - supported only when FEAT_EXT_CMDS is set */
+ case (16 + 0) ... (16 + 31):
+ if (!(info->features & FEAT_EXT_CMDS))
+ return -EINVAL;
+ ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
+ if (ret)
+ return ret;
+ return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
+ ((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
+
+ /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
+ case (16 + 32 + 0) ... (16 + 32 + 15):
+ if (!(info->features & FEAT_EXT_CMDS))
+ return -EINVAL;
+ if (!(info->features & FEAT_PERIPH_MCU))
+ return -EINVAL;
+ ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
+ if (ret)
+ return ret;
+ return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
+{
+ struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+ u8 val16[2];
+ u8 val32[4];
+
+ switch (offset) {
+ /* bank 0 */
+ case 0 ... 15:
+ switch (offset) {
+ case ilog2(STS_USB30_PWRON):
+ val16[1] = CTL_USB30_PWRON;
+ break;
+ case ilog2(STS_USB31_PWRON):
+ val16[1] = CTL_USB31_PWRON;
+ break;
+ case ilog2(STS_ENABLE_4V5):
+ val16[1] = CTL_ENABLE_4V5;
+ break;
+ case ilog2(STS_BUTTON_MODE):
+ val16[1] = CTL_BUTTON_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ val16[0] = value ? val16[1] : 0;
+ return dm_i2c_write(dev, CMD_GENERAL_CONTROL, val16, sizeof(val16));
+
+ /* bank 2 - supported only when FEAT_EXT_CMDS and FEAT_PERIPH_MCU is set */
+ case (16 + 32 + 0) ... (16 + 32 + 15):
+ if (!(info->features & FEAT_EXT_CMDS))
+ return -EINVAL;
+ if (!(info->features & FEAT_PERIPH_MCU))
+ return -EINVAL;
+ val32[3] = BIT(offset - 16 - 32) >> 8;
+ val32[2] = BIT(offset - 16 - 32) & 0xff;
+ val32[1] = value ? val32[3] : 0;
+ val32[0] = value ? val32[2] : 0;
+ return dm_i2c_write(dev, CMD_EXT_CONTROL, val32, sizeof(val32));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
+{
+ int ret;
+
+ ret = turris_omnia_mcu_get_function(dev, offset);
+ if (ret < 0)
+ return ret;
+ else if (ret != GPIOF_INPUT)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
+{
+ int ret;
+
+ ret = turris_omnia_mcu_get_function(dev, offset);
+ if (ret < 0)
+ return ret;
+ else if (ret != GPIOF_OUTPUT)
+ return -EOPNOTSUPP;
+
+ return turris_omnia_mcu_set_value(dev, offset, value);
+}
+
+static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ uint bank, gpio, flags, offset;
+ int ret;
+
+ if (args->args_count != 3)
+ return -EINVAL;
+
+ bank = args->args[0];
+ gpio = args->args[1];
+ flags = args->args[2];
+
+ switch (bank) {
+ case 0:
+ if (gpio >= 16)
+ return -EINVAL;
+ offset = gpio;
+ break;
+ case 1:
+ if (gpio >= 32)
+ return -EINVAL;
+ offset = 16 + gpio;
+ break;
+ case 2:
+ if (gpio >= 16)
+ return -EINVAL;
+ offset = 16 + 32 + gpio;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = turris_omnia_mcu_get_function(dev, offset);
+ if (ret < 0)
+ return ret;
+
+ desc->offset = offset;
+ desc->flags = gpio_flags_xlate(flags);
+
+ return 0;
+}
+
+static const struct dm_gpio_ops turris_omnia_mcu_ops = {
+ .direction_input = turris_omnia_mcu_direction_input,
+ .direction_output = turris_omnia_mcu_direction_output,
+ .get_value = turris_omnia_mcu_get_value,
+ .set_value = turris_omnia_mcu_set_value,
+ .get_function = turris_omnia_mcu_get_function,
+ .xlate = turris_omnia_mcu_xlate,
+};
+
+static int turris_omnia_mcu_probe(struct udevice *dev)
+{
+ struct turris_omnia_mcu_info *info = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ u16 status;
+ u8 val[2];
+ int ret;
+
+ ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
+ if (ret) {
+ printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
+ return ret;
+ }
+
+ status = ((u16)val[1] << 8) | val[0];
+
+ if (status & STS_FEATURES_SUPPORTED) {
+ ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
+ if (ret) {
+ printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
+ return ret;
+ }
+ info->features = ((u16)val[1] << 8) | val[0];
+ }
+
+ uc_priv->bank_name = "mcu_";
+
+ if ((info->features & FEAT_EXT_CMDS) && (info->features & FEAT_PERIPH_MCU))
+ uc_priv->gpio_count = 16 + 32 + 16;
+ else if (info->features & FEAT_EXT_CMDS)
+ uc_priv->gpio_count = 16 + 32;
+ else
+ uc_priv->gpio_count = 16;
+
+ return 0;
+}
+
+static const struct udevice_id turris_omnia_mcu_ids[] = {
+ { .compatible = "cznic,turris-omnia-mcu" },
+ { }
+};
+
+U_BOOT_DRIVER(turris_omnia_mcu) = {
+ .name = "turris-omnia-mcu",
+ .id = UCLASS_GPIO,
+ .ops = &turris_omnia_mcu_ops,
+ .probe = turris_omnia_mcu_probe,
+ .plat_auto = sizeof(struct turris_omnia_mcu_info),
+ .of_match = turris_omnia_mcu_ids,
+};
diff --git a/drivers/gpio/vybrid_gpio.c b/drivers/gpio/vybrid_gpio.c
new file mode 100644
index 00000000000..339392dcd35
--- /dev/null
+++ b/drivers/gpio/vybrid_gpio.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015
+ * Bhuvanchandra DV, Toradex, Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <asm/global_data.h>
+#include <asm/gpio.h>
+#include <asm/mach-imx/iomux-v3.h>
+#include <asm/io.h>
+#include <malloc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct vybrid_gpios {
+ unsigned int chip;
+ struct vybrid_gpio_regs *reg;
+};
+
+static int vybrid_gpio_direction_input(struct udevice *dev, unsigned gpio)
+{
+ const struct vybrid_gpios *gpios = dev_get_priv(dev);
+
+ gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT);
+ imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_IN);
+
+ return 0;
+}
+
+static int vybrid_gpio_direction_output(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ const struct vybrid_gpios *gpios = dev_get_priv(dev);
+
+ gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT);
+ gpio_set_value(gpio, value);
+ imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_OUT);
+
+ return 0;
+}
+
+static int vybrid_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ const struct vybrid_gpios *gpios = dev_get_priv(dev);
+
+ return ((readl(&gpios->reg->gpio_pdir) & (1 << gpio))) ? 1 : 0;
+}
+
+static int vybrid_gpio_set_value(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ const struct vybrid_gpios *gpios = dev_get_priv(dev);
+ if (value)
+ writel((1 << gpio), &gpios->reg->gpio_psor);
+ else
+ writel((1 << gpio), &gpios->reg->gpio_pcor);
+
+ return 0;
+}
+
+static int vybrid_gpio_get_function(struct udevice *dev, unsigned gpio)
+{
+ const struct vybrid_gpios *gpios = dev_get_priv(dev);
+ u32 g_state = 0;
+
+ gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT);
+
+ imx_iomux_gpio_get_function(gpio, &g_state);
+
+ if (((g_state & (0x07 << PAD_MUX_MODE_SHIFT)) >> PAD_MUX_MODE_SHIFT) > 0)
+ return GPIOF_FUNC;
+ if (g_state & PAD_CTL_OBE_ENABLE)
+ return GPIOF_OUTPUT;
+ if (g_state & PAD_CTL_IBE_ENABLE)
+ return GPIOF_INPUT;
+ if (!(g_state & PAD_CTL_OBE_IBE_ENABLE))
+ return GPIOF_UNUSED;
+
+ return GPIOF_UNKNOWN;
+}
+
+static const struct dm_gpio_ops gpio_vybrid_ops = {
+ .direction_input = vybrid_gpio_direction_input,
+ .direction_output = vybrid_gpio_direction_output,
+ .get_value = vybrid_gpio_get_value,
+ .set_value = vybrid_gpio_set_value,
+ .get_function = vybrid_gpio_get_function,
+};
+
+static int vybrid_gpio_probe(struct udevice *dev)
+{
+ struct vybrid_gpios *gpios = dev_get_priv(dev);
+ struct vybrid_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ uc_priv->bank_name = plat->port_name;
+ uc_priv->gpio_count = VYBRID_GPIO_COUNT;
+ gpios->reg = (struct vybrid_gpio_regs *)plat->base;
+ gpios->chip = plat->chip;
+
+ return 0;
+}
+
+static int vybrid_gpio_odata_to_plat(struct udevice *dev)
+{
+ struct vybrid_gpio_plat *plat = dev_get_plat(dev);
+ fdt_addr_t base_addr;
+
+ base_addr = dev_read_addr(dev);
+ if (base_addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->base = base_addr;
+ plat->chip = dev_seq(dev);
+ plat->port_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL);
+
+ return 0;
+}
+
+static const struct udevice_id vybrid_gpio_ids[] = {
+ { .compatible = "fsl,vf610-gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_vybrid) = {
+ .name = "gpio_vybrid",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_vybrid_ops,
+ .of_match = vybrid_gpio_ids,
+ .of_to_plat = vybrid_gpio_odata_to_plat,
+ .probe = vybrid_gpio_probe,
+ .priv_auto = sizeof(struct vybrid_gpios),
+ .plat_auto = sizeof(struct vybrid_gpio_plat),
+};
diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c
new file mode 100644
index 00000000000..fa8d630b465
--- /dev/null
+++ b/drivers/gpio/xilinx_gpio.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 - 2018 Xilinx, Michal Simek
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/list.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#define XILINX_GPIO_MAX_BANK 2
+
+/* Gpio simple map */
+struct gpio_regs {
+ u32 gpiodata;
+ u32 gpiodir;
+};
+
+struct xilinx_gpio_plat {
+ struct gpio_regs *regs;
+ int bank_max[XILINX_GPIO_MAX_BANK];
+ int bank_input[XILINX_GPIO_MAX_BANK];
+ int bank_output[XILINX_GPIO_MAX_BANK];
+ u32 dout_default[XILINX_GPIO_MAX_BANK];
+};
+
+struct xilinx_gpio_privdata {
+ u32 output_val[XILINX_GPIO_MAX_BANK];
+};
+
+static int xilinx_gpio_get_bank_pin(unsigned offset, u32 *bank_num,
+ u32 *bank_pin_num, struct udevice *dev)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ u32 bank, max_pins;
+ /* the first gpio is 0 not 1 */
+ u32 pin_num = offset;
+
+ for (bank = 0; bank < XILINX_GPIO_MAX_BANK; bank++) {
+ max_pins = plat->bank_max[bank];
+ if (pin_num < max_pins) {
+ debug("%s: found at bank 0x%x pin 0x%x\n", __func__,
+ bank, pin_num);
+ *bank_num = bank;
+ *bank_pin_num = pin_num;
+ return 0;
+ }
+ pin_num -= max_pins;
+ }
+
+ return -EINVAL;
+}
+
+static int xilinx_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ struct xilinx_gpio_privdata *priv = dev_get_priv(dev);
+ int val, ret;
+ u32 bank, pin;
+
+ ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
+ if (ret)
+ return ret;
+
+ val = priv->output_val[bank];
+
+ debug("%s: regs: %lx, value: %x, gpio: %x, bank %x, pin %x, out %x\n",
+ __func__, (ulong)plat->regs, value, offset, bank, pin, val);
+
+ if (value)
+ val = val | (1 << pin);
+ else
+ val = val & ~(1 << pin);
+
+ writel(val, &plat->regs->gpiodata + bank * 2);
+
+ priv->output_val[bank] = val;
+
+ return 0;
+};
+
+static int xilinx_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ struct xilinx_gpio_privdata *priv = dev_get_priv(dev);
+ int val, ret;
+ u32 bank, pin;
+
+ ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
+ if (ret)
+ return ret;
+
+ debug("%s: regs: %lx, gpio: %x, bank %x, pin %x\n", __func__,
+ (ulong)plat->regs, offset, bank, pin);
+
+ if (plat->bank_output[bank]) {
+ debug("%s: Read saved output value\n", __func__);
+ val = priv->output_val[bank];
+ } else {
+ debug("%s: Read input value from reg\n", __func__);
+ val = readl(&plat->regs->gpiodata + bank * 2);
+ }
+
+ val = !!(val & (1 << pin));
+
+ return val;
+};
+
+static int xilinx_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ int val, ret;
+ u32 bank, pin;
+
+ ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
+ if (ret)
+ return ret;
+
+ /* Check if all pins are inputs */
+ if (plat->bank_input[bank])
+ return GPIOF_INPUT;
+
+ /* Check if all pins are outputs */
+ if (plat->bank_output[bank])
+ return GPIOF_OUTPUT;
+
+ /* FIXME test on dual */
+ val = readl(&plat->regs->gpiodir + bank * 2);
+ val = !(val & (1 << pin));
+
+ /* input is 1 in reg but GPIOF_INPUT is 0 */
+ /* output is 0 in reg but GPIOF_OUTPUT is 1 */
+
+ return val;
+}
+
+static int xilinx_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ int val, ret;
+ u32 bank, pin;
+
+ ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
+ if (ret)
+ return ret;
+
+ /* can't change it if all is input by default */
+ if (plat->bank_input[bank])
+ return -EINVAL;
+
+ xilinx_gpio_set_value(dev, offset, value);
+
+ if (!plat->bank_output[bank]) {
+ val = readl(&plat->regs->gpiodir + bank * 2);
+ val = val & ~(1 << pin);
+ writel(val, &plat->regs->gpiodir + bank * 2);
+ }
+
+ return 0;
+}
+
+static int xilinx_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ int val, ret;
+ u32 bank, pin;
+
+ ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
+ if (ret)
+ return ret;
+
+ /* Already input */
+ if (plat->bank_input[bank])
+ return 0;
+
+ /* can't change it if all is output by default */
+ if (plat->bank_output[bank])
+ return -EINVAL;
+
+ val = readl(&plat->regs->gpiodir + bank * 2);
+ val = val | (1 << pin);
+ writel(val, &plat->regs->gpiodir + bank * 2);
+
+ return 0;
+}
+
+static int xilinx_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+
+ desc->offset = args->args[0];
+
+ debug("%s: argc: %x, [0]: %x, [1]: %x, [2]: %x\n", __func__,
+ args->args_count, args->args[0], args->args[1], args->args[2]);
+
+ /*
+ * The second cell is channel offset:
+ * 0 is first channel, 8 is second channel
+ *
+ * U-Boot driver just combine channels together that's why simply
+ * add amount of pins in second channel if present.
+ */
+ if (args->args[1]) {
+ if (!plat->bank_max[1]) {
+ printf("%s: %s has no second channel\n",
+ __func__, dev->name);
+ return -EINVAL;
+ }
+
+ desc->offset += plat->bank_max[0];
+ }
+
+ /* The third cell is optional */
+ if (args->args_count > 2)
+ desc->flags = (args->args[2] &
+ GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0);
+
+ debug("%s: offset %x, flags %lx\n",
+ __func__, desc->offset, desc->flags);
+ return 0;
+}
+
+static const struct dm_gpio_ops xilinx_gpio_ops = {
+ .direction_input = xilinx_gpio_direction_input,
+ .direction_output = xilinx_gpio_direction_output,
+ .get_value = xilinx_gpio_get_value,
+ .set_value = xilinx_gpio_set_value,
+ .get_function = xilinx_gpio_get_function,
+ .xlate = xilinx_gpio_xlate,
+};
+
+static int xilinx_gpio_probe(struct udevice *dev)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ struct xilinx_gpio_privdata *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *label_ptr;
+
+ label_ptr = dev_read_prop(dev, "label", NULL);
+ if (label_ptr) {
+ uc_priv->bank_name = strdup(label_ptr);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+ } else {
+ uc_priv->bank_name = dev->name;
+ }
+
+ uc_priv->gpio_count = plat->bank_max[0] + plat->bank_max[1];
+
+ priv->output_val[0] = plat->dout_default[0];
+
+ if (plat->bank_max[1])
+ priv->output_val[1] = plat->dout_default[1];
+
+ return 0;
+}
+
+static int xilinx_gpio_of_to_plat(struct udevice *dev)
+{
+ struct xilinx_gpio_plat *plat = dev_get_plat(dev);
+ int is_dual;
+
+ plat->regs = dev_read_addr_ptr(dev);
+
+ plat->bank_max[0] = dev_read_u32_default(dev, "xlnx,gpio-width", 0);
+ plat->bank_input[0] = dev_read_u32_default(dev, "xlnx,all-inputs", 0);
+ plat->bank_output[0] = dev_read_u32_default(dev, "xlnx,all-outputs", 0);
+ plat->dout_default[0] = dev_read_u32_default(dev, "xlnx,dout-default",
+ 0);
+
+ is_dual = dev_read_u32_default(dev, "xlnx,is-dual", 0);
+ if (is_dual) {
+ plat->bank_max[1] = dev_read_u32_default(dev,
+ "xlnx,gpio2-width", 0);
+ plat->bank_input[1] = dev_read_u32_default(dev,
+ "xlnx,all-inputs-2", 0);
+ plat->bank_output[1] = dev_read_u32_default(dev,
+ "xlnx,all-outputs-2", 0);
+ plat->dout_default[1] = dev_read_u32_default(dev,
+ "xlnx,dout-default-2", 0);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id xilinx_gpio_ids[] = {
+ { .compatible = "xlnx,xps-gpio-1.00.a",},
+ { }
+};
+
+U_BOOT_DRIVER(xilinx_gpio) = {
+ .name = "xlnx_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &xilinx_gpio_ops,
+ .of_match = xilinx_gpio_ids,
+ .of_to_plat = xilinx_gpio_of_to_plat,
+ .probe = xilinx_gpio_probe,
+ .plat_auto = sizeof(struct xilinx_gpio_plat),
+ .priv_auto = sizeof(struct xilinx_gpio_privdata),
+};
diff --git a/drivers/gpio/zynq_gpio.c b/drivers/gpio/zynq_gpio.c
new file mode 100644
index 00000000000..71a56127c0a
--- /dev/null
+++ b/drivers/gpio/zynq_gpio.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Xilinx Zynq GPIO device driver
+ *
+ * Copyright (C) 2015 DAVE Embedded Systems <devel@dave.eu>
+ *
+ * Most of code taken from linux kernel driver (linux/drivers/gpio/gpio-zynq.c)
+ * Copyright (C) 2009 - 2014 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+
+/* Maximum banks */
+#define ZYNQ_GPIO_MAX_BANK 4
+
+#define ZYNQ_GPIO_BANK0_NGPIO 32
+#define ZYNQ_GPIO_BANK1_NGPIO 22
+#define ZYNQ_GPIO_BANK2_NGPIO 32
+#define ZYNQ_GPIO_BANK3_NGPIO 32
+
+#define ZYNQ_GPIO_NR_GPIOS (ZYNQ_GPIO_BANK0_NGPIO + \
+ ZYNQ_GPIO_BANK1_NGPIO + \
+ ZYNQ_GPIO_BANK2_NGPIO + \
+ ZYNQ_GPIO_BANK3_NGPIO)
+
+#define ZYNQMP_GPIO_MAX_BANK 6
+
+#define ZYNQMP_GPIO_BANK0_NGPIO 26
+#define ZYNQMP_GPIO_BANK1_NGPIO 26
+#define ZYNQMP_GPIO_BANK2_NGPIO 26
+#define ZYNQMP_GPIO_BANK3_NGPIO 32
+#define ZYNQMP_GPIO_BANK4_NGPIO 32
+#define ZYNQMP_GPIO_BANK5_NGPIO 32
+
+#define ZYNQMP_GPIO_NR_GPIOS 174
+
+#define ZYNQ_GPIO_BANK0_PIN_MIN(str) 0
+#define ZYNQ_GPIO_BANK0_PIN_MAX(str) (ZYNQ_GPIO_BANK0_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK0_NGPIO - 1)
+#define ZYNQ_GPIO_BANK1_PIN_MIN(str) (ZYNQ_GPIO_BANK0_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK1_PIN_MAX(str) (ZYNQ_GPIO_BANK1_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK1_NGPIO - 1)
+#define ZYNQ_GPIO_BANK2_PIN_MIN(str) (ZYNQ_GPIO_BANK1_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK2_PIN_MAX(str) (ZYNQ_GPIO_BANK2_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK2_NGPIO - 1)
+#define ZYNQ_GPIO_BANK3_PIN_MIN(str) (ZYNQ_GPIO_BANK2_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK3_PIN_MAX(str) (ZYNQ_GPIO_BANK3_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK3_NGPIO - 1)
+#define ZYNQ_GPIO_BANK4_PIN_MIN(str) (ZYNQ_GPIO_BANK3_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK4_PIN_MAX(str) (ZYNQ_GPIO_BANK4_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK4_NGPIO - 1)
+#define ZYNQ_GPIO_BANK5_PIN_MIN(str) (ZYNQ_GPIO_BANK4_PIN_MAX(str) + 1)
+#define ZYNQ_GPIO_BANK5_PIN_MAX(str) (ZYNQ_GPIO_BANK5_PIN_MIN(str) + \
+ ZYNQ##str##_GPIO_BANK5_NGPIO - 1)
+
+/* Register offsets for the GPIO device */
+/* LSW Mask & Data -WO */
+#define ZYNQ_GPIO_DATA_LSW_OFFSET(BANK) (0x000 + (8 * BANK))
+/* MSW Mask & Data -WO */
+#define ZYNQ_GPIO_DATA_MSW_OFFSET(BANK) (0x004 + (8 * BANK))
+/* Data Register-RW */
+#define ZYNQ_GPIO_DATA_RO_OFFSET(BANK) (0x060 + (4 * BANK))
+/* Direction mode reg-RW */
+#define ZYNQ_GPIO_DIRM_OFFSET(BANK) (0x204 + (0x40 * BANK))
+/* Output enable reg-RW */
+#define ZYNQ_GPIO_OUTEN_OFFSET(BANK) (0x208 + (0x40 * BANK))
+/* Interrupt mask reg-RO */
+#define ZYNQ_GPIO_INTMASK_OFFSET(BANK) (0x20C + (0x40 * BANK))
+/* Interrupt enable reg-WO */
+#define ZYNQ_GPIO_INTEN_OFFSET(BANK) (0x210 + (0x40 * BANK))
+/* Interrupt disable reg-WO */
+#define ZYNQ_GPIO_INTDIS_OFFSET(BANK) (0x214 + (0x40 * BANK))
+/* Interrupt status reg-RO */
+#define ZYNQ_GPIO_INTSTS_OFFSET(BANK) (0x218 + (0x40 * BANK))
+/* Interrupt type reg-RW */
+#define ZYNQ_GPIO_INTTYPE_OFFSET(BANK) (0x21C + (0x40 * BANK))
+/* Interrupt polarity reg-RW */
+#define ZYNQ_GPIO_INTPOL_OFFSET(BANK) (0x220 + (0x40 * BANK))
+/* Interrupt on any, reg-RW */
+#define ZYNQ_GPIO_INTANY_OFFSET(BANK) (0x224 + (0x40 * BANK))
+
+/* Disable all interrupts mask */
+#define ZYNQ_GPIO_IXR_DISABLE_ALL 0xFFFFFFFF
+
+/* Mid pin number of a bank */
+#define ZYNQ_GPIO_MID_PIN_NUM 16
+
+/* GPIO upper 16 bit mask */
+#define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
+
+#define PMC_GPIO_NR_GPIOS 116
+#define PMC_GPIO_MAX_BANK 5
+
+struct zynq_gpio_plat {
+ phys_addr_t base;
+ const struct zynq_platform_data *p_data;
+};
+
+/**
+ * struct zynq_platform_data - zynq gpio platform data structure
+ * @label: string to store in gpio->label
+ * @ngpio: max number of gpio pins
+ * @max_bank: maximum number of gpio banks
+ * @bank_min: this array represents bank's min pin
+ * @bank_max: this array represents bank's max pin
+ */
+struct zynq_platform_data {
+ const char *label;
+ u16 ngpio;
+ u32 max_bank;
+ u32 bank_min[ZYNQMP_GPIO_MAX_BANK];
+ u32 bank_max[ZYNQMP_GPIO_MAX_BANK];
+};
+
+#define VERSAL_GPIO_NR_GPIOS 58
+#define VERSAL_GPIO_MAX_BANK 4
+
+static const struct zynq_platform_data versal_gpio_def = {
+ .label = "versal_gpio",
+ .ngpio = VERSAL_GPIO_NR_GPIOS,
+ .max_bank = VERSAL_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25,
+ .bank_min[3] = 26,
+ .bank_max[3] = 57,
+};
+
+static const struct zynq_platform_data pmc_gpio_def = {
+ .label = "pmc_gpio",
+ .ngpio = PMC_GPIO_NR_GPIOS,
+ .max_bank = PMC_GPIO_MAX_BANK,
+ .bank_min[0] = 0,
+ .bank_max[0] = 25,
+ .bank_min[1] = 26,
+ .bank_max[1] = 51,
+ .bank_min[3] = 52,
+ .bank_max[3] = 83,
+ .bank_min[4] = 84,
+ .bank_max[4] = 115,
+};
+
+static const struct zynq_platform_data zynqmp_gpio_def = {
+ .label = "zynqmp_gpio",
+ .ngpio = ZYNQMP_GPIO_NR_GPIOS,
+ .max_bank = ZYNQMP_GPIO_MAX_BANK,
+ .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(MP),
+ .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(MP),
+ .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(MP),
+ .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(MP),
+ .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(MP),
+ .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(MP),
+ .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(MP),
+ .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(MP),
+ .bank_min[4] = ZYNQ_GPIO_BANK4_PIN_MIN(MP),
+ .bank_max[4] = ZYNQ_GPIO_BANK4_PIN_MAX(MP),
+ .bank_min[5] = ZYNQ_GPIO_BANK5_PIN_MIN(MP),
+ .bank_max[5] = ZYNQ_GPIO_BANK5_PIN_MAX(MP),
+};
+
+static const struct zynq_platform_data zynq_gpio_def = {
+ .label = "zynq_gpio",
+ .ngpio = ZYNQ_GPIO_NR_GPIOS,
+ .max_bank = ZYNQ_GPIO_MAX_BANK,
+ .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
+ .bank_max[0] = ZYNQ_GPIO_BANK0_PIN_MAX(),
+ .bank_min[1] = ZYNQ_GPIO_BANK1_PIN_MIN(),
+ .bank_max[1] = ZYNQ_GPIO_BANK1_PIN_MAX(),
+ .bank_min[2] = ZYNQ_GPIO_BANK2_PIN_MIN(),
+ .bank_max[2] = ZYNQ_GPIO_BANK2_PIN_MAX(),
+ .bank_min[3] = ZYNQ_GPIO_BANK3_PIN_MIN(),
+ .bank_max[3] = ZYNQ_GPIO_BANK3_PIN_MAX(),
+};
+
+/**
+ * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank
+ * for a given pin in the GPIO device
+ * @pin_num: gpio pin number within the device
+ * @bank_num: an output parameter used to return the bank number of the gpio
+ * pin
+ * @bank_pin_num: an output parameter used to return pin number within a bank
+ * for the given gpio pin
+ *
+ * Returns the bank number and pin offset within the bank.
+ */
+static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
+ unsigned int *bank_num,
+ unsigned int *bank_pin_num,
+ struct udevice *dev)
+{
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+ u32 bank;
+
+ for (bank = 0; bank < plat->p_data->max_bank; bank++) {
+ if (pin_num >= plat->p_data->bank_min[bank] &&
+ pin_num <= plat->p_data->bank_max[bank]) {
+ *bank_num = bank;
+ *bank_pin_num = pin_num -
+ plat->p_data->bank_min[bank];
+ return;
+ }
+ }
+
+ if (bank >= plat->p_data->max_bank) {
+ printf("Invalid bank and pin num\n");
+ *bank_num = 0;
+ *bank_pin_num = 0;
+ }
+}
+
+static int gpio_is_valid(unsigned gpio, struct udevice *dev)
+{
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ return gpio < plat->p_data->ngpio;
+}
+
+static int check_gpio(unsigned gpio, struct udevice *dev)
+{
+ if (!gpio_is_valid(gpio, dev)) {
+ printf("ERROR : check_gpio: invalid GPIO %d\n", gpio);
+ return -1;
+ }
+ return 0;
+}
+
+static int zynq_gpio_get_value(struct udevice *dev, unsigned gpio)
+{
+ u32 data;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ if (check_gpio(gpio, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num, dev);
+
+ data = readl(plat->base +
+ ZYNQ_GPIO_DATA_RO_OFFSET(bank_num));
+
+ return (data >> bank_pin_num) & 1;
+}
+
+static int zynq_gpio_set_value(struct udevice *dev, unsigned gpio, int value)
+{
+ unsigned int reg_offset, bank_num, bank_pin_num;
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ if (check_gpio(gpio, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num, dev);
+
+ if (bank_pin_num >= ZYNQ_GPIO_MID_PIN_NUM) {
+ /* only 16 data bits in bit maskable reg */
+ bank_pin_num -= ZYNQ_GPIO_MID_PIN_NUM;
+ reg_offset = ZYNQ_GPIO_DATA_MSW_OFFSET(bank_num);
+ } else {
+ reg_offset = ZYNQ_GPIO_DATA_LSW_OFFSET(bank_num);
+ }
+
+ /*
+ * get the 32 bit value to be written to the mask/data register where
+ * the upper 16 bits is the mask and lower 16 bits is the data
+ */
+ value = !!value;
+ value = ~(1 << (bank_pin_num + ZYNQ_GPIO_MID_PIN_NUM)) &
+ ((value << bank_pin_num) | ZYNQ_GPIO_UPPER_MASK);
+
+ writel(value, plat->base + reg_offset);
+
+ return 0;
+}
+
+static int zynq_gpio_direction_input(struct udevice *dev, unsigned gpio)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ if (check_gpio(gpio, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num, dev);
+
+ /* bank 0 pins 7 and 8 are special and cannot be used as inputs */
+ if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
+ return -1;
+
+ /* clear the bit in direction mode reg to set the pin as input */
+ reg = readl(plat->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ reg &= ~BIT(bank_pin_num);
+ writel(reg, plat->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+
+ return 0;
+}
+
+static int zynq_gpio_direction_output(struct udevice *dev, unsigned gpio,
+ int value)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ if (check_gpio(gpio, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num, dev);
+
+ /* set the GPIO pin as output */
+ reg = readl(plat->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ reg |= BIT(bank_pin_num);
+ writel(reg, plat->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+
+ /* configure the output enable reg for the pin */
+ reg = readl(plat->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
+ reg |= BIT(bank_pin_num);
+ writel(reg, plat->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
+
+ /* set the state of the pin */
+ zynq_gpio_set_value(dev, gpio, value);
+ return 0;
+}
+
+static int zynq_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ u32 reg;
+ unsigned int bank_num, bank_pin_num;
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ if (check_gpio(offset, dev) < 0)
+ return -1;
+
+ zynq_gpio_get_bank_pin(offset, &bank_num, &bank_pin_num, dev);
+
+ /* set the GPIO pin as output */
+ reg = readl(plat->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
+ reg &= BIT(bank_pin_num);
+ if (reg)
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops gpio_zynq_ops = {
+ .direction_input = zynq_gpio_direction_input,
+ .direction_output = zynq_gpio_direction_output,
+ .get_value = zynq_gpio_get_value,
+ .set_value = zynq_gpio_set_value,
+ .get_function = zynq_gpio_get_function,
+};
+
+static const struct udevice_id zynq_gpio_ids[] = {
+ { .compatible = "xlnx,zynq-gpio-1.0",
+ .data = (ulong)&zynq_gpio_def},
+ { .compatible = "xlnx,zynqmp-gpio-1.0",
+ .data = (ulong)&zynqmp_gpio_def},
+ { .compatible = "xlnx,versal-gpio-1.0",
+ .data = (ulong)&versal_gpio_def},
+ { .compatible = "xlnx,pmc-gpio-1.0",
+ .data = (ulong)&pmc_gpio_def },
+ { }
+};
+
+static int zynq_gpio_probe(struct udevice *dev)
+{
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *label_ptr;
+
+ label_ptr = dev_read_prop(dev, "label", NULL);
+ if (label_ptr) {
+ uc_priv->bank_name = strdup(label_ptr);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+ } else {
+ uc_priv->bank_name = dev->name;
+ }
+
+ if (plat->p_data)
+ uc_priv->gpio_count = plat->p_data->ngpio;
+
+ return 0;
+}
+
+static int zynq_gpio_of_to_plat(struct udevice *dev)
+{
+ struct zynq_gpio_plat *plat = dev_get_plat(dev);
+
+ plat->base = (phys_addr_t)dev_read_addr(dev);
+
+ plat->p_data =
+ (struct zynq_platform_data *)dev_get_driver_data(dev);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(gpio_zynq) = {
+ .name = "gpio_zynq",
+ .id = UCLASS_GPIO,
+ .ops = &gpio_zynq_ops,
+ .of_match = zynq_gpio_ids,
+ .of_to_plat = zynq_gpio_of_to_plat,
+ .probe = zynq_gpio_probe,
+ .plat_auto = sizeof(struct zynq_gpio_plat),
+};
diff --git a/drivers/gpio/zynqmp_gpio_modepin.c b/drivers/gpio/zynqmp_gpio_modepin.c
new file mode 100644
index 00000000000..e9565ff5430
--- /dev/null
+++ b/drivers/gpio/zynqmp_gpio_modepin.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ZynqMP GPIO modepin driver
+ *
+ * Copyright (C) 2021 Xilinx, Inc.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <dm.h>
+#include <asm/arch/hardware.h>
+#include <zynqmp_firmware.h>
+
+#define OUTEN(pin) (BIT(0) << (pin))
+#define INVAL(pin) (BIT(4) << (pin))
+#define OUTVAL(pin) (BIT(8) << (pin))
+
+#define ZYNQMP_CRL_APB_BOOTPIN_CTRL_MASK 0xF0F
+#define ZYNQMP_CRL_APB_BOOT_PIN_CTRL (ZYNQMP_CRL_APB_BASEADDR + \
+ (0x250U))
+
+static int get_gpio_modepin(u32 *ret_payload)
+{
+ return xilinx_pm_request(PM_MMIO_READ, ZYNQMP_CRL_APB_BOOT_PIN_CTRL,
+ 0, 0, 0, ret_payload);
+}
+
+static int set_gpio_modepin(int val)
+{
+ return xilinx_pm_request(PM_MMIO_WRITE, ZYNQMP_CRL_APB_BOOT_PIN_CTRL,
+ ZYNQMP_CRL_APB_BOOTPIN_CTRL_MASK,
+ val, 0, NULL);
+}
+
+static int modepin_gpio_direction_input(struct udevice *dev,
+ unsigned int offset)
+{
+ return 0;
+}
+
+static int modepin_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 out_val = 0;
+ int ret;
+
+ ret = get_gpio_modepin(ret_payload);
+ if (ret)
+ return ret;
+
+ if (value)
+ out_val = OUTVAL(offset) | ret_payload[1];
+ else
+ out_val = ~OUTVAL(offset) & ret_payload[1];
+
+ return set_gpio_modepin(out_val);
+}
+
+static int modepin_gpio_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ u32 out_en = 0;
+ int ret;
+
+ ret = get_gpio_modepin(ret_payload);
+ if (ret)
+ return ret;
+
+ if (value)
+ out_en = OUTEN(offset) | ret_payload[1];
+ else
+ out_en = ~OUTEN(offset) & ret_payload[1];
+
+ ret = set_gpio_modepin(out_en);
+ if (ret)
+ return ret;
+
+ return modepin_gpio_set_value(dev, offset, value);
+}
+
+static int modepin_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct ofnode_phandle_args *args)
+{
+ desc->offset = args->args[0];
+
+ return 0;
+}
+
+static int modepin_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ ret = get_gpio_modepin(ret_payload);
+ if (ret)
+ return ret;
+
+ return (INVAL(offset) & ret_payload[1]) ? 1 : 0;
+}
+
+static int modepin_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ u32 ret_payload[PAYLOAD_ARG_CNT];
+ int ret;
+
+ ret = get_gpio_modepin(ret_payload);
+ if (ret)
+ return ret;
+
+ return (OUTEN(offset) & ret_payload[1]) ? GPIOF_OUTPUT : GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops modepin_gpio_ops = {
+ .direction_input = modepin_gpio_direction_input,
+ .direction_output = modepin_gpio_direction_output,
+ .get_value = modepin_gpio_get_value,
+ .set_value = modepin_gpio_set_value,
+ .get_function = modepin_gpio_get_function,
+ .xlate = modepin_gpio_xlate,
+};
+
+static int modepin_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ const void *label_ptr;
+
+ label_ptr = dev_read_prop(dev, "label", NULL);
+ if (label_ptr) {
+ uc_priv->bank_name = strdup(label_ptr);
+ if (!uc_priv->bank_name)
+ return -ENOMEM;
+ } else {
+ uc_priv->bank_name = dev->name;
+ }
+
+ uc_priv->gpio_count = 4;
+
+ return 0;
+}
+
+static const struct udevice_id modepin_gpio_ids[] = {
+ { .compatible = "xlnx,zynqmp-gpio-modepin",},
+ { }
+};
+
+U_BOOT_DRIVER(modepin_gpio) = {
+ .name = "modepin_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &modepin_gpio_ops,
+ .of_match = modepin_gpio_ids,
+ .probe = modepin_gpio_probe,
+};