diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/gpio | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/gpio')
66 files changed, 17370 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/gpio/74x164_gpio.c b/roms/u-boot/drivers/gpio/74x164_gpio.c new file mode 100644 index 000000000..7a7cfe861 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/Kconfig b/roms/u-boot/drivers/gpio/Kconfig new file mode 100644 index 000000000..d24884739 --- /dev/null +++ b/roms/u-boot/drivers/gpio/Kconfig @@ -0,0 +1,498 @@ +# +# GPIO infrastructure and drivers +# + +menu "GPIO Support" + +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_SUPPORT + 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_SUPPORT + 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 GPIO_HOG + bool "Enable GPIO hog support" + depends on DM_GPIO + default n + 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 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 DM_GPIO && SPL_DM && SPL_GPIO_SUPPORT + 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 BCM6345_GPIO + bool "BCM6345 GPIO driver" + depends on DM_GPIO && (ARCH_BMIPS || ARCH_BCM68360 || \ + ARCH_BCM6858 || ARCH_BCM63158) + 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 + default n + help + Support for the Designware APB GPIO driver. + +config AT91_GPIO + bool "AT91 PIO GPIO driver" + default n + 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 + default n + 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 DA8XX_GPIO + bool "DA8xx GPIO Driver" + help + This driver supports the DA8xx GPIO controller + +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 + default n + help + This driver supports i.MX7ULP Rapid GPIO2P controller. + +config IPROC_GPIO + bool "Broadcom iProc GPIO driver(without pinconf)" + default n + 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 + default n + help + This driver supports CREG GPIOs on Synopsys HSDK SOC. + +config LPC32XX_GPIO + bool "LPC32XX GPIO driver" + depends on DM + default n + help + Support for the LPC32XX GPIO driver. + +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 + default n + 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 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 PM8916_GPIO + bool "Qualcomm PM8916 PMIC GPIO/keypad driver" + depends on DM_GPIO && PMIC_PM8916 + help + Support for GPIO pins and power/reset buttons found on + Qualcomm PM8916 PMIC. + Default name for GPIO bank is "pm8916". + Power and reset buttons are placed in "pm8916_key" bank and + have gpio numbers 0 and 1 respectively. + +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 + help + Support the GPIO device in Allwinner SoCs. + +config XILINX_GPIO + bool "Xilinx GPIO driver" + depends on DM_GPIO + help + This config enable the Xilinx GPIO driver for Microblaze. + +config CMD_TCA642X + bool "tca642x - Command to access tca642x state" + 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 + default n + help + Say yes here to support Vybrid vf610 GPIOs. + +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 && DM_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 + 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 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 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 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_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. + +endmenu diff --git a/roms/u-boot/drivers/gpio/Makefile b/roms/u-boot/drivers/gpio/Makefile new file mode 100644 index 000000000..8541ba0b0 --- /dev/null +++ b/roms/u-boot/drivers/gpio/Makefile @@ -0,0 +1,71 @@ +# 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_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_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_KONA_GPIO) += kona_gpio.o +obj-$(CONFIG_MARVELL_GPIO) += mvgpio.o +obj-$(CONFIG_MARVELL_MFP) += mvmfp.o +obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o +obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o +obj-$(CONFIG_PCA953X) += pca953x.o +obj-$(CONFIG_PCA9698) += pca9698.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 +obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o +obj-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o +obj-$(CONFIG_TEGRA186_GPIO) += tegra186_gpio.o +obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o +obj-$(CONFIG_DM644X_GPIO) += da8xx_gpio.o +obj-$(CONFIG_ALTERA_PIO) += altera_pio.o +obj-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o +obj-$(CONFIG_MPC8XXX_GPIO) += mpc8xxx_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_DB8500_GPIO) += db8500_gpio.o +obj-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o +obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o +obj-$(CONFIG_ADI_GPIO2) += adi_gpio2.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_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_PM8916_GPIO) += pm8916_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 diff --git a/roms/u-boot/drivers/gpio/adi_gpio2.c b/roms/u-boot/drivers/gpio/adi_gpio2.c new file mode 100644 index 000000000..d0849c85c --- /dev/null +++ b/roms/u-boot/drivers/gpio/adi_gpio2.c @@ -0,0 +1,425 @@ +/* + * ADI GPIO2 Abstraction Layer + * Support BF54x, BF60x and future processors. + * + * Copyright 2008-2013 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include <common.h> +#include <malloc.h> +#include <linux/bug.h> +#include <linux/errno.h> +#include <asm/gpio.h> + +#define RESOURCE_LABEL_SIZE 16 + +static struct str_ident { + char name[RESOURCE_LABEL_SIZE]; +} str_ident[MAX_RESOURCES]; + +static void gpio_error(unsigned gpio) +{ + printf("adi_gpio2: GPIO %d wasn't requested!\n", gpio); +} + +static void set_label(unsigned short ident, const char *label) +{ + if (label) { + strncpy(str_ident[ident].name, label, + RESOURCE_LABEL_SIZE); + str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; + } +} + +static char *get_label(unsigned short ident) +{ + return *str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"; +} + +static int cmp_label(unsigned short ident, const char *label) +{ + if (label == NULL) + printf("adi_gpio2: please provide none-null label\n"); + + if (label) + return strcmp(str_ident[ident].name, label); + else + return -EINVAL; +} + +#define map_entry(m, i) reserved_##m##_map[gpio_bank(i)] +#define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) +#define reserve(m, i) (map_entry(m, i) |= gpio_bit(i)) +#define unreserve(m, i) (map_entry(m, i) &= ~gpio_bit(i)) +#define DECLARE_RESERVED_MAP(m, c) unsigned short reserved_##m##_map[c] + +static DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +static DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); + +inline int check_gpio(unsigned gpio) +{ +#if defined(CONFIG_BF54x) + if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 || + gpio == GPIO_PH14 || gpio == GPIO_PH15 || + gpio == GPIO_PJ14 || gpio == GPIO_PJ15) + return -EINVAL; +#endif + if (gpio >= MAX_GPIOS) + return -EINVAL; + return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ +#if defined(CONFIG_BF54x) + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); +#else + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer_clear = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer_set = gpio_bit(gpio); +#endif +} + +inline void portmux_setup(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + u16 function = P_FUNCT2MUX(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); + pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); + + gpio_array[gpio_bank(ident)]->port_mux = pmux; +} + +inline u16 get_portmux(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + return pmux >> (2 * gpio_sub_n(ident)) & 0x3; +} + +unsigned short get_gpio_dir(unsigned gpio) +{ + return 0x01 & + (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); +} + +/*********************************************************** +* +* FUNCTIONS: Peripheral Resource Allocation +* and PortMux Setup +* +* INPUTS/OUTPUTS: +* per Peripheral Identifier +* label String +* +* DESCRIPTION: Peripheral Resource Allocation and Setup API +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ + unsigned short ident = P_IDENT(per); + + /* + * Don't cares are pins with only one dedicated function + */ + + if (per & P_DONTCARE) + return 0; + + if (!(per & P_DEFINED)) + return -EINVAL; + + BUG_ON(ident >= MAX_RESOURCES); + + /* If a pin can be muxed as either GPIO or peripheral, make + * sure it is not already a GPIO pin when we request it. + */ + if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { + printf("%s: Peripheral %d is already reserved as GPIO by %s!\n", + __func__, ident, get_label(ident)); + return -EBUSY; + } + + if (unlikely(is_reserved(peri, ident, 1))) { + /* + * Pin functions like AMC address strobes my + * be requested and used by several drivers + */ + + if (!((per & P_MAYSHARE) && + get_portmux(per) == P_FUNCT2MUX(per))) { + /* + * Allow that the identical pin function can + * be requested from the same driver twice + */ + + if (cmp_label(ident, label) == 0) + goto anyway; + + printf("%s: Peripheral %d function %d is already " + "reserved by %s!\n", __func__, ident, + P_FUNCT2MUX(per), get_label(ident)); + return -EBUSY; + } + } + + anyway: + reserve(peri, ident); + + portmux_setup(per); + port_setup(ident, PERIPHERAL_USAGE); + + set_label(ident, label); + + return 0; +} + +int peripheral_request_list(const unsigned short per[], const char *label) +{ + u16 cnt; + int ret; + + for (cnt = 0; per[cnt] != 0; cnt++) { + ret = peripheral_request(per[cnt], label); + + if (ret < 0) { + for (; cnt > 0; cnt--) + peripheral_free(per[cnt - 1]); + + return ret; + } + } + + return 0; +} + +void peripheral_free(unsigned short per) +{ + unsigned short ident = P_IDENT(per); + + if (per & P_DONTCARE) + return; + + if (!(per & P_DEFINED)) + return; + + if (unlikely(!is_reserved(peri, ident, 0))) + return; + + if (!(per & P_MAYSHARE)) + port_setup(ident, GPIO_USAGE); + + unreserve(peri, ident); + + set_label(ident, "free"); +} + +void peripheral_free_list(const unsigned short per[]) +{ + u16 cnt; + for (cnt = 0; per[cnt] != 0; cnt++) + peripheral_free(per[cnt]); +} + +/*********************************************************** +* +* FUNCTIONS: GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio PIO Number between 0 and MAX_GPIOS +* label String +* +* DESCRIPTION: GPIO Driver API +**************************************************************/ + +int gpio_request(unsigned gpio, const char *label) +{ + if (check_gpio(gpio) < 0) + return -EINVAL; + + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + return -EBUSY; + } + + reserve(gpio, gpio); + set_label(gpio, label); + + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (check_gpio(gpio) < 0) + return -1; + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + gpio_error(gpio); + return -1; + } + + unreserve(gpio, gpio); + + set_label(gpio, "free"); + + return 0; +} + +#ifdef ADI_SPECIAL_GPIO_BANKS +static DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int special_gpio_request(unsigned gpio, const char *label) +{ + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(special_gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + + return -EBUSY; + } + + reserve(special_gpio, gpio); + reserve(peri, gpio); + + set_label(gpio, label); + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +void special_gpio_free(unsigned gpio) +{ + if (unlikely(!is_reserved(special_gpio, gpio, 0))) { + gpio_error(gpio); + return; + } + + unreserve(special_gpio, gpio); + unreserve(peri, gpio); + set_label(gpio, "free"); +} +#endif + +static inline void __gpio_direction_input(unsigned gpio) +{ + gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_set = gpio_bit(gpio); +#endif +} + +int gpio_direction_input(unsigned gpio) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + __gpio_direction_input(gpio); + local_irq_restore(flags); + + return 0; +} + +int gpio_set_value(unsigned gpio, int arg) +{ + if (arg) + gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_clear = gpio_bit(gpio); +#endif + gpio_set_value(gpio, value); + gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); + + local_irq_restore(flags); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return 1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); +} + +void gpio_labels(void) +{ + int c, gpio; + + for (c = 0; c < MAX_RESOURCES; c++) { + gpio = is_reserved(gpio, c, 1); + if (!check_gpio(c) && gpio) + printf("GPIO_%d:\t%s\tGPIO %s\n", c, get_label(c), + get_gpio_dir(c) ? "OUTPUT" : "INPUT"); + else if (is_reserved(peri, c, 1)) + printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); + else + continue; + } +} diff --git a/roms/u-boot/drivers/gpio/altera_pio.c b/roms/u-boot/drivers/gpio/altera_pio.c new file mode 100644 index 000000000..edc5a8093 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/at91_gpio.c b/roms/u-boot/drivers/gpio/at91_gpio.c new file mode 100644 index 000000000..1409db5dc --- /dev/null +++ b/roms/u-boot/drivers/gpio/at91_gpio.c @@ -0,0 +1,638 @@ +// 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; + + clk_free(&clk); + +#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/roms/u-boot/drivers/gpio/atmel_pio4.c b/roms/u-boot/drivers/gpio/atmel_pio4.c new file mode 100644 index 000000000..bea609db9 --- /dev/null +++ b/roms/u-boot/drivers/gpio/atmel_pio4.c @@ -0,0 +1,367 @@ +// 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; + 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; + + clk_free(&clk); + + 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[] = { + { + .compatible = "atmel,sama5d2-gpio", + .data = (ulong)&atmel_sama5d2_pioctrl_data, + }, { + .compatible = "microchip,sama7g5-gpio", + .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/roms/u-boot/drivers/gpio/axp_gpio.c b/roms/u-boot/drivers/gpio/axp_gpio.c new file mode 100644 index 000000000..73058cf40 --- /dev/null +++ b/roms/u-boot/drivers/gpio/axp_gpio.c @@ -0,0 +1,179 @@ +// 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/gpio.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> + +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; + + switch (pin) { +#ifndef CONFIG_AXP152_POWER /* NA on axp152 */ + case SUNXI_GPIO_AXP0_VBUS_DETECT: + return 0; +#endif + default: + 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) { +#ifndef CONFIG_AXP152_POWER /* NA on axp152 */ + case SUNXI_GPIO_AXP0_VBUS_DETECT: + ret = pmic_bus_read(AXP_POWER_STATUS, &val); + mask = AXP_POWER_STATUS_VBUS_PRESENT; + break; +#endif +#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/roms/u-boot/drivers/gpio/bcm2835_gpio.c b/roms/u-boot/drivers/gpio/bcm2835_gpio.c new file mode 100644 index 000000000..704a6fa71 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/bcm6345_gpio.c b/roms/u-boot/drivers/gpio/bcm6345_gpio.c new file mode 100644 index 000000000..e031f71a7 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/cortina_gpio.c b/roms/u-boot/drivers/gpio/cortina_gpio.c new file mode 100644 index 000000000..72ef523be --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/da8xx_gpio.c b/roms/u-boot/drivers/gpio/da8xx_gpio.c new file mode 100644 index 000000000..d106e9846 --- /dev/null +++ b/roms/u-boot/drivers/gpio/da8xx_gpio.c @@ -0,0 +1,569 @@ +// 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; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = addr; + 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/roms/u-boot/drivers/gpio/da8xx_gpio.h b/roms/u-boot/drivers/gpio/da8xx_gpio.h new file mode 100644 index 000000000..ca59d6a90 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/db8500_gpio.c b/roms/u-boot/drivers/gpio/db8500_gpio.c new file mode 100644 index 000000000..eefb56d83 --- /dev/null +++ b/roms/u-boot/drivers/gpio/db8500_gpio.c @@ -0,0 +1,221 @@ +/* + * Code ported from Nomadik GPIO driver in ST-Ericsson Linux kernel code. + * The purpose is that GPIO config found in kernel should work by simply + * copy-paste it to U-Boot. + * + * Original Linux authors: + * Copyright (C) 2008,2009 STMicroelectronics + * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it> + * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com> + * + * Ported to U-Boot by: + * Copyright (C) 2010 Joakim Axelsson <joakim.axelsson AT stericsson.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <asm/io.h> + +#include <asm/arch/db8500_gpio.h> +#include <asm/arch/db8500_pincfg.h> +#include <linux/compiler.h> + +#define IO_ADDR(x) (void *) (x) + +/* + * The GPIO module in the db8500 Systems-on-Chip is an + * AMBA device, managing 32 pins and alternate functions. The logic block + * is currently only used in the db8500. + */ + +#define GPIO_TOTAL_PINS 268 +#define GPIO_PINS_PER_BLOCK 32 +#define GPIO_BLOCKS_COUNT (GPIO_TOTAL_PINS/GPIO_PINS_PER_BLOCK + 1) +#define GPIO_BLOCK(pin) (((pin + GPIO_PINS_PER_BLOCK) >> 5) - 1) +#define GPIO_PIN_WITHIN_BLOCK(pin) ((pin)%(GPIO_PINS_PER_BLOCK)) + +/* Register in the logic block */ +#define DB8500_GPIO_DAT 0x00 +#define DB8500_GPIO_DATS 0x04 +#define DB8500_GPIO_DATC 0x08 +#define DB8500_GPIO_PDIS 0x0c +#define DB8500_GPIO_DIR 0x10 +#define DB8500_GPIO_DIRS 0x14 +#define DB8500_GPIO_DIRC 0x18 +#define DB8500_GPIO_SLPC 0x1c +#define DB8500_GPIO_AFSLA 0x20 +#define DB8500_GPIO_AFSLB 0x24 + +#define DB8500_GPIO_RIMSC 0x40 +#define DB8500_GPIO_FIMSC 0x44 +#define DB8500_GPIO_IS 0x48 +#define DB8500_GPIO_IC 0x4c +#define DB8500_GPIO_RWIMSC 0x50 +#define DB8500_GPIO_FWIMSC 0x54 +#define DB8500_GPIO_WKS 0x58 + +static void __iomem *get_gpio_addr(unsigned gpio) +{ + /* Our list of GPIO chips */ + static void __iomem *gpio_addrs[GPIO_BLOCKS_COUNT] = { + IO_ADDR(CFG_GPIO_0_BASE), + IO_ADDR(CFG_GPIO_1_BASE), + IO_ADDR(CFG_GPIO_2_BASE), + IO_ADDR(CFG_GPIO_3_BASE), + IO_ADDR(CFG_GPIO_4_BASE), + IO_ADDR(CFG_GPIO_5_BASE), + IO_ADDR(CFG_GPIO_6_BASE), + IO_ADDR(CFG_GPIO_7_BASE), + IO_ADDR(CFG_GPIO_8_BASE) + }; + + return gpio_addrs[GPIO_BLOCK(gpio)]; +} + +static unsigned get_gpio_offset(unsigned gpio) +{ + return GPIO_PIN_WITHIN_BLOCK(gpio); +} + +/* Can only be called from config_pin. Don't configure alt-mode directly */ +static void gpio_set_mode(unsigned gpio, enum db8500_gpio_alt mode) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + u32 afunc, bfunc; + + afunc = readl(addr + DB8500_GPIO_AFSLA) & ~bit; + bfunc = readl(addr + DB8500_GPIO_AFSLB) & ~bit; + if (mode & DB8500_GPIO_ALT_A) + afunc |= bit; + if (mode & DB8500_GPIO_ALT_B) + bfunc |= bit; + writel(afunc, addr + DB8500_GPIO_AFSLA); + writel(bfunc, addr + DB8500_GPIO_AFSLB); +} + +/** + * db8500_gpio_set_pull() - enable/disable pull up/down on a gpio + * @gpio: pin number + * @pull: one of DB8500_GPIO_PULL_DOWN, DB8500_GPIO_PULL_UP, + * and DB8500_GPIO_PULL_NONE + * + * Enables/disables pull up/down on a specified pin. This only takes effect if + * the pin is configured as an input (either explicitly or by the alternate + * function). + * + * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is + * configured as an input. Otherwise, due to the way the controller registers + * work, this function will change the value output on the pin. + */ +void db8500_gpio_set_pull(unsigned gpio, enum db8500_gpio_pull pull) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + u32 pdis; + + pdis = readl(addr + DB8500_GPIO_PDIS); + if (pull == DB8500_GPIO_PULL_NONE) + pdis |= bit; + else + pdis &= ~bit; + writel(pdis, addr + DB8500_GPIO_PDIS); + + if (pull == DB8500_GPIO_PULL_UP) + writel(bit, addr + DB8500_GPIO_DATS); + else if (pull == DB8500_GPIO_PULL_DOWN) + writel(bit, addr + DB8500_GPIO_DATC); +} + +void db8500_gpio_make_input(unsigned gpio) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + writel(1 << offset, addr + DB8500_GPIO_DIRC); +} + +int db8500_gpio_get_input(unsigned gpio) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + u32 bit = 1 << offset; + + printf("db8500_gpio_get_input gpio=%u addr=%p offset=%u bit=%#x\n", + gpio, addr, offset, bit); + + return (readl(addr + DB8500_GPIO_DAT) & bit) != 0; +} + +void db8500_gpio_make_output(unsigned gpio, int val) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + writel(1 << offset, addr + DB8500_GPIO_DIRS); + db8500_gpio_set_output(gpio, val); +} + +void db8500_gpio_set_output(unsigned gpio, int val) +{ + void __iomem *addr = get_gpio_addr(gpio); + unsigned offset = get_gpio_offset(gpio); + + if (val) + writel(1 << offset, addr + DB8500_GPIO_DATS); + else + writel(1 << offset, addr + DB8500_GPIO_DATC); +} + +/** + * config_pin - configure a pin's mux attributes + * @cfg: pin configuration + * + * Configures a pin's mode (alternate function or GPIO), its pull up status, + * and its sleep mode based on the specified configuration. The @cfg is + * usually one of the SoC specific macros defined in mach/<soc>-pins.h. These + * are constructed using, and can be further enhanced with, the macros in + * plat/pincfg.h. + * + * If a pin's mode is set to GPIO, it is configured as an input to avoid + * side-effects. The gpio can be manipulated later using standard GPIO API + * calls. + */ +static void config_pin(unsigned long cfg) +{ + int pin = PIN_NUM(cfg); + int pull = PIN_PULL(cfg); + int af = PIN_ALT(cfg); + int output = PIN_DIR(cfg); + int val = PIN_VAL(cfg); + + if (output) + db8500_gpio_make_output(pin, val); + else { + db8500_gpio_make_input(pin); + db8500_gpio_set_pull(pin, pull); + } + + gpio_set_mode(pin, af); +} + +/** + * db8500_config_pins - configure several pins at once + * @cfgs: array of pin configurations + * @num: number of elments in the array + * + * Configures several pins using config_pin(). Refer to that function for + * further information. + */ +void db8500_gpio_config_pins(unsigned long *cfgs, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) + config_pin(cfgs[i]); +} diff --git a/roms/u-boot/drivers/gpio/dwapb_gpio.c b/roms/u-boot/drivers/gpio/dwapb_gpio.c new file mode 100644 index 000000000..e6e919444 --- /dev/null +++ b/roms/u-boot/drivers/gpio/dwapb_gpio.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Marek Vasut <marex@denx.de> + * + * DesignWare APB GPIO driver + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/arch/gpio.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <errno.h> +#include <reset.h> +#include <linux/bitops.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/roms/u-boot/drivers/gpio/gpio-rcar.c b/roms/u-boot/drivers/gpio/gpio-rcar.c new file mode 100644 index 000000000..5f1ec39a9 --- /dev/null +++ b/roms/u-boot/drivers/gpio/gpio-rcar.c @@ -0,0 +1,194 @@ +// 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 RCAR_MAX_GPIO_PER_BANK 32 + +DECLARE_GLOBAL_DATA_PTR; + +struct rcar_gpio_priv { + void __iomem *regs; + 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 "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 int rcar_gpio_request(struct udevice *dev, unsigned offset, + const char *label) +{ + return pinctrl_gpio_request(dev, offset); +} + +static int rcar_gpio_free(struct udevice *dev, unsigned offset) +{ + return pinctrl_gpio_free(dev, offset); +} + +static const struct dm_gpio_ops rcar_gpio_ops = { + .request = rcar_gpio_request, + .rfree = rcar_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); + 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); + clk_free(&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,rcar-gen2-gpio" }, + { .compatible = "renesas,rcar-gen3-gpio" }, + { /* 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/roms/u-boot/drivers/gpio/gpio-rza1.c b/roms/u-boot/drivers/gpio/gpio-rza1.c new file mode 100644 index 000000000..f14be871e --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/gpio-uclass.c b/roms/u-boot/drivers/gpio/gpio-uclass.c new file mode 100644 index 000000000..131099cc1 --- /dev/null +++ b/roms/u-boot/drivers/gpio/gpio-uclass.c @@ -0,0 +1,1459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +#define LOG_CATEGORY UCLASS_GPIO + +#include <common.h> +#include <dm.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; + +/** + * 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; + int ret; + + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = 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 ret ? ret : -ENOENT; +} + +#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 len; + int i; + + *offset = -1; + len = strlen(name); + for (i = 0; i < uc_priv->gpio_count; i++) { + if (!uc_priv->name[i]) + continue; + if (!strncmp(name, uc_priv->name[i], len)) { + *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; + int ret; + + numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1; + for (ret = uclass_first_device(UCLASS_GPIO, &dev); + dev; + ret = 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)) + 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 ret ? ret : -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; +} + +int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc, + struct ofnode_phandle_args *args) +{ + if (args->args_count < 1) + return -EINVAL; + + desc->offset = args->args[0]; + + if (args->args_count < 2) + return 0; + + desc->flags = 0; + if (args->args[1] & GPIO_ACTIVE_LOW) + desc->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 (args->args[1] & GPIO_SINGLE_ENDED) { + if (args->args[1] & GPIO_LINE_OPEN_DRAIN) + desc->flags |= GPIOD_OPEN_DRAIN; + else + desc->flags |= GPIOD_OPEN_SOURCE; + } + + if (args->args[1] & GPIO_PULL_UP) + desc->flags |= GPIOD_PULL_UP; + + if (args->args[1] & GPIO_PULL_DOWN) + desc->flags |= GPIOD_PULL_DOWN; + + 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 defined(CONFIG_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_probe_all(void) +{ + struct udevice *dev; + int ret; + int retval = 0; + + for (uclass_first_device(UCLASS_NOP, &dev); + dev; + uclass_find_next_device(&dev)) { + if (dev->driver == DM_DRIVER_GET(gpio_hog)) { + ret = device_probe(dev); + if (ret) { + printf("Failed to probe device %s err: %d\n", + dev->name, ret); + retval = ret; + } + } + } + + return retval; +} + +int gpio_hog_lookup_name(const char *name, struct gpio_desc **desc) +{ + struct udevice *dev; + + *desc = NULL; + gpio_hog_probe_all(); + 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 (uc_priv->name[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; + } + } + 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 (!uc_priv->name[offset]) + return -ENXIO; + if (ops->rfree) { + ret = ops->rfree(dev, offset); + if (ret) + return ret; + } + + 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 (!uc_priv->name[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 && !uc_priv->name[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; + int func; + int ret; + int len; + + BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function)); + + *buf = 0; + priv = dev_get_uclass_priv(dev); + ret = gpio_get_raw_function(dev, offset, NULL); + if (ret < 0) + return ret; + func = ret; + len = snprintf(str, buffsize, "%s%d: %s", + priv->bank_name ? priv->bank_name : "", + offset, gpio_function[func]); + if (func == GPIOF_INPUT || func == GPIOF_OUTPUT || + func == GPIOF_UNUSED) { + const char *label; + bool used; + + 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' : ' ', + used ? " " : "", + label ? label : ""); + } + + 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) { + debug("%s: uclass_get_device_by_ofnode failed\n", + __func__); + goto err; + } + } + 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_PLATDATA) +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_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 - 1); + + 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 */ + +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; + + 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->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) +{ + struct udevice *child; + ofnode node; + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + struct dm_gpio_ops *ops = (struct dm_gpio_ops *)device_get_ops(dev); + static int reloc_done; + + if (!reloc_done) { + if (ops->request) + ops->request += gd->reloc_off; + if (ops->rfree) + ops->rfree += gd->reloc_off; + if (ops->direction_input) + ops->direction_input += gd->reloc_off; + if (ops->direction_output) + ops->direction_output += gd->reloc_off; + if (ops->get_value) + ops->get_value += gd->reloc_off; + if (ops->set_value) + ops->set_value += gd->reloc_off; + if (ops->get_function) + ops->get_function += gd->reloc_off; + if (ops->xlate) + ops->xlate += gd->reloc_off; + if (ops->set_flags) + ops->set_flags += gd->reloc_off; + if (ops->get_flags) + ops->get_flags += gd->reloc_off; + + reloc_done++; + } +#endif + + if (IS_ENABLED(CONFIG_GPIO_HOG)) { + 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; + } + } + } + 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/roms/u-boot/drivers/gpio/gpio-uniphier.c b/roms/u-boot/drivers/gpio/gpio-uniphier.c new file mode 100644 index 000000000..61c705b5a --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/hi6220_gpio.c b/roms/u-boot/drivers/gpio/hi6220_gpio.c new file mode 100644 index 000000000..04f8d904a --- /dev/null +++ b/roms/u-boot/drivers/gpio/hi6220_gpio.c @@ -0,0 +1,95 @@ +// 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/roms/u-boot/drivers/gpio/hsdk-creg-gpio.c b/roms/u-boot/drivers/gpio/hsdk-creg-gpio.c new file mode 100644 index 000000000..66f844184 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/imx_rgpio2p.c b/roms/u-boot/drivers/gpio/imx_rgpio2p.c new file mode 100644 index 000000000..0e2874ca9 --- /dev/null +++ b/roms/u-boot/drivers/gpio/imx_rgpio2p.c @@ -0,0 +1,227 @@ +// 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 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) +{ + return (readl(®s->gpio_pdir) >> 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/roms/u-boot/drivers/gpio/intel_broadwell_gpio.c b/roms/u-boot/drivers/gpio/intel_broadwell_gpio.c new file mode 100644 index 000000000..20af35de2 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/intel_gpio.c b/roms/u-boot/drivers/gpio/intel_gpio.c new file mode 100644 index 000000000..f15ce7b59 --- /dev/null +++ b/roms/u-boot/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_PLATDATA) +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/roms/u-boot/drivers/gpio/intel_ich6_gpio.c b/roms/u-boot/drivers/gpio/intel_ich6_gpio.c new file mode 100644 index 000000000..63a07b959 --- /dev/null +++ b/roms/u-boot/drivers/gpio/intel_ich6_gpio.c @@ -0,0 +1,242 @@ +// 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. + */ + +#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))) { + debug("%s: gpio %d is reserved for internal use\n", __func__, + 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/roms/u-boot/drivers/gpio/iproc_gpio.c b/roms/u-boot/drivers/gpio/iproc_gpio.c new file mode 100644 index 000000000..8c143e9b7 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/kona_gpio.c b/roms/u-boot/drivers/gpio/kona_gpio.c new file mode 100644 index 000000000..29791882a --- /dev/null +++ b/roms/u-boot/drivers/gpio/kona_gpio.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2013 Broadcom Corporation. + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/sysmap.h> + +#define GPIO_BASE (void *)GPIO2_BASE_ADDR + +#define GPIO_PASSWD 0x00a5a501 +#define GPIO_PER_BANK 32 +#define GPIO_MAX_BANK_NUM 8 + +#define GPIO_BANK(gpio) ((gpio) >> 5) +#define GPIO_BITMASK(gpio) \ + (1UL << ((gpio) & (GPIO_PER_BANK - 1))) + +#define GPIO_OUT_STATUS(bank) (0x00000000 + ((bank) << 2)) +#define GPIO_IN_STATUS(bank) (0x00000020 + ((bank) << 2)) +#define GPIO_OUT_SET(bank) (0x00000040 + ((bank) << 2)) +#define GPIO_OUT_CLEAR(bank) (0x00000060 + ((bank) << 2)) +#define GPIO_INT_STATUS(bank) (0x00000080 + ((bank) << 2)) +#define GPIO_INT_MASK(bank) (0x000000a0 + ((bank) << 2)) +#define GPIO_INT_MSKCLR(bank) (0x000000c0 + ((bank) << 2)) +#define GPIO_CONTROL(bank) (0x00000100 + ((bank) << 2)) +#define GPIO_PWD_STATUS(bank) (0x00000500 + ((bank) << 2)) + +#define GPIO_GPPWR_OFFSET 0x00000520 + +#define GPIO_GPCTR0_DBR_SHIFT 5 +#define GPIO_GPCTR0_DBR_MASK 0x000001e0 + +#define GPIO_GPCTR0_ITR_SHIFT 3 +#define GPIO_GPCTR0_ITR_MASK 0x00000018 +#define GPIO_GPCTR0_ITR_CMD_RISING_EDGE 0x00000001 +#define GPIO_GPCTR0_ITR_CMD_FALLING_EDGE 0x00000002 +#define GPIO_GPCTR0_ITR_CMD_BOTH_EDGE 0x00000003 + +#define GPIO_GPCTR0_IOTR_MASK 0x00000001 +#define GPIO_GPCTR0_IOTR_CMD_0UTPUT 0x00000000 +#define GPIO_GPCTR0_IOTR_CMD_INPUT 0x00000001 + +int gpio_request(unsigned gpio, const char *label) +{ + unsigned int value, off; + + writel(GPIO_PASSWD, GPIO_BASE + GPIO_GPPWR_OFFSET); + off = GPIO_PWD_STATUS(GPIO_BANK(gpio)); + value = readl(GPIO_BASE + off) & ~GPIO_BITMASK(gpio); + writel(value, GPIO_BASE + off); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + unsigned int value, off; + + writel(GPIO_PASSWD, GPIO_BASE + GPIO_GPPWR_OFFSET); + off = GPIO_PWD_STATUS(GPIO_BANK(gpio)); + value = readl(GPIO_BASE + off) | GPIO_BITMASK(gpio); + writel(value, GPIO_BASE + off); + + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + u32 val; + + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_INPUT; + writel(val, GPIO_BASE + GPIO_CONTROL(gpio)); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= ~GPIO_GPCTR0_IOTR_MASK; + val |= GPIO_GPCTR0_IOTR_CMD_0UTPUT; + writel(val, GPIO_BASE + GPIO_CONTROL(gpio)); + off = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(GPIO_BASE + off); + val |= bitmask; + writel(val, GPIO_BASE + off); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + /* determine the GPIO pin direction */ + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* read the GPIO bank status */ + off = (GPIO_GPCTR0_IOTR_CMD_INPUT == val) ? + GPIO_IN_STATUS(bank_id) : GPIO_OUT_STATUS(bank_id); + val = readl(GPIO_BASE + off); + + /* return the specified bit status */ + return !!(val & bitmask); +} + +void gpio_set_value(unsigned gpio, int value) +{ + int bank_id = GPIO_BANK(gpio); + int bitmask = GPIO_BITMASK(gpio); + u32 val, off; + + /* determine the GPIO pin direction */ + val = readl(GPIO_BASE + GPIO_CONTROL(gpio)); + val &= GPIO_GPCTR0_IOTR_MASK; + + /* this function only applies to output pin */ + if (GPIO_GPCTR0_IOTR_CMD_INPUT == val) { + printf("%s: Cannot set an input pin %d\n", __func__, gpio); + return; + } + + off = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); + + val = readl(GPIO_BASE + off); + val |= bitmask; + writel(val, GPIO_BASE + off); +} diff --git a/roms/u-boot/drivers/gpio/kw_gpio.c b/roms/u-boot/drivers/gpio/kw_gpio.c new file mode 100644 index 000000000..a15769793 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/lpc32xx_gpio.c b/roms/u-boot/drivers/gpio/lpc32xx_gpio.c new file mode 100644 index 000000000..de66c765d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/mpc83xx_gpio.c b/roms/u-boot/drivers/gpio/mpc83xx_gpio.c new file mode 100644 index 000000000..276a3b350 --- /dev/null +++ b/roms/u-boot/drivers/gpio/mpc83xx_gpio.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Freescale MPC83xx GPIO handling. + */ + +#include <common.h> +#include <malloc.h> +#include <mpc83xx.h> +#include <asm/gpio.h> +#include <asm/io.h> + +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION +#define CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION +#define CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN +#define CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN +#define CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_0_INIT_VALUE +#define CONFIG_MPC83XX_GPIO_0_INIT_VALUE 0 +#endif +#ifndef CONFIG_MPC83XX_GPIO_1_INIT_VALUE +#define CONFIG_MPC83XX_GPIO_1_INIT_VALUE 0 +#endif + +static unsigned int gpio_output_value[MPC83XX_GPIO_CTRLRS]; + +/* + * Generic_GPIO primitives. + */ + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + /* Do not set to input */ + return 0; +} + +/* set GPIO pin 'gpio' as an input */ +int gpio_direction_input(unsigned gpio) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + clrbits_be32(&im->gpio[ctrlr].dir, line_mask); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +int gpio_direction_output(unsigned gpio, int value) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + if (value != 0 && value != 1) { + printf("Error: Value parameter must be 0 or 1.\n"); + return -1; + } + + gpio_set_value(gpio, value); + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Make the line output */ + setbits_be32(&im->gpio[ctrlr].dir, line_mask); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +int gpio_get_value(unsigned gpio) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Read the value and mask off the bit */ + return (in_be32(&im->gpio[ctrlr].dat) & line_mask) != 0; +} + +/* write GPIO OUT value to pin 'gpio' */ +int gpio_set_value(unsigned gpio, int value) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + unsigned int ctrlr; + unsigned int line; + unsigned int line_mask; + + if (value != 0 && value != 1) { + printf("Error: Value parameter must be 0 or 1.\n"); + return -1; + } + + /* 32-bits per controller */ + ctrlr = gpio >> 5; + line = gpio & (0x1F); + + /* Big endian */ + line_mask = 1 << (31 - line); + + /* Update the local output buffer soft copy */ + gpio_output_value[ctrlr] = + (gpio_output_value[ctrlr] & ~line_mask) | \ + (value ? line_mask : 0); + + /* Write the output */ + out_be32(&im->gpio[ctrlr].dat, gpio_output_value[ctrlr]); + + return 0; +} + +/* Configure GPIO registers early */ +void mpc83xx_gpio_init_f(void) +{ + immap_t *im = (immap_t *)CONFIG_SYS_IMMR; + +#if MPC83XX_GPIO_CTRLRS >= 1 + out_be32(&im->gpio[0].dir, CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION); + out_be32(&im->gpio[0].odr, CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN); + out_be32(&im->gpio[0].dat, CONFIG_MPC83XX_GPIO_0_INIT_VALUE); + out_be32(&im->gpio[0].ier, 0xFFFFFFFF); /* Clear all events */ + out_be32(&im->gpio[0].imr, 0); + out_be32(&im->gpio[0].icr, 0); +#endif + +#if MPC83XX_GPIO_CTRLRS >= 2 + out_be32(&im->gpio[1].dir, CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION); + out_be32(&im->gpio[1].odr, CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN); + out_be32(&im->gpio[1].dat, CONFIG_MPC83XX_GPIO_1_INIT_VALUE); + out_be32(&im->gpio[1].ier, 0xFFFFFFFF); /* Clear all events */ + out_be32(&im->gpio[1].imr, 0); + out_be32(&im->gpio[1].icr, 0); +#endif +} + +/* Initialize GPIO soft-copies */ +void mpc83xx_gpio_init_r(void) +{ +#if MPC83XX_GPIO_CTRLRS >= 1 + gpio_output_value[0] = CONFIG_MPC83XX_GPIO_0_INIT_VALUE; +#endif + +#if MPC83XX_GPIO_CTRLRS >= 2 + gpio_output_value[1] = CONFIG_MPC83XX_GPIO_1_INIT_VALUE; +#endif +} diff --git a/roms/u-boot/drivers/gpio/mpc83xx_spisel_boot.c b/roms/u-boot/drivers/gpio/mpc83xx_spisel_boot.c new file mode 100644 index 000000000..fd26a36a0 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/mpc8xxx_gpio.c b/roms/u-boot/drivers/gpio/mpc8xxx_gpio.c new file mode 100644 index 000000000..f7ffd8926 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/mscc_sgpio.c b/roms/u-boot/drivers/gpio/mscc_sgpio.c new file mode 100644 index 000000000..1cbcc4348 --- /dev/null +++ b/roms/u-boot/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 = (u32 __iomem *)dev_read_addr(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/roms/u-boot/drivers/gpio/msm_gpio.c b/roms/u-boot/drivers/gpio/msm_gpio.c new file mode 100644 index 000000000..e1ff84c1c --- /dev/null +++ b/roms/u-boot/drivers/gpio/msm_gpio.c @@ -0,0 +1,134 @@ +// 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> + +DECLARE_GLOBAL_DATA_PTR; + +/* Register offsets */ +#define GPIO_CONFIG_OFF(no) ((no) * 0x1000) +#define GPIO_IN_OUT_OFF(no) ((no) * 0x1000 + 0x4) + +/* 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; +}; + +static int msm_gpio_direction_input(struct udevice *dev, unsigned int gpio) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); + + /* Disable OE bit */ + clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_DISABLE); + + return 0; +} + +static int msm_gpio_set_value(struct udevice *dev, unsigned 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_OFF(gpio)); + + return 0; +} + +static int msm_gpio_direction_output(struct udevice *dev, unsigned gpio, + int value) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); + + value = !!value; + /* set value */ + writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_OFF(gpio)); + /* switch direction */ + clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_ENABLE); + + return 0; +} + +static int msm_gpio_get_value(struct udevice *dev, unsigned gpio) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + return !!(readl(priv->base + GPIO_IN_OUT_OFF(gpio)) >> GPIO_IN); +} + +static int msm_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + if (readl(priv->base + GPIO_CONFIG_OFF(offset)) & 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); + + 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); + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "gpio-count", 0); + 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; +} + +static const struct udevice_id msm_gpio_ids[] = { + { .compatible = "qcom,msm8916-pinctrl" }, + { .compatible = "qcom,apq8016-pinctrl" }, + { .compatible = "qcom,ipq4019-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(gpio_msm) = { + .name = "gpio_msm", + .id = UCLASS_GPIO, + .of_match = msm_gpio_ids, + .of_to_plat = msm_gpio_of_to_plat, + .probe = msm_gpio_probe, + .ops = &gpio_msm_ops, + .priv_auto = sizeof(struct msm_gpio_bank), +}; diff --git a/roms/u-boot/drivers/gpio/mt7620_gpio.c b/roms/u-boot/drivers/gpio/mt7620_gpio.c new file mode 100644 index 000000000..713fa2ed7 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/mt7621_gpio.c b/roms/u-boot/drivers/gpio/mt7621_gpio.c new file mode 100644 index 000000000..43bb4df4d --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/mvebu_gpio.c b/roms/u-boot/drivers/gpio/mvebu_gpio.c new file mode 100644 index 000000000..4c1c68ee1 --- /dev/null +++ b/roms/u-boot/drivers/gpio/mvebu_gpio.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Stefan Roese <sr@denx.de> + */ + +#include <common.h> +#include <dm.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[2]; +}; + +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 = MVEBU_GPIOS_PER_BANK; + priv->name[0] = 'A' + dev_seq(dev); + uc_priv->bank_name = priv->name; + + return 0; +} + +static const struct dm_gpio_ops mvebu_gpio_ops = { + .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/roms/u-boot/drivers/gpio/mvgpio.c b/roms/u-boot/drivers/gpio/mvgpio.c new file mode 100644 index 000000000..12e7197da --- /dev/null +++ b/roms/u-boot/drivers/gpio/mvgpio.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * eInfochips Ltd. <www.einfochips.com> + * Written-by: Ajay Bhargav <contact@8051projects.net> + * + * (C) Copyright 2010 + * Marvell Semiconductor <www.marvell.com> + */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/errno.h> +#include "mvgpio.h" +#include <asm/gpio.h> + +#ifndef MV_MAX_GPIO +#define MV_MAX_GPIO 128 +#endif + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO requested %d\n", __func__, gpio); + return -1; + } + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +int gpio_direction_input(unsigned gpio) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gcdr); + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gsdr); + gpio_set_value(gpio, value); + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct gpio_reg *gpio_reg_bank; + u32 gpio_val; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + gpio_val = readl(&gpio_reg_bank->gplr); + + return GPIO_VAL(gpio, gpio_val); +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_reg *gpio_reg_bank; + + if (gpio >= MV_MAX_GPIO) { + printf("%s: Invalid GPIO %d\n", __func__, gpio); + return -1; + } + + gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio)); + if (value) + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpsr); + else + writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpcr); + + return 0; +} diff --git a/roms/u-boot/drivers/gpio/mvgpio.h b/roms/u-boot/drivers/gpio/mvgpio.h new file mode 100644 index 000000000..d68c48e63 --- /dev/null +++ b/roms/u-boot/drivers/gpio/mvgpio.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2011 + * eInfochips Ltd. <www.einfochips.com> + * Written-by: Ajay Bhargav <contact@8051projects.net> + * + * (C) Copyright 2010 + * Marvell Semiconductor <www.marvell.com> + */ + +#ifndef __MVGPIO_H__ +#define __MVGPIO_H__ + +#include <common.h> + +/* + * GPIO Register map for Marvell SOCs + */ +struct gpio_reg { + u32 gplr; /* Pin Level Register - 0x0000 */ + u32 pad0[2]; + u32 gpdr; /* Pin Direction Register - 0x000C */ + u32 pad1[2]; + u32 gpsr; /* Pin Output Set Register - 0x0018 */ + u32 pad2[2]; + u32 gpcr; /* Pin Output Clear Register - 0x0024 */ + u32 pad3[2]; + u32 grer; /* Rising-Edge Detect Enable Register - 0x0030 */ + u32 pad4[2]; + u32 gfer; /* Falling-Edge Detect Enable Register - 0x003C */ + u32 pad5[2]; + u32 gedr; /* Edge Detect Status Register - 0x0048 */ + u32 pad6[2]; + u32 gsdr; /* Bitwise Set of GPIO Direction Register - 0x0054 */ + u32 pad7[2]; + u32 gcdr; /* Bitwise Clear of GPIO Direction Register - 0x0060 */ + u32 pad8[2]; + u32 gsrer; /* Bitwise Set of Rising-Edge Detect Enable + Register - 0x006C */ + u32 pad9[2]; + u32 gcrer; /* Bitwise Clear of Rising-Edge Detect Enable + Register - 0x0078 */ + u32 pad10[2]; + u32 gsfer; /* Bitwise Set of Falling-Edge Detect Enable + Register - 0x0084 */ + u32 pad11[2]; + u32 gcfer; /* Bitwise Clear of Falling-Edge Detect Enable + Register - 0x0090 */ + u32 pad12[2]; + u32 apmask; /* Bitwise Mask of Edge Detect Register - 0x009C */ +}; + +#endif /* __MVGPIO_H__ */ diff --git a/roms/u-boot/drivers/gpio/mvmfp.c b/roms/u-boot/drivers/gpio/mvmfp.c new file mode 100644 index 000000000..511042c19 --- /dev/null +++ b/roms/u-boot/drivers/gpio/mvmfp.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2010 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com>, + */ + +#include <common.h> +#include <asm/io.h> +#include <mvmfp.h> +#include <asm/arch/mfp.h> + +/* + * mfp_config + * + * On most of Marvell SoCs (ex. ARMADA100) there is Multi-Funtion-Pin + * configuration registers to configure each GPIO/Function pin on the + * SoC. + * + * This function reads the array of values for + * MFPR_X registers and programms them into respective + * Multi-Function Pin registers. + * It supports - Alternate Function Selection programming. + * + * Whereas, + * The Configureation value is constructed using MFP() + * array consists of 32bit values as defined in MFP(xx,xx..) macro + */ +void mfp_config(u32 *mfp_cfgs) +{ + u32 *p_mfpr = NULL; + u32 cfg_val, val; + + do { + cfg_val = *mfp_cfgs++; + /* exit if End of configuration table detected */ + if (cfg_val == MFP_EOC) + break; + + p_mfpr = (u32 *)(MV_MFPR_BASE + + MFP_REG_GET_OFFSET(cfg_val)); + + /* Write a mfg register as per configuration */ + val = 0; + if (cfg_val & MFP_VALUE_MASK) + val |= cfg_val & MFP_VALUE_MASK; + + writel(val, p_mfpr); + } while (1); + /* + * perform a read-back of any MFPR register to make sure the + * previous writings are finished + */ + readl(p_mfpr); +} diff --git a/roms/u-boot/drivers/gpio/mxc_gpio.c b/roms/u-boot/drivers/gpio/mxc_gpio.c new file mode 100644 index 000000000..06e6b2279 --- /dev/null +++ b/roms/u-boot/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_MX25) || defined(CONFIG_MX27) || 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_MX27) || 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_MX25) || defined(CONFIG_MX27) || 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_MX27) || 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_MX25) || defined(CONFIG_MX27) || 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_MX27) || 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/roms/u-boot/drivers/gpio/mxs_gpio.c b/roms/u-boot/drivers/gpio/mxs_gpio.c new file mode 100644 index 000000000..5775a22ab --- /dev/null +++ b/roms/u-boot/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 = simple_strtoul(name, &end, 10); + + if (!*end || *end != ':') + return bank; + + pin = simple_strtoul(end + 1, NULL, 10); + + 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_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +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_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + .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/roms/u-boot/drivers/gpio/nx_gpio.c b/roms/u-boot/drivers/gpio/nx_gpio.c new file mode 100644 index 000000000..e2565d709 --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/octeon_gpio.c b/roms/u-boot/drivers/gpio/octeon_gpio.c new file mode 100644 index 000000000..42eae79d8 --- /dev/null +++ b/roms/u-boot/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, + 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/roms/u-boot/drivers/gpio/omap_gpio.c b/roms/u-boot/drivers/gpio/omap_gpio.c new file mode 100644 index 000000000..316a28efa --- /dev/null +++ b/roms/u-boot/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_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) +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_PLATDATA) + .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/roms/u-boot/drivers/gpio/pca953x.c b/roms/u-boot/drivers/gpio/pca953x.c new file mode 100644 index 000000000..4ab8cee2d --- /dev/null +++ b/roms/u-boot/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 CONFIG_SYS_I2C_PCA953X_ADDR +#define CONFIG_SYS_I2C_PCA953X_ADDR (~0) +#endif + +enum { + PCA953X_CMD_INFO, + PCA953X_CMD_DEVICE, + PCA953X_CMD_OUTPUT, + PCA953X_CMD_INPUT, + PCA953X_CMD_INVERT, +}; + +#ifdef CONFIG_SYS_I2C_PCA953X_WIDTH +struct pca953x_chip_ngpio { + uint8_t chip; + uint8_t ngpio; +}; + +static struct pca953x_chip_ngpio pca953x_chip_ngpios[] = + CONFIG_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 = CONFIG_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 = simple_strtoul(argv[2], NULL, 16); + + /* arg3 used as pin or invert value */ + if (argc > 3) + ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 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/roms/u-boot/drivers/gpio/pca953x_gpio.c b/roms/u-boot/drivers/gpio/pca953x_gpio.c new file mode 100644 index 000000000..dc8911a8e --- /dev/null +++ b/roms/u-boot/drivers/gpio/pca953x_gpio.c @@ -0,0 +1,390 @@ +// 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 PCA_GPIO_MASK 0x00FF +#define PCA_INT 0x0100 +#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 pca953x_info - Data for pca953x + * + * @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 + */ +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]; +}; + +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) +{ + int ret; + u8 val = 0; + + int off = offset % BANK_SZ; + + ret = pca953x_read_single(dev, PCA953X_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, PCA953X_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, PCA953X_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) { + dev_err(dev, "Only support PCA953X chip type now.\n"); + return -EINVAL; + } + + info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ); + + ret = pca953x_read_regs(dev, PCA953X_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, PCA953X_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 = "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 = "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/roms/u-boot/drivers/gpio/pca9698.c b/roms/u-boot/drivers/gpio/pca9698.c new file mode 100644 index 000000000..11274c781 --- /dev/null +++ b/roms/u-boot/drivers/gpio/pca9698.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + */ + +/* + * Driver for NXP's pca9698 40 bit I2C gpio expander + */ + +#include <common.h> +#include <i2c.h> +#include <malloc.h> +#include <linux/errno.h> +#include <pca9698.h> + +/* + * The pca9698 registers + */ + +#define PCA9698_REG_INPUT 0x00 +#define PCA9698_REG_OUTPUT 0x08 +#define PCA9698_REG_POLARITY 0x10 +#define PCA9698_REG_CONFIG 0x18 + +#define PCA9698_BUFFER_SIZE 5 +#define PCA9698_GPIO_COUNT 40 + +static int pca9698_read40(u8 addr, u8 offset, u8 *buffer) +{ + u8 command = offset | 0x80; /* autoincrement */ + + return i2c_read(addr, command, 1, buffer, PCA9698_BUFFER_SIZE); +} + +static int pca9698_write40(u8 addr, u8 offset, u8 *buffer) +{ + u8 command = offset | 0x80; /* autoincrement */ + + return i2c_write(addr, command, 1, buffer, PCA9698_BUFFER_SIZE); +} + +static void pca9698_set_bit(unsigned gpio, u8 *buffer, unsigned value) +{ + unsigned byte = gpio / 8; + unsigned bit = gpio % 8; + + if (value) + buffer[byte] |= (1 << bit); + else + buffer[byte] &= ~(1 << bit); +} + +int pca9698_request(unsigned gpio, const char *label) +{ + if (gpio >= PCA9698_GPIO_COUNT) + return -EINVAL; + + return 0; +} + +void pca9698_free(unsigned gpio) +{ +} + +int pca9698_direction_input(u8 addr, unsigned gpio) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_CONFIG, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, 1); + + return pca9698_write40(addr, PCA9698_REG_CONFIG, data); +} + +int pca9698_direction_output(u8 addr, unsigned gpio, int value) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_set_value(addr, gpio, value); + if (res) + return res; + + res = pca9698_read40(addr, PCA9698_REG_CONFIG, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, 0); + + return pca9698_write40(addr, PCA9698_REG_CONFIG, data); +} + +int pca9698_get_value(u8 addr, unsigned gpio) +{ + unsigned config_byte = gpio / 8; + unsigned config_bit = gpio % 8; + unsigned value; + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_INPUT, data); + if (res) + return -1; + + value = data[config_byte] & (1 << config_bit); + + return !!value; +} + +int pca9698_set_value(u8 addr, unsigned gpio, int value) +{ + u8 data[PCA9698_BUFFER_SIZE]; + int res; + + res = pca9698_read40(addr, PCA9698_REG_OUTPUT, data); + if (res) + return res; + + pca9698_set_bit(gpio, data, value); + + return pca9698_write40(addr, PCA9698_REG_OUTPUT, data); +} diff --git a/roms/u-boot/drivers/gpio/pcf8575_gpio.c b/roms/u-boot/drivers/gpio/pcf8575_gpio.c new file mode 100644 index 000000000..359646266 --- /dev/null +++ b/roms/u-boot/drivers/gpio/pcf8575_gpio.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCF8575 I2C GPIO EXPANDER DRIVER + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://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 + * + */ + +/* + * NOTE: The driver and devicetree bindings are borrowed from Linux + * Kernel, but driver does not support all PCF857x devices. It currently + * supports PCF8575 16-bit expander by TI and NXP. + * + * TODO(vigneshr@ti.com): + * Support 8 bit PCF857x compatible expanders. + */ + +#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 { + int gpio_count; /* No. GPIOs supported by the 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 */ + const char *bank_name; /* Name of the expander bank */ +}; + +/* Read/Write to 16-bit I/O expander */ + +static int pcf8575_i2c_write_le16(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, 2); + if (ret) + printf("%s i2c write failed to addr %x\n", __func__, + chip->chip_addr); + + return ret; +} + +static int pcf8575_i2c_read_le16(struct udevice *dev) +{ + struct dm_i2c_chip *chip = dev_get_parent_plat(dev); + u8 buf[2]; + int ret; + + ret = dm_i2c_read(dev, 0, buf, 2); + 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_le16(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_le16(dev, plat->out); + + return ret; +} + +static int pcf8575_get_value(struct udevice *dev, unsigned int offset) +{ + int value; + + value = pcf8575_i2c_read_le16(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; + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "gpio-count", 16); + 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" }, + { .compatible = "ti,pcf8575" }, + { } +}; + +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/roms/u-boot/drivers/gpio/pic32_gpio.c b/roms/u-boot/drivers/gpio/pic32_gpio.c new file mode 100644 index 000000000..975a2af3c --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/pm8916_gpio.c b/roms/u-boot/drivers/gpio/pm8916_gpio.c new file mode 100644 index 000000000..40b0f2578 --- /dev/null +++ b/roms/u-boot/drivers/gpio/pm8916_gpio.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm pm8916 pmic gpio driver - part of Qualcomm PM8916 PMIC + * + * (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 pm8916 peripherals */ +#define REG_TYPE 0x4 +#define REG_SUBTYPE 0x5 + +#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_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_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 pm8916_gpio_bank { + uint32_t pid; /* Peripheral ID on SPMI bus */ +}; + +static int pm8916_gpio_set_direction(struct udevice *dev, unsigned offset, + bool input, int value) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + 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 */ + if (input) + ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, + REG_CTL_MODE_INPUT); + else + ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, + REG_CTL_MODE_INOUT | (value ? 1 : 0)); + 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 pm8916_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + return pm8916_gpio_set_direction(dev, offset, true, 0); +} + +static int pm8916_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + return pm8916_gpio_set_direction(dev, offset, false, value); +} + +static int pm8916_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + int reg; + + /* Set the output value of the gpio */ + reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL); + if (reg < 0) + return reg; + + 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 pm8916_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct pm8916_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 pm8916_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + + /* Set the output value of the gpio */ + return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL, + REG_CTL_OUTPUT_MASK, !!value); +} + +static const struct dm_gpio_ops pm8916_gpio_ops = { + .direction_input = pm8916_gpio_direction_input, + .direction_output = pm8916_gpio_direction_output, + .get_value = pm8916_gpio_get_value, + .set_value = pm8916_gpio_set_value, + .get_function = pm8916_gpio_get_function, +}; + +static int pm8916_gpio_probe(struct udevice *dev) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + int reg; + + priv->pid = dev_read_addr(dev); + if (priv->pid == FDT_ADDR_T_NONE) + return log_msg_ret("bad address", -EINVAL); + + /* Do a sanity check */ + reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE); + if (reg != 0x10) + return log_msg_ret("bad type", -ENXIO); + + reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); + if (reg != 0x5 && reg != 0x1) + return log_msg_ret("bad subtype", -ENXIO); + + return 0; +} + +static int pm8916_gpio_of_to_plat(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = dev_read_u32_default(dev, "gpio-count", 0); + uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name"); + if (uc_priv->bank_name == NULL) + uc_priv->bank_name = "pm8916"; + + return 0; +} + +static const struct udevice_id pm8916_gpio_ids[] = { + { .compatible = "qcom,pm8916-gpio" }, + { .compatible = "qcom,pm8994-gpio" }, /* 22 GPIO's */ + { } +}; + +U_BOOT_DRIVER(gpio_pm8916) = { + .name = "gpio_pm8916", + .id = UCLASS_GPIO, + .of_match = pm8916_gpio_ids, + .of_to_plat = pm8916_gpio_of_to_plat, + .probe = pm8916_gpio_probe, + .ops = &pm8916_gpio_ops, + .priv_auto = sizeof(struct pm8916_gpio_bank), +}; + + +/* Add pmic buttons as GPIO as well - there is no generic way for now */ +#define PON_INT_RT_STS 0x10 +#define KPDPWR_ON_INT_BIT 0 +#define RESIN_ON_INT_BIT 1 + +static int pm8941_pwrkey_get_function(struct udevice *dev, unsigned offset) +{ + return GPIOF_INPUT; +} + +static int pm8941_pwrkey_get_value(struct udevice *dev, unsigned offset) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + + int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS); + + if (reg < 0) + return 0; + + switch (offset) { + case 0: /* Power button */ + return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0; + break; + case 1: /* Reset button */ + default: + return (reg & BIT(RESIN_ON_INT_BIT)) != 0; + break; + } +} + +static const struct dm_gpio_ops pm8941_pwrkey_ops = { + .get_value = pm8941_pwrkey_get_value, + .get_function = pm8941_pwrkey_get_function, +}; + +static int pm8941_pwrkey_probe(struct udevice *dev) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + int reg; + + priv->pid = dev_read_addr(dev); + if (priv->pid == FDT_ADDR_T_NONE) + return log_msg_ret("bad address", -EINVAL); + + /* Do a sanity check */ + reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE); + if (reg != 0x1) + return log_msg_ret("bad type", -ENXIO); + + reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); + if (reg != 0x1) + return log_msg_ret("bad subtype", -ENXIO); + + return 0; +} + +static int pm8941_pwrkey_of_to_plat(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = 2; + uc_priv->bank_name = dev_read_string(dev, "gpio-bank-name"); + if (uc_priv->bank_name == NULL) + uc_priv->bank_name = "pm8916_key"; + + return 0; +} + +static const struct udevice_id pm8941_pwrkey_ids[] = { + { .compatible = "qcom,pm8916-pwrkey" }, + { .compatible = "qcom,pm8994-pwrkey" }, + { } +}; + +U_BOOT_DRIVER(pwrkey_pm8941) = { + .name = "pwrkey_pm8916", + .id = UCLASS_GPIO, + .of_match = pm8941_pwrkey_ids, + .of_to_plat = pm8941_pwrkey_of_to_plat, + .probe = pm8941_pwrkey_probe, + .ops = &pm8941_pwrkey_ops, + .priv_auto = sizeof(struct pm8916_gpio_bank), +}; diff --git a/roms/u-boot/drivers/gpio/rk_gpio.c b/roms/u-boot/drivers/gpio/rk_gpio.c new file mode 100644 index 000000000..68f30157a --- /dev/null +++ b/roms/u-boot/drivers/gpio/rk_gpio.c @@ -0,0 +1,182 @@ +// 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/gpio.h> +#include <dm/pinctrl.h> +#include <dt-bindings/clock/rk3288-cru.h> + +enum { + ROCKCHIP_GPIOS_PER_BANK = 32, +}; + +#define OFFSET_TO_BIT(bit) (1UL << (bit)) + +struct rockchip_gpio_priv { + struct rockchip_gpio_regs *regs; + struct udevice *pinctrl; + int bank; + char name[2]; +}; + +static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + + clrbits_le32(®s->swport_ddr, OFFSET_TO_BIT(offset)); + + return 0; +} + +static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + int mask = OFFSET_TO_BIT(offset); + + clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0); + setbits_le32(®s->swport_ddr, mask); + + return 0; +} + +static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + + return readl(®s->ext_port) & OFFSET_TO_BIT(offset) ? 1 : 0; +} + +static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + int mask = OFFSET_TO_BIT(offset); + + clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0); + + return 0; +} + +static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset) +{ +#ifdef CONFIG_SPL_BUILD + return -ENODATA; +#else + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + bool is_output; + int ret; + + ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset); + if (ret) + return ret; + is_output = readl(®s->swport_ddr) & OFFSET_TO_BIT(offset); + + return is_output ? GPIOF_OUTPUT : GPIOF_INPUT; +#endif +} + +/* 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); + char *end; + int ret; + + priv->regs = dev_read_addr_ptr(dev); + ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl); + if (ret) + return ret; + + 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; + + 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/roms/u-boot/drivers/gpio/s5p_gpio.c b/roms/u-boot/drivers/gpio/s5p_gpio.c new file mode 100644 index 000000000..76f35ac5d --- /dev/null +++ b/roms/u-boot/drivers/gpio/s5p_gpio.c @@ -0,0 +1,372 @@ +// 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" }, + { } +}; + +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/roms/u-boot/drivers/gpio/sandbox.c b/roms/u-boot/drivers/gpio/sandbox.c new file mode 100644 index 000000000..d008fdd22 --- /dev/null +++ b/roms/u-boot/drivers/gpio/sandbox.c @@ -0,0 +1,581 @@ +// 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; + + 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; + + 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) +{ + 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) + +/* 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) +}; diff --git a/roms/u-boot/drivers/gpio/sh_pfc.c b/roms/u-boot/drivers/gpio/sh_pfc.c new file mode 100644 index 000000000..0653171af --- /dev/null +++ b/roms/u-boot/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 -= 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 defined(CONFIG_RCAR_GEN3) + if ((gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE) == PINMUX_TYPE_INPUT) + offset += 4; +#endif + + 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/roms/u-boot/drivers/gpio/sifive-gpio.c b/roms/u-boot/drivers/gpio/sifive-gpio.c new file mode 100644 index 000000000..abd1f629b --- /dev/null +++ b/roms/u-boot/drivers/gpio/sifive-gpio.c @@ -0,0 +1,178 @@ +// 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); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = (void *)addr; + 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/roms/u-boot/drivers/gpio/spear_gpio.c b/roms/u-boot/drivers/gpio/spear_gpio.c new file mode 100644 index 000000000..4e4cd1254 --- /dev/null +++ b/roms/u-boot/drivers/gpio/spear_gpio.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Stefan Roese <sr@denx.de> + */ + +/* + * Driver for SPEAr600 GPIO controller + */ + +#include <common.h> +#include <malloc.h> +#include <asm/arch/hardware.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <errno.h> + +static int gpio_direction(unsigned gpio, + enum gpio_direction direction) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodir); + + if (direction == GPIO_DIRECTION_OUT) + val |= 1 << gpio; + else + val &= ~(1 << gpio); + + writel(val, ®s->gpiodir); + + return 0; +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + + if (value) + writel(1 << gpio, ®s->gpiodata[DATA_REG_ADDR(gpio)]); + else + writel(0, ®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return !!val; +} + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= SPEAR_GPIO_COUNT) + return -EINVAL; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +void gpio_toggle_value(unsigned gpio) +{ + gpio_set_value(gpio, !gpio_get_value(gpio)); +} + +int gpio_direction_input(unsigned gpio) +{ + return gpio_direction(gpio, GPIO_DIRECTION_IN); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_direction(gpio, GPIO_DIRECTION_OUT); + + if (ret < 0) + return ret; + + gpio_set_value(gpio, value); + return 0; +} diff --git a/roms/u-boot/drivers/gpio/stm32_gpio.c b/roms/u-boot/drivers/gpio/stm32_gpio.c new file mode 100644 index 000000000..125c23755 --- /dev/null +++ b/roms/u-boot/drivers/gpio/stm32_gpio.c @@ -0,0 +1,349 @@ +// 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/gpio.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> + +#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; +} + +/* + * convert gpio offset to gpio index taking into account gpio holes + * into gpio bank + */ +int stm32_offset_to_index(struct udevice *dev, unsigned int offset) +{ + struct stm32_gpio_priv *priv = dev_get_priv(dev); + unsigned int idx = 0; + int i; + + for (i = 0; i < STM32_GPIOS_PER_BANK; i++) { + if (priv->gpio_range & BIT(i)) { + if (idx == offset) + return idx; + idx++; + } + } + /* shouldn't happen */ + return -EINVAL; +} + +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; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + stm32_gpio_set_moder(regs, idx, 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; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT); + + writel(BSRR_BIT(idx, 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; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + return readl(®s->idr) & BIT(idx) ? 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; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + writel(BSRR_BIT(idx, 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; + int idx; + u32 mode; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + bits_index = MODE_BITS(idx); + 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; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + if (flags & GPIOD_IS_OUT) { + bool value = flags & GPIOD_IS_OUT_ACTIVE; + + if (flags & GPIOD_OPEN_DRAIN) + stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_OD); + else + stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_PP); + + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT); + writel(BSRR_BIT(idx, value), ®s->bsrr); + + } else if (flags & GPIOD_IS_IN) { + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_IN); + } + if (flags & GPIOD_PULL_UP) + stm32_gpio_set_pupd(regs, idx, STM32_GPIO_PUPD_UP); + else if (flags & GPIOD_PULL_DOWN) + stm32_gpio_set_pupd(regs, idx, 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; + int idx; + ulong dir_flags = 0; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + switch (stm32_gpio_get_moder(regs, idx)) { + case STM32_GPIO_MODE_OUT: + dir_flags |= GPIOD_IS_OUT; + if (stm32_gpio_get_otype(regs, idx) == STM32_GPIO_OTYPE_OD) + dir_flags |= GPIOD_OPEN_DRAIN; + if (readl(®s->idr) & BIT(idx)) + 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, idx)) { + 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; + + if (ret == -ENOENT) { + uc_priv->gpio_count = STM32_GPIOS_PER_BANK; + 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]); + + uc_priv->gpio_count += args.args[2]; + + 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/roms/u-boot/drivers/gpio/sunxi_gpio.c b/roms/u-boot/drivers/gpio/sunxi_gpio.c new file mode 100644 index 000000000..24cb604e3 --- /dev/null +++ b/roms/u-boot/drivers/gpio/sunxi_gpio.c @@ -0,0 +1,378 @@ +// 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/arch/gpio.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <dm/device-internal.h> +#include <dt-bindings/gpio/gpio.h> + +#define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR + +struct sunxi_gpio_plat { + struct sunxi_gpio *regs; + const char *bank_name; /* Name of bank, e.g. "B" */ + int gpio_count; +}; + +#if !CONFIG_IS_ENABLED(DM_GPIO) +static int sunxi_gpio_output(u32 pin, u32 val) +{ + u32 dat; + u32 bank = GPIO_BANK(pin); + u32 num = GPIO_NUM(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + dat = readl(&pio->dat); + if (val) + dat |= 0x1 << num; + else + dat &= ~(0x1 << num); + + writel(dat, &pio->dat); + + return 0; +} + +static int sunxi_gpio_input(u32 pin) +{ + u32 dat; + u32 bank = GPIO_BANK(pin); + u32 num = GPIO_NUM(pin); + struct sunxi_gpio *pio = BANK_TO_GPIO(bank); + + dat = readl(&pio->dat); + dat >>= num; + + return dat & 0x1; +} + +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); + + return sunxi_gpio_output(gpio, value); +} + +int gpio_get_value(unsigned gpio) +{ + return sunxi_gpio_input(gpio); +} + +int gpio_set_value(unsigned gpio, int value) +{ + return sunxi_gpio_output(gpio, value); +} + +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 */ + +int sunxi_name_to_gpio_bank(const char *name) +{ + int group = 0; + + if (*name == 'P' || *name == 'p') + name++; + if (*name >= 'A') { + group = *name - (*name > 'a' ? 'a' : 'A'); + return group; + } + + return -1; +} + +#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-DETECT") == 0) { + sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d", + SUNXI_GPIO_AXP0_VBUS_DETECT); + name = lookup; + } else 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_direction_input(struct udevice *dev, unsigned offset) +{ + struct sunxi_gpio_plat *plat = dev_get_plat(dev); + + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); + + return 0; +} + +static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct sunxi_gpio_plat *plat = dev_get_plat(dev); + u32 num = GPIO_NUM(offset); + + sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); + + return 0; +} + +static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct sunxi_gpio_plat *plat = dev_get_plat(dev); + u32 num = GPIO_NUM(offset); + unsigned dat; + + dat = readl(&plat->regs->dat); + dat >>= num; + + return dat & 0x1; +} + +static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct sunxi_gpio_plat *plat = dev_get_plat(dev); + u32 num = GPIO_NUM(offset); + + clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); + return 0; +} + +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 = args->args[2] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + +static const struct dm_gpio_ops gpio_sunxi_ops = { + .direction_input = sunxi_gpio_direction_input, + .direction_output = sunxi_gpio_direction_output, + .get_value = sunxi_gpio_get_value, + .set_value = sunxi_gpio_set_value, + .get_function = sunxi_gpio_get_function, + .xlate = sunxi_gpio_xlate, +}; + +/** + * Returns the name of a GPIO bank + * + * GPIO banks are named A, B, C, ... + * + * @bank: Bank number (0, 1..n-1) + * @return allocated string containing the name + */ +static char *gpio_bank_name(int bank) +{ + char *name; + + name = malloc(3); + if (name) { + name[0] = 'P'; + name[1] = 'A' + bank; + name[2] = '\0'; + } + + return name; +} + +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 = plat->gpio_count; + uc_priv->bank_name = plat->bank_name; + } + + return 0; +} + +struct sunxi_gpio_soc_data { + int start; + int no_banks; +}; + +/** + * We have a top-level GPIO device with no actual GPIOs. It has a child + * device for each Sunxi bank. + */ +static int gpio_sunxi_bind(struct udevice *parent) +{ + struct sunxi_gpio_soc_data *soc_data = + (struct sunxi_gpio_soc_data *)dev_get_driver_data(parent); + struct sunxi_gpio_plat *plat = dev_get_plat(parent); + struct sunxi_gpio_reg *ctlr; + int bank, ret; + + /* If this is a child device, there is nothing to do here */ + if (plat) + return 0; + + ctlr = dev_read_addr_ptr(parent); + for (bank = 0; bank < soc_data->no_banks; bank++) { + struct sunxi_gpio_plat *plat; + struct udevice *dev; + + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + plat->regs = &ctlr->gpio_bank[bank]; + plat->bank_name = gpio_bank_name(soc_data->start + bank); + plat->gpio_count = SUNXI_GPIOS_PER_BANK; + + ret = device_bind(parent, parent->driver, plat->bank_name, plat, + dev_ofnode(parent), &dev); + if (ret) + return ret; + } + + return 0; +} + +static const struct sunxi_gpio_soc_data soc_data_a_all = { + .start = 0, + .no_banks = SUNXI_GPIO_BANKS, +}; + +static const struct sunxi_gpio_soc_data soc_data_l_1 = { + .start = 'L' - 'A', + .no_banks = 1, +}; + +static const struct sunxi_gpio_soc_data soc_data_l_2 = { + .start = 'L' - 'A', + .no_banks = 2, +}; + +static const struct sunxi_gpio_soc_data soc_data_l_3 = { + .start = 'L' - 'A', + .no_banks = 3, +}; + +#define ID(_compat_, _soc_data_) \ + { .compatible = _compat_, .data = (ulong)&soc_data_##_soc_data_ } + +static const struct udevice_id sunxi_gpio_ids[] = { + ID("allwinner,sun4i-a10-pinctrl", a_all), + ID("allwinner,sun5i-a10s-pinctrl", a_all), + ID("allwinner,sun5i-a13-pinctrl", a_all), + ID("allwinner,sun50i-h5-pinctrl", a_all), + ID("allwinner,sun6i-a31-pinctrl", a_all), + ID("allwinner,sun6i-a31s-pinctrl", a_all), + ID("allwinner,sun7i-a20-pinctrl", a_all), + ID("allwinner,sun8i-a23-pinctrl", a_all), + ID("allwinner,sun8i-a33-pinctrl", a_all), + ID("allwinner,sun8i-a83t-pinctrl", a_all), + ID("allwinner,sun8i-h3-pinctrl", a_all), + ID("allwinner,sun8i-r40-pinctrl", a_all), + ID("allwinner,sun8i-v3-pinctrl", a_all), + ID("allwinner,sun8i-v3s-pinctrl", a_all), + ID("allwinner,sun9i-a80-pinctrl", a_all), + ID("allwinner,sun50i-a64-pinctrl", a_all), + ID("allwinner,sun50i-h6-pinctrl", a_all), + ID("allwinner,sun50i-h616-pinctrl", a_all), + ID("allwinner,sun6i-a31-r-pinctrl", l_2), + ID("allwinner,sun8i-a23-r-pinctrl", l_1), + ID("allwinner,sun8i-a83t-r-pinctrl", l_1), + ID("allwinner,sun8i-h3-r-pinctrl", l_1), + ID("allwinner,sun9i-a80-r-pinctrl", l_3), + ID("allwinner,sun50i-a64-r-pinctrl", l_1), + ID("allwinner,sun50i-h6-r-pinctrl", l_2), + ID("allwinner,sun50i-h616-r-pinctrl", l_1), + { } +}; + +U_BOOT_DRIVER(gpio_sunxi) = { + .name = "gpio_sunxi", + .id = UCLASS_GPIO, + .ops = &gpio_sunxi_ops, + .of_match = sunxi_gpio_ids, + .bind = gpio_sunxi_bind, + .probe = gpio_sunxi_probe, +}; +#endif /* DM_GPIO */ diff --git a/roms/u-boot/drivers/gpio/tca642x.c b/roms/u-boot/drivers/gpio/tca642x.c new file mode 100644 index 000000000..7007c7a00 --- /dev/null +++ b/roms/u-boot/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(CONFIG_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(CONFIG_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 = CONFIG_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 = simple_strtoul(argv[2], NULL, 10); + + /* arg3 used as pin or invert value */ + if (argc > 3) + ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 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/roms/u-boot/drivers/gpio/tegra186_gpio.c b/roms/u-boot/drivers/gpio/tegra186_gpio.c new file mode 100644 index 000000000..82dcaf963 --- /dev/null +++ b/roms/u-boot/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 = (uint32_t *)devfdt_get_addr_name(parent, "gpio"); + if (regs == (uint32_t *)FDT_ADDR_T_NONE) + 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/roms/u-boot/drivers/gpio/tegra186_gpio_priv.h b/roms/u-boot/drivers/gpio/tegra186_gpio_priv.h new file mode 100644 index 000000000..3e686beed --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/tegra_gpio.c b/roms/u-boot/drivers/gpio/tegra_gpio.c new file mode 100644 index 000000000..5d3af8a01 --- /dev/null +++ b/roms/u-boot/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 CONFIG_SFIO = 0; +static const int CONFIG_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 ? CONFIG_GPIO : CONFIG_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 != CONFIG_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, CONFIG_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 = (struct gpio_ctlr *)dev_read_addr(parent); + if ((ulong)ctlr == FDT_ADDR_T_NONE) + 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/roms/u-boot/drivers/gpio/vybrid_gpio.c b/roms/u-boot/drivers/gpio/vybrid_gpio.c new file mode 100644 index 000000000..339392dcd --- /dev/null +++ b/roms/u-boot/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/roms/u-boot/drivers/gpio/xilinx_gpio.c b/roms/u-boot/drivers/gpio/xilinx_gpio.c new file mode 100644 index 000000000..510838d2f --- /dev/null +++ b/roms/u-boot/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 = (struct gpio_regs *)dev_read_addr(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/roms/u-boot/drivers/gpio/zynq_gpio.c b/roms/u-boot/drivers/gpio/zynq_gpio.c new file mode 100644 index 000000000..71a56127c --- /dev/null +++ b/roms/u-boot/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), +}; |