diff options
Diffstat (limited to 'drivers/gpio')
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(®s->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(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin); + /* change the data first, then the direction. to avoid glitch */ + setbits_le32(®s->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(®s->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(®s->data, 1 << pin); + else + clrbits_le32(®s->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)µchip_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(®s->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 ? ®s->set : ®s->clear, 1 << pin); + setbits_le32(®s->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(®s->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 ? ®s->set : ®s->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(®s->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(®s->gpio_pddr); + + return val & (1 << offset) ? 1 : 0; +} + +static int imx_rgpio2p_bank_get_direction(struct gpio_regs *regs, int offset) +{ + if ((readl(®s->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(®s->gpio_pddr); + + switch (direction) { + case IMX_RGPIO2P_DIRECTION_OUT: + l |= 1 << offset; + break; + case IMX_RGPIO2P_DIRECTION_IN: + l &= ~(1 << offset); + } + writel(l, ®s->gpio_pddr); +} + +static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset, + int value) +{ + if (value) + writel((1 << offset), ®s->gpio_psor); + else + writel((1 << offset), ®s->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(®s->gpio_pdir) >> offset) & 0x01; + + return (readl(®s->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(®s->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(®s->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(®s->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(®s->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(®s->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(®s->own[priv->bank]) & mask)) + return GPIOF_FUNC; + if (inl(®s->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, ®s->p0_dir_clr); + break; + case 1: + writel(mask, ®s->p1_dir_clr); + break; + case 2: + /* ports 2 and 3 share a common direction */ + writel(mask, ®s->p2_p3_dir_clr); + break; + case 3: + /* Setup direction only for GPIO_xx. */ + if ((mask >= 25) && (mask <= 30)) + writel(mask, ®s->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(®s->p0_inp_state); + break; + case 1: + value = readl(®s->p1_inp_state); + break; + case 2: + value = readl(®s->p2_inp_state); + break; + case 3: + /* Read GPO_xx and GPIO_xx (as output) using p3_outp_state. */ + value = readl(®s->p3_outp_state); + break; + case 4: + /* Read GPI_xx and GPIO_xx (as input) using p3_inp_state. */ + value = readl(®s->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, ®s->p0_outp_set); + break; + case 1: + writel(mask, ®s->p1_outp_set); + break; + case 2: + writel(mask, ®s->p2_outp_set); + break; + case 3: + writel(mask, ®s->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, ®s->p0_outp_clr); + break; + case 1: + writel(mask, ®s->p1_outp_clr); + break; + case 2: + writel(mask, ®s->p2_outp_clr); + break; + case 3: + writel(mask, ®s->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, ®s->p0_dir_set); + break; + case 1: + writel(mask, ®s->p1_dir_set); + break; + case 2: + /* ports 2 and 3 share a common direction */ + writel(mask, ®s->p2_p3_dir_set); + break; + case 3: + /* Setup direction only for GPIO_xx. */ + if ((mask >= 25) && (mask <= 30)) + writel(mask, ®s->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(®s->pdat) & mask; +} + +static inline u16 gpio16_get_dir(void __iomem *base, u16 mask, int type) +{ + struct iop_16 *regs = base; + + return in_be16(®s->pdir) & mask; +} + +static inline void gpio16_set_in(void __iomem *base, u16 gpios, int type) +{ + struct iop_16 *regs = base; + + clrbits_be16(®s->pdat, gpios); + /* GPDIR register 0 -> input */ + clrbits_be16(®s->pdir, gpios); +} + +static inline void gpio16_set_lo(void __iomem *base, u16 gpios, int type) +{ + struct iop_16 *regs = base; + + clrbits_be16(®s->pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be16(®s->pdir, gpios); +} + +static inline void gpio16_set_hi(void __iomem *base, u16 gpios, int type) +{ + struct iop_16 *regs = base; + + setbits_be16(®s->pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be16(®s->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(®s->b.pdat) & mask; + else + return in_be32(®s->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(®s->b.pdir) & mask; + else + return in_be32(®s->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(®s->b.pdat, gpios); + /* GPDIR register 0 -> input */ + clrbits_be32(®s->b.pdir, gpios); + } else { /* Port E */ + clrbits_be32(®s->e.pdat, gpios); + /* GPDIR register 0 -> input */ + clrbits_be32(®s->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(®s->b.pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be32(®s->b.pdir, gpios); + } else { + clrbits_be32(®s->e.pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be32(®s->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(®s->b.pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be32(®s->b.pdir, gpios); + } else { + setbits_be32(®s->e.pdat, gpios); + /* GPDIR register 1 -> output */ + setbits_be32(®s->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(®s->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(®s->data_out, BIT(gpio)); + else + clrbits_le32(®s->data_out, BIT(gpio)); + clrbits_le32(®s->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(®s->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(®s->data_out, BIT(gpio)); + else + clrbits_le32(®s->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(®s->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(®s->gpio_dir); + + switch (direction) { + case MXC_GPIO_DIRECTION_OUT: + l |= 1 << gpio; + break; + case MXC_GPIO_DIRECTION_IN: + l &= ~(1 << gpio); + } + writel(l, ®s->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(®s->gpio_dr); + if (value) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + writel(l, ®s->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(®s->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(®s->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(®s->gpio_dir); + + switch (direction) { + case MXC_GPIO_DIRECTION_OUT: + l |= 1 << offset; + break; + case MXC_GPIO_DIRECTION_IN: + l &= ~(1 << offset); + } + writel(l, ®s->gpio_dir); +} + +static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset, + int value) +{ + u32 l; + + l = readl(®s->gpio_dr); + if (value) + l |= 1 << offset; + else + l &= ~(1 << offset); + writel(l, ®s->gpio_dr); +} + +static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset) +{ + return (readl(®s->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) >> 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_set); + else + writel(1 << PAD_PIN(gpio), ®->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_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_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) >> 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_set); + else + writel(BIT(offset), ®->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_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_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) >> 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(®s->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(®s->data, 1 << pin); + else + setbits_le32(®s->pad_reset, 1 << pin); + + setbits_le32(®s->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(®s->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(®s->data, 1 << pin); + else + clrbits_le32(®s->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(®s->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(®s->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(®s->data, 1 << pin); + else + clrbits_le32(®s->data, 1 << pin); + + setbits_le32(®s->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(®s->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(®s->data, 1 << pin); + else + clrbits_le32(®s->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(®s->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(®s->swport_dr, 1 << gpio, value << gpio); + + /* Set direction */ + clrsetbits_le32(®s->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(®s->moder, mask, mode << bits_index); +} + +static int stm32_gpio_get_moder(struct stm32_gpio_regs *regs, int idx) +{ + return (readl(®s->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(®s->otyper, OTYPE_MSK << bits, otype << bits); +} + +static enum stm32_gpio_otype stm32_gpio_get_otype(struct stm32_gpio_regs *regs, + int idx) +{ + return (readl(®s->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(®s->pupdr, PUPD_MASK << bits, pupd << bits); +} + +static enum stm32_gpio_pupd stm32_gpio_get_pupd(struct stm32_gpio_regs *regs, + int idx) +{ + return (readl(®s->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), ®s->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(®s->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), ®s->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(®s->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), ®s->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(®s->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, +}; |