GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,191 @@
// 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/gpio.h>
#include <asm/io.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_alloc_size = sizeof(struct gen_74x164_priv),
.of_match = gen_74x164_ids,
};
@@ -0,0 +1,393 @@
#
# 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 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 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_BCM6858 || ARCH_BCM63158)
help
This driver supports the GPIO banks on BCM6345 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"
depends on DM_GPIO
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_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 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 STM32_GPIO
bool "ST STM32 GPIO driver"
depends on DM_GPIO && (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
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 && (ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_VERSAL)
default y
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 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.
endmenu
@@ -0,0 +1,64 @@
# 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
endif
obj-$(CONFIG_DM_GPIO) += gpio-uclass.o
obj-$(CONFIG_$(SPL_)DM_PCA953X) += pca953x_gpio.o
obj-$(CONFIG_DM_74X164) += 74x164_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_INTEL_ICH6_GPIO) += intel_ich6_gpio.o
obj-$(CONFIG_INTEL_BROADWELL_GPIO) += intel_broadwell_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_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_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_MT7621_GPIO) += mt7621_gpio.o
obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o
obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o
@@ -0,0 +1,423 @@
/*
* 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 <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;
}
}
@@ -0,0 +1,122 @@
// 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/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_platdata {
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_platdata *plat = dev_get_platdata(dev);
struct altera_pio_regs *const regs = plat->regs;
clrbits_le32(&regs->direction, 1 << pin);
return 0;
}
static int altera_pio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
struct altera_pio_platdata *plat = dev_get_platdata(dev);
struct altera_pio_regs *const regs = plat->regs;
if (val)
setbits_le32(&regs->data, 1 << pin);
else
clrbits_le32(&regs->data, 1 << pin);
/* change the data first, then the direction. to avoid glitch */
setbits_le32(&regs->direction, 1 << pin);
return 0;
}
static int altera_pio_get_value(struct udevice *dev, unsigned pin)
{
struct altera_pio_platdata *plat = dev_get_platdata(dev);
struct altera_pio_regs *const regs = plat->regs;
return !!(readl(&regs->data) & (1 << pin));
}
static int altera_pio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct altera_pio_platdata *plat = dev_get_platdata(dev);
struct altera_pio_regs *const regs = plat->regs;
if (val)
setbits_le32(&regs->data, 1 << pin);
else
clrbits_le32(&regs->data, 1 << pin);
return 0;
}
static int altera_pio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct altera_pio_platdata *plat = dev_get_platdata(dev);
uc_priv->gpio_count = plat->gpio_count;
uc_priv->bank_name = plat->bank_name;
return 0;
}
static int altera_pio_ofdata_to_platdata(struct udevice *dev)
{
struct altera_pio_platdata *plat = dev_get_platdata(dev);
plat->regs = map_physmem(devfdt_get_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,
.ofdata_to_platdata = altera_pio_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct altera_pio_platdata),
.probe = altera_pio_probe,
};
@@ -0,0 +1,637 @@
// 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 <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;
}
#ifdef CONFIG_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;
}
#ifndef CONFIG_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
#ifdef CONFIG_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_platdata *plat = dev_get_platdata(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 = (uint32_t)devfdt_get_addr_ptr(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(gpio_at91) = {
.name = "gpio_at91",
.id = UCLASS_GPIO,
#if CONFIG_IS_ENABLED(OF_CONTROL)
.of_match = at91_gpio_ids,
.platdata_auto_alloc_size = sizeof(struct at91_port_platdata),
#endif
.ops = &gpio_at91_ops,
.probe = at91_gpio_probe,
.priv_auto_alloc_size = sizeof(struct at91_port_priv),
};
#endif
@@ -0,0 +1,342 @@
// 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 <asm/arch/hardware.h>
#include <asm/gpio.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;
}
#ifdef CONFIG_DM_GPIO
struct atmel_pioctrl_data {
u32 nbanks;
};
struct atmel_pio4_platdata {
struct atmel_pio4_port *reg_base;
};
static struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev,
u32 bank)
{
struct atmel_pio4_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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 = devfdt_get_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;
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,
};
static const struct udevice_id atmel_pio4_ids[] = {
{
.compatible = "atmel,sama5d2-gpio",
.data = (ulong)&atmel_sama5d2_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,
.platdata_auto_alloc_size = sizeof(struct atmel_pio4_platdata),
};
#endif
@@ -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;
}
@@ -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_platdata *plat = dev_get_platdata(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_ofdata_to_platdata(struct udevice *dev)
{
struct bcm2835_gpio_platdata *plat = dev_get_platdata(dev);
fdt_addr_t addr;
addr = devfdt_get_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,
.ofdata_to_platdata = of_match_ptr(bcm2835_gpio_ofdata_to_platdata),
.platdata_auto_alloc_size = sizeof(struct bcm2835_gpio_platdata),
.ops = &gpio_bcm2835_ops,
.probe = bcm2835_gpio_probe,
.flags = DM_FLAG_PRE_RELOC,
.priv_auto_alloc_size = sizeof(struct bcm2835_gpios),
};
@@ -0,0 +1,118 @@
// 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>
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_alloc_size = sizeof(struct bcm6345_gpio_priv),
.probe = bcm6345_gpio_probe,
};
@@ -0,0 +1,565 @@
// 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 <asm/io.h>
#include <asm/gpio.h>
#include <dt-bindings/gpio/gpio.h>
#include "da8xx_gpio.h"
#ifndef CONFIG_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;
}
#ifndef CONFIG_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 /* CONFIG_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 int 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_platdata *plat = dev_get_platdata(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" },
{ }
};
static int davinci_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct davinci_gpio_platdata *plat = dev_get_platdata(dev);
fdt_addr_t addr;
addr = devfdt_get_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
plat->base = addr;
return 0;
}
U_BOOT_DRIVER(gpio_davinci) = {
.name = "gpio_davinci",
.id = UCLASS_GPIO,
.ops = &gpio_davinci_ops,
.ofdata_to_platdata = of_match_ptr(davinci_gpio_ofdata_to_platdata),
.of_match = davinci_gpio_ids,
.bind = dm_scan_fdt_dev,
.platdata_auto_alloc_size = sizeof(struct davinci_gpio_platdata),
.probe = davinci_gpio_probe,
.priv_auto_alloc_size = sizeof(struct davinci_gpio_bank),
};
#endif
@@ -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)
#ifdef CONFIG_DM_GPIO
/* Information about a GPIO bank */
struct davinci_gpio_platdata {
int bank_index;
ulong base; /* address of registers in physical memory */
const char *port_name;
};
#endif
#endif
@@ -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]);
}
@@ -0,0 +1,224 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2015 Marek Vasut <marex@denx.de>
*
* DesignWare APB GPIO driver
*/
#include <common.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/lists.h>
#include <dm/root.h>
#include <errno.h>
#include <reset.h>
#define GPIO_SWPORT_DR(p) (0x00 + (p) * 0xc)
#define GPIO_SWPORT_DDR(p) (0x04 + (p) * 0xc)
#define GPIO_INTEN 0x30
#define GPIO_INTMASK 0x34
#define GPIO_INTTYPE_LEVEL 0x38
#define GPIO_INT_POLARITY 0x3c
#define GPIO_INTSTATUS 0x40
#define GPIO_PORTA_DEBOUNCE 0x48
#define GPIO_PORTA_EOI 0x4c
#define GPIO_EXT_PORT(p) (0x50 + (p) * 4)
struct gpio_dwapb_priv {
struct reset_ctl_bulk resets;
};
struct gpio_dwapb_platdata {
const char *name;
int bank;
int pins;
fdt_addr_t base;
};
static int dwapb_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct gpio_dwapb_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_get_value(struct udevice *dev, unsigned pin)
{
struct gpio_dwapb_platdata *plat = dev_get_platdata(dev);
return !!(readl(plat->base + GPIO_EXT_PORT(plat->bank)) & (1 << pin));
}
static int dwapb_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct gpio_dwapb_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(dev);
u32 gpio;
gpio = readl(plat->base + GPIO_SWPORT_DDR(plat->bank));
if (gpio & BIT(offset))
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
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_platdata *plat = dev->platdata;
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_platdata *plat = dev_get_platdata(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 = 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.
*/
plat->name = ofnode_get_name(node);
}
ret = device_bind_ofnode(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_platdata *plat = dev_get_platdata(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_alloc_size = sizeof(struct gpio_dwapb_priv),
};
@@ -0,0 +1,191 @@
// 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 <dm/pinctrl.h>
#include <errno.h>
#include <asm/gpio.h>
#include <asm/io.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(void __iomem *regs, unsigned offset,
bool output)
{
/*
* 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)
{
struct rcar_gpio_priv *priv = dev_get_priv(dev);
rcar_gpio_set_direction(priv->regs, offset, false);
return 0;
}
static int rcar_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct rcar_gpio_priv *priv = dev_get_priv(dev);
/* write GPIO value to output before selecting output mode of pin */
rcar_gpio_set_value(dev, offset, value);
rcar_gpio_set_direction(priv->regs, 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,
.free = 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 = (void __iomem *)devfdt_get_addr(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_alloc_size = sizeof(struct rcar_gpio_priv),
.probe = rcar_gpio_probe,
};
@@ -0,0 +1,134 @@
// 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/gpio.h>
#include <asm/io.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 = devfdt_get_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_alloc_size = sizeof(struct r7s72100_gpio_priv),
.probe = r7s72100_gpio_probe,
};
File diff suppressed because it is too large Load Diff
@@ -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 = devfdt_get_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_alloc_size = sizeof(struct uniphier_gpio_priv),
.ops = &uniphier_gpio_ops,
};
@@ -0,0 +1,94 @@
// 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>
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_platdata *plat = dev_get_platdata(dev);
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
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_alloc_size = sizeof(struct gpio_bank),
};
@@ -0,0 +1,167 @@
/*
* 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 <asm-generic/gpio.h>
#include <asm/io.h>
#include <common.h>
#include <dm.h>
#include <errno.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 = (u32 *)devfdt_get_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,
.platdata_auto_alloc_size = sizeof(struct hsdk_creg_gpio),
};
@@ -0,0 +1,226 @@
// 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 <malloc.h>
enum imx_rgpio2p_direction {
IMX_RGPIO2P_DIRECTION_IN,
IMX_RGPIO2P_DIRECTION_OUT,
};
#define GPIO_PER_BANK 32
struct imx_rgpio2p_data {
struct gpio_regs *regs;
};
struct imx_rgpio2p_plat {
int bank_index;
struct gpio_regs *regs;
};
static int imx_rgpio2p_is_output(struct gpio_regs *regs, int offset)
{
u32 val;
val = readl(&regs->gpio_pddr);
return val & (1 << offset) ? 1 : 0;
}
static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset,
enum imx_rgpio2p_direction direction)
{
u32 l;
l = readl(&regs->gpio_pddr);
switch (direction) {
case IMX_RGPIO2P_DIRECTION_OUT:
l |= 1 << offset;
break;
case IMX_RGPIO2P_DIRECTION_IN:
l &= ~(1 << offset);
}
writel(l, &regs->gpio_pddr);
}
static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset,
int value)
{
if (value)
writel((1 << offset), &regs->gpio_psor);
else
writel((1 << offset), &regs->gpio_pcor);
}
static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset)
{
return (readl(&regs->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_platdata(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->platdata;
fdt_addr_t addr;
/*
* If platdata already exsits, directly return.
* Actually only when DT is not supported, platdata
* is statically initialized in U_BOOT_DEVICES.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 platdata.
*
* 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->req_seq;
dev->platdata = 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_alloc_size = 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_DEVICES(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
@@ -0,0 +1,188 @@
// 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 <pch.h>
#include <pci.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/arch/gpio.h>
#include <dt-bindings/gpio/x86-gpio.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* struct broadwell_bank_priv - Private driver data
*
* @regs: Pointer to GPIO registers
* @bank: Bank number for this bank (0, 1 or 2)
* @offset: GPIO offset for this bank (0, 32 or 64)
*/
struct broadwell_bank_priv {
struct pch_lp_gpio_regs *regs;
int bank;
int offset;
};
static int broadwell_gpio_request(struct udevice *dev, unsigned offset,
const char *label)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
u32 val;
/*
* Make sure that the GPIO pin we want isn't already in use for some
* built-in hardware function. We have to check this for every
* requested pin.
*/
debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset);
val = inl(&regs->own[priv->bank]);
if (!(val & (1UL << offset))) {
debug("gpio is reserved for internal use\n");
return -EPERM;
}
debug("ok\n");
return 0;
}
static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
setio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
return 0;
}
static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
return inl(&regs->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
1 : 0;
}
static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
value);
clrsetio_32(&regs->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
value ? CONFA_OUTPUT_HIGH : 0);
return 0;
}
static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
broadwell_gpio_set_value(dev, offset, value);
clrio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
return 0;
}
static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct broadwell_bank_priv *priv = dev_get_priv(dev);
struct pch_lp_gpio_regs *regs = priv->regs;
u32 mask = 1UL << offset;
if (!(inl(&regs->own[priv->bank]) & mask))
return GPIOF_FUNC;
if (inl(&regs->config[priv->offset + offset]) & CONFA_DIR_INPUT)
return GPIOF_INPUT;
else
return GPIOF_OUTPUT;
}
static int broadwell_gpio_probe(struct udevice *dev)
{
struct broadwell_bank_platdata *plat = dev_get_platdata(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_ofdata_to_platdata(struct udevice *dev)
{
struct broadwell_bank_platdata *plat = dev_get_platdata(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,
.ofdata_to_platdata = broadwell_gpio_ofdata_to_platdata,
.probe = broadwell_gpio_probe,
.priv_auto_alloc_size = sizeof(struct broadwell_bank_priv),
.platdata_auto_alloc_size = sizeof(struct broadwell_bank_platdata),
};
@@ -0,0 +1,240 @@
// 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 <pch.h>
#include <pci.h>
#include <asm/cpu.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_ofdata_to_platdata(struct udevice *dev)
{
struct ich6_bank_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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,
.ofdata_to_platdata = gpio_ich6_ofdata_to_platdata,
.probe = ich6_gpio_probe,
.priv_auto_alloc_size = sizeof(struct ich6_bank_priv),
.platdata_auto_alloc_size = sizeof(struct ich6_bank_platdata),
};
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2013 Broadcom Corporation.
*/
#include <common.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);
}
@@ -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);
}
@@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* LPC32xxGPIO driver
*
* (C) Copyright 2014 DENX Software Engineering GmbH
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch-lpc32xx/cpu.h>
#include <asm/arch-lpc32xx/gpio.h>
#include <asm-generic/gpio.h>
#include <dm.h>
/**
* LPC32xx GPIOs work in banks but are non-homogeneous:
* - each bank holds a different number of GPIOs
* - some GPIOs are input/ouput, some input only, some output only;
* - some GPIOs have different meanings as an input and as an output;
* - some GPIOs are controlled on a given port and bit index, but
* read on another one.
*
* In order to keep this code simple, GPIOS are considered here as
* homogeneous and linear, from 0 to 159.
*
* ** WARNING #1 **
*
* Client code is responsible for properly using valid GPIO numbers,
* including cases where a single physical GPIO has differing numbers
* for setting its direction, reading it and/or writing to it.
*
* ** WARNING #2 **
*
* Please read NOTE in description of lpc32xx_gpio_get_function().
*/
#define LPC32XX_GPIOS 160
struct lpc32xx_gpio_priv {
struct gpio_regs *regs;
/* GPIO FUNCTION: SEE WARNING #2 */
signed char function[LPC32XX_GPIOS];
};
/**
* We have 4 GPIO ports of 32 bits each
*
* Port mapping offset (32 bits each):
* - Port 0: 0
* - Port 1: 32
* - Port 2: 64
* - Port 3: GPO / GPIO (output): 96
* - Port 3: GPI: 128
*/
#define MAX_GPIO 160
#define GPIO_TO_PORT(gpio) ((gpio / 32) & 7)
#define GPIO_TO_RANK(gpio) (gpio % 32)
#define GPIO_TO_MASK(gpio) (1 << (gpio % 32))
/**
* Configure a GPIO number 'offset' as input
*/
static int lpc32xx_gpio_direction_input(struct udevice *dev, unsigned offset)
{
int port, mask;
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_regs *regs = gpio_priv->regs;
port = GPIO_TO_PORT(offset);
mask = GPIO_TO_MASK(offset);
switch (port) {
case 0:
writel(mask, &regs->p0_dir_clr);
break;
case 1:
writel(mask, &regs->p1_dir_clr);
break;
case 2:
/* ports 2 and 3 share a common direction */
writel(mask, &regs->p2_p3_dir_clr);
break;
case 3:
/* Setup direction only for GPIO_xx. */
if ((mask >= 25) && (mask <= 30))
writel(mask, &regs->p2_p3_dir_clr);
break;
case 4:
/* GPI_xx; nothing to do. */
break;
default:
return -1;
}
/* GPIO FUNCTION: SEE WARNING #2 */
gpio_priv->function[offset] = GPIOF_INPUT;
return 0;
}
/**
* Get the value of a GPIO
*/
static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset)
{
int port, rank, mask, value;
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_regs *regs = gpio_priv->regs;
port = GPIO_TO_PORT(offset);
switch (port) {
case 0:
value = readl(&regs->p0_inp_state);
break;
case 1:
value = readl(&regs->p1_inp_state);
break;
case 2:
value = readl(&regs->p2_inp_state);
break;
case 3:
/* Read GPO_xx and GPIO_xx (as output) using p3_outp_state. */
value = readl(&regs->p3_outp_state);
break;
case 4:
/* Read GPI_xx and GPIO_xx (as input) using p3_inp_state. */
value = readl(&regs->p3_inp_state);
break;
default:
return -1;
}
rank = GPIO_TO_RANK(offset);
mask = GPIO_TO_MASK(offset);
return (value & mask) >> rank;
}
/**
* Set a GPIO
*/
static int gpio_set(struct udevice *dev, unsigned gpio)
{
int port, mask;
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_regs *regs = gpio_priv->regs;
port = GPIO_TO_PORT(gpio);
mask = GPIO_TO_MASK(gpio);
switch (port) {
case 0:
writel(mask, &regs->p0_outp_set);
break;
case 1:
writel(mask, &regs->p1_outp_set);
break;
case 2:
writel(mask, &regs->p2_outp_set);
break;
case 3:
writel(mask, &regs->p3_outp_set);
break;
case 4:
/* GPI_xx; invalid. */
default:
return -1;
}
return 0;
}
/**
* Clear a GPIO
*/
static int gpio_clr(struct udevice *dev, unsigned gpio)
{
int port, mask;
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_regs *regs = gpio_priv->regs;
port = GPIO_TO_PORT(gpio);
mask = GPIO_TO_MASK(gpio);
switch (port) {
case 0:
writel(mask, &regs->p0_outp_clr);
break;
case 1:
writel(mask, &regs->p1_outp_clr);
break;
case 2:
writel(mask, &regs->p2_outp_clr);
break;
case 3:
writel(mask, &regs->p3_outp_clr);
break;
case 4:
/* GPI_xx; invalid. */
default:
return -1;
}
return 0;
}
/**
* Set the value of a GPIO
*/
static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
if (value)
return gpio_set(dev, offset);
else
return gpio_clr(dev, offset);
}
/**
* Configure a GPIO number 'offset' as output with given initial value.
*/
static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
int port, mask;
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_regs *regs = gpio_priv->regs;
port = GPIO_TO_PORT(offset);
mask = GPIO_TO_MASK(offset);
switch (port) {
case 0:
writel(mask, &regs->p0_dir_set);
break;
case 1:
writel(mask, &regs->p1_dir_set);
break;
case 2:
/* ports 2 and 3 share a common direction */
writel(mask, &regs->p2_p3_dir_set);
break;
case 3:
/* Setup direction only for GPIO_xx. */
if ((mask >= 25) && (mask <= 30))
writel(mask, &regs->p2_p3_dir_set);
break;
case 4:
/* GPI_xx; invalid. */
default:
return -1;
}
/* GPIO FUNCTION: SEE WARNING #2 */
gpio_priv->function[offset] = GPIOF_OUTPUT;
return lpc32xx_gpio_set_value(dev, offset, value);
}
/**
* GPIO functions are supposed to be computed from their current
* configuration, but that's way too complicated in LPC32XX. A simpler
* approach is used, where the GPIO functions are cached in an array.
* When the GPIO is in use, its function is either "input" or "output"
* depending on its direction, otherwise its function is "unknown".
*
* ** NOTE **
*
* THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX
* GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE.
*/
static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
return gpio_priv->function[offset];
}
static const struct dm_gpio_ops gpio_lpc32xx_ops = {
.direction_input = lpc32xx_gpio_direction_input,
.direction_output = lpc32xx_gpio_direction_output,
.get_value = lpc32xx_gpio_get_value,
.set_value = lpc32xx_gpio_set_value,
.get_function = lpc32xx_gpio_get_function,
};
static int lpc32xx_gpio_probe(struct udevice *dev)
{
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
struct gpio_dev_priv *uc_priv = dev->uclass_priv;
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_alloc_size = sizeof(struct lpc32xx_gpio_priv),
};
@@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale MPC83xx GPIO handling.
*/
#include <common.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
}
@@ -0,0 +1,270 @@
// 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
*/
#include <common.h>
#include <dm.h>
#include <mapmem.h>
#include <asm/gpio.h>
struct ccsr_gpio {
u32 gpdir;
u32 gpodr;
u32 gpdat;
u32 gpier;
u32 gpimr;
u32 gpicr;
};
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 */
ulong 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;
};
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 ccsr_gpio *base, u32 mask)
{
return in_be32(&base->gpdat) & mask;
}
static inline u32 mpc8xxx_gpio_get_dir(struct ccsr_gpio *base, u32 mask)
{
return in_be32(&base->gpdir) & mask;
}
static inline void mpc8xxx_gpio_set_in(struct ccsr_gpio *base, u32 gpios)
{
clrbits_be32(&base->gpdat, gpios);
/* GPDIR register 0 -> input */
clrbits_be32(&base->gpdir, gpios);
}
static inline void mpc8xxx_gpio_set_low(struct ccsr_gpio *base, u32 gpios)
{
clrbits_be32(&base->gpdat, gpios);
/* GPDIR register 1 -> output */
setbits_be32(&base->gpdir, gpios);
}
static inline void mpc8xxx_gpio_set_high(struct ccsr_gpio *base, u32 gpios)
{
setbits_be32(&base->gpdat, gpios);
/* GPDIR register 1 -> output */
setbits_be32(&base->gpdir, gpios);
}
static inline int mpc8xxx_gpio_open_drain_val(struct ccsr_gpio *base, u32 mask)
{
return in_be32(&base->gpodr) & mask;
}
static inline void mpc8xxx_gpio_open_drain_on(struct ccsr_gpio *base, u32
gpios)
{
/* GPODR register 1 -> open drain on */
setbits_be32(&base->gpodr, gpios);
}
static inline void mpc8xxx_gpio_open_drain_off(struct ccsr_gpio *base,
u32 gpios)
{
/* GPODR register 0 -> open drain off (actively driven) */
clrbits_be32(&base->gpodr, gpios);
}
static int mpc8xxx_gpio_direction_input(struct udevice *dev, uint gpio)
{
struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
mpc8xxx_gpio_set_in(data->base, gpio_mask(gpio));
return 0;
}
static int mpc8xxx_gpio_set_value(struct udevice *dev, uint gpio, int value)
{
struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
if (value) {
data->dat_shadow |= gpio_mask(gpio);
mpc8xxx_gpio_set_high(data->base, gpio_mask(gpio));
} else {
data->dat_shadow &= ~gpio_mask(gpio);
mpc8xxx_gpio_set_low(data->base, gpio_mask(gpio));
}
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(data->base, 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(data->base, gpio_mask(gpio));
}
static int mpc8xxx_gpio_get_open_drain(struct udevice *dev, uint gpio)
{
struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
return !!mpc8xxx_gpio_open_drain_val(data->base, gpio_mask(gpio));
}
static int mpc8xxx_gpio_set_open_drain(struct udevice *dev, uint gpio,
int value)
{
struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
if (value)
mpc8xxx_gpio_open_drain_on(data->base, gpio_mask(gpio));
else
mpc8xxx_gpio_open_drain_off(data->base, gpio_mask(gpio));
return 0;
}
static int mpc8xxx_gpio_get_function(struct udevice *dev, uint gpio)
{
struct mpc8xxx_gpio_data *data = dev_get_priv(dev);
int dir;
dir = !!mpc8xxx_gpio_get_dir(data->base, gpio_mask(gpio));
return dir ? GPIOF_OUTPUT : GPIOF_INPUT;
}
#if CONFIG_IS_ENABLED(OF_CONTROL)
static int mpc8xxx_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct mpc8xxx_gpio_plat *plat = dev_get_platdata(dev);
fdt_addr_t addr;
u32 reg[2];
dev_read_u32_array(dev, "reg", reg, 2);
addr = dev_translate_address(dev, reg);
plat->addr = addr;
plat->size = reg[1];
plat->ngpios = dev_read_u32_default(dev, "ngpios", 32);
return 0;
}
#endif
static int mpc8xxx_gpio_platdata_to_priv(struct udevice *dev)
{
struct mpc8xxx_gpio_data *priv = dev_get_priv(dev);
struct mpc8xxx_gpio_plat *plat = dev_get_platdata(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_platdata_to_priv(dev);
snprintf(name, sizeof(name), "MPC@%lx_", data->addr);
str = strdup(name);
if (!str)
return -ENOMEM;
uc_priv->bank_name = str;
uc_priv->gpio_count = data->gpio_count;
return 0;
}
static const struct dm_gpio_ops gpio_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_open_drain = mpc8xxx_gpio_get_open_drain,
.set_open_drain = mpc8xxx_gpio_set_open_drain,
.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)
.ofdata_to_platdata = mpc8xxx_gpio_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct mpc8xxx_gpio_plat),
.of_match = mpc8xxx_gpio_ids,
#endif
.probe = mpc8xxx_gpio_probe,
.priv_auto_alloc_size = sizeof(struct mpc8xxx_gpio_data),
};
@@ -0,0 +1,275 @@
// 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 <asm/gpio.h>
#include <asm/io.h>
#include <errno.h>
#include <clk.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_alloc_size = sizeof(struct mscc_sgpio_priv),
};
@@ -0,0 +1,132 @@
// 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/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 = devfdt_get_addr(dev);
return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0;
}
static int msm_gpio_ofdata_to_platdata(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" },
{ }
};
U_BOOT_DRIVER(gpio_msm) = {
.name = "gpio_msm",
.id = UCLASS_GPIO,
.of_match = msm_gpio_ids,
.ofdata_to_platdata = msm_gpio_ofdata_to_platdata,
.probe = msm_gpio_probe,
.ops = &gpio_msm_ops,
.priv_auto_alloc_size = sizeof(struct msm_gpio_bank),
};
@@ -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/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_platdata {
char bank_name[3]; /* Name of bank, e.g. "PA", "PB" etc */
int gpio_count;
int bank;
};
static u32 reg_offs(struct mediatek_gpio_platdata *plat, int reg)
{
return (reg * 0x10) + (plat->bank * 0x4);
}
static int mediatek_gpio_get_value(struct udevice *dev, unsigned int offset)
{
struct mediatek_gpio_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = parent->platdata;
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_platdata *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, -1, &dev);
if (ret)
return ret;
dev->node = node;
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,
};
@@ -0,0 +1,120 @@
// 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>
#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(&regs->io_conf, BIT(gpio));
return 0;
}
static int mvebu_gpio_direction_output(struct udevice *dev, unsigned gpio,
int value)
{
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
struct mvebu_gpio_regs *regs = priv->regs;
if (value)
setbits_le32(&regs->data_out, BIT(gpio));
else
clrbits_le32(&regs->data_out, BIT(gpio));
clrbits_le32(&regs->io_conf, BIT(gpio));
return 0;
}
static int mvebu_gpio_get_function(struct udevice *dev, unsigned gpio)
{
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
struct mvebu_gpio_regs *regs = priv->regs;
u32 val;
val = readl(&regs->io_conf) & BIT(gpio);
if (val)
return GPIOF_INPUT;
else
return GPIOF_OUTPUT;
}
static int mvebu_gpio_set_value(struct udevice *dev, unsigned gpio,
int value)
{
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
struct mvebu_gpio_regs *regs = priv->regs;
if (value)
setbits_le32(&regs->data_out, BIT(gpio));
else
clrbits_le32(&regs->data_out, BIT(gpio));
return 0;
}
static int mvebu_gpio_get_value(struct udevice *dev, unsigned gpio)
{
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
struct mvebu_gpio_regs *regs = priv->regs;
return !!(readl(&regs->data_in) & BIT(gpio));
}
static int mvebu_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
priv->regs = (struct mvebu_gpio_regs *)devfdt_get_addr(dev);
uc_priv->gpio_count = MVEBU_GPIOS_PER_BANK;
priv->name[0] = 'A' + dev->req_seq;
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_alloc_size = sizeof(struct mvebu_gpio_priv),
};
@@ -0,0 +1,96 @@
// 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 <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;
}
@@ -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__ */
@@ -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);
}
@@ -0,0 +1,398 @@
// 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>
enum mxc_gpio_direction {
MXC_GPIO_DIRECTION_IN,
MXC_GPIO_DIRECTION_OUT,
};
#define GPIO_PER_BANK 32
struct mxc_gpio_plat {
int bank_index;
struct gpio_regs *regs;
};
struct mxc_bank_info {
struct gpio_regs *regs;
};
#ifndef CONFIG_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)
[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)
[4] = GPIO5_BASE_ADDR,
#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || defined(CONFIG_IMX8M))
[5] = GPIO6_BASE_ADDR,
#endif
#endif
#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
defined(CONFIG_ARCH_IMX8)
#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
[6] = GPIO7_BASE_ADDR,
#endif
#endif
#if defined(CONFIG_ARCH_IMX8)
[7] = GPIO8_BASE_ADDR,
#endif
};
static int mxc_gpio_direction(unsigned int gpio,
enum mxc_gpio_direction direction)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 l;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
l = readl(&regs->gpio_dir);
switch (direction) {
case MXC_GPIO_DIRECTION_OUT:
l |= 1 << gpio;
break;
case MXC_GPIO_DIRECTION_IN:
l &= ~(1 << gpio);
}
writel(l, &regs->gpio_dir);
return 0;
}
int gpio_set_value(unsigned gpio, int value)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 l;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
l = readl(&regs->gpio_dr);
if (value)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
writel(l, &regs->gpio_dr);
return 0;
}
int gpio_get_value(unsigned gpio)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 val;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
return val;
}
int gpio_request(unsigned gpio, const char *label)
{
unsigned int port = GPIO_TO_PORT(gpio);
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
}
int gpio_direction_output(unsigned gpio, int value)
{
int ret = gpio_set_value(gpio, value);
if (ret < 0)
return ret;
return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
}
#endif
#ifdef CONFIG_DM_GPIO
#include <fdtdec.h>
static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
{
u32 val;
val = readl(&regs->gpio_dir);
return val & (1 << offset) ? 1 : 0;
}
static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
enum mxc_gpio_direction direction)
{
u32 l;
l = readl(&regs->gpio_dir);
switch (direction) {
case MXC_GPIO_DIRECTION_OUT:
l |= 1 << offset;
break;
case MXC_GPIO_DIRECTION_IN:
l &= ~(1 << offset);
}
writel(l, &regs->gpio_dir);
}
static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
int value)
{
u32 l;
l = readl(&regs->gpio_dr);
if (value)
l |= 1 << offset;
else
l &= ~(1 << offset);
writel(l, &regs->gpio_dr);
}
static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
{
return (readl(&regs->gpio_psr) >> offset) & 0x01;
}
/* set GPIO pin 'gpio' as an input */
static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
/* Configure GPIO direction as input. */
mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);
return 0;
}
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
/* Configure GPIO output value. */
mxc_gpio_bank_set_value(bank->regs, offset, value);
/* Configure GPIO direction as output. */
mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);
return 0;
}
/* read GPIO IN value of pin 'gpio' */
static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
return mxc_gpio_bank_get_value(bank->regs, offset);
}
/* write GPIO OUT value to pin 'gpio' */
static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
mxc_gpio_bank_set_value(bank->regs, offset, value);
return 0;
}
static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
/* GPIOF_FUNC is not implemented yet */
if (mxc_gpio_is_output(bank->regs, offset))
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
static const struct dm_gpio_ops gpio_mxc_ops = {
.direction_input = mxc_gpio_direction_input,
.direction_output = mxc_gpio_direction_output,
.get_value = mxc_gpio_get_value,
.set_value = mxc_gpio_set_value,
.get_function = mxc_gpio_get_function,
};
static int mxc_gpio_probe(struct udevice *dev)
{
struct mxc_bank_info *bank = dev_get_priv(dev);
struct mxc_gpio_plat *plat = dev_get_platdata(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 mxc_gpio_bind(struct udevice *dev)
{
struct mxc_gpio_plat *plat = dev->platdata;
fdt_addr_t addr;
/*
* If platdata already exsits, directly return.
* Actually only when DT is not supported, platdata
* is statically initialized in U_BOOT_DEVICES.Here
* will return.
*/
if (plat)
return 0;
addr = devfdt_get_addr(dev);
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 platdata.
*
* For example mxc_plat below 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->req_seq;
dev->platdata = plat;
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,
.priv_auto_alloc_size = sizeof(struct mxc_bank_info),
.of_match = mxc_gpio_ids,
.bind = mxc_gpio_bind,
};
#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_DEVICES(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
@@ -0,0 +1,315 @@
// 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 <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/iomux.h>
#include <asm/arch/imx-regs.h>
#if defined(CONFIG_MX23)
#define PINCTRL_BANKS 3
#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10))
#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10))
#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10))
#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10))
#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10))
#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10))
#elif defined(CONFIG_MX28)
#define PINCTRL_BANKS 5
#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10))
#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10))
#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10))
#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10))
#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10))
#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10))
#else
#error "Please select CONFIG_MX23 or CONFIG_MX28"
#endif
#define GPIO_INT_FALL_EDGE 0x0
#define GPIO_INT_LOW_LEV 0x1
#define GPIO_INT_RISE_EDGE 0x2
#define GPIO_INT_HIGH_LEV 0x3
#define GPIO_INT_LEV_MASK (1 << 0)
#define GPIO_INT_POL_MASK (1 << 1)
void mxs_gpio_init(void)
{
int i;
for (i = 0; i < PINCTRL_BANKS; i++) {
writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i));
writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i));
/* Use SCT address here to clear the IRQSTAT bits */
writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8);
}
}
#if !CONFIG_IS_ENABLED(DM_GPIO)
int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DIN(bank);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
return (readl(&reg->reg) >> PAD_PIN(gpio)) & 1;
}
void gpio_set_value(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOUT(bank);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
if (value)
writel(1 << PAD_PIN(gpio), &reg->reg_set);
else
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
}
int gpio_direction_input(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
gpio_set_value(gpio, value);
writel(1 << PAD_PIN(gpio), &reg->reg_set);
return 0;
}
int gpio_request(unsigned gpio, const char *label)
{
if (PAD_BANK(gpio) >= PINCTRL_BANKS)
return -1;
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int name_to_gpio(const char *name)
{
unsigned bank, pin;
char *end;
bank = 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 /* CONFIG_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
#ifdef CONFIG_MX28
#define dtd_fsl_imx_gpio dtd_fsl_imx28_gpio
#else /* CONFIG_MX23 */
#define dtd_fsl_imx_gpio dtd_fsl_imx23_gpio
#endif
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_platdata {
#if CONFIG_IS_ENABLED(OF_PLATDATA)
struct dtd_fsl_imx_gpio dtplat;
#endif
unsigned int bank;
int gpio_ranges;
};
struct mxs_gpio_priv {
unsigned int bank;
};
static int mxs_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DIN(priv->bank));
return (readl(&reg->reg) >> offset) & 1;
}
static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
int value)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOUT(priv->bank));
if (value)
writel(BIT(offset), &reg->reg_set);
else
writel(BIT(offset), &reg->reg_clr);
return 0;
}
static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
writel(BIT(offset), &reg->reg_clr);
return 0;
}
static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
int value)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
mxs_gpio_set_value(dev, offset, value);
writel(BIT(offset), &reg->reg_set);
return 0;
}
static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct mxs_gpio_priv *priv = dev_get_priv(dev);
struct mxs_register_32 *reg =
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
PINCTRL_DOE(priv->bank));
bool is_output = !!(readl(&reg->reg) >> offset);
return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
}
static const struct dm_gpio_ops gpio_mxs_ops = {
.direction_input = mxs_gpio_direction_input,
.direction_output = mxs_gpio_direction_output,
.get_value = mxs_gpio_get_value,
.set_value = mxs_gpio_set_value,
.get_function = mxs_gpio_get_function,
};
static int mxs_gpio_probe(struct udevice *dev)
{
struct mxs_gpio_platdata *plat = dev_get_platdata(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_imx_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_ofdata_to_platdata(struct udevice *dev)
{
struct mxs_gpio_platdata *plat = dev->platdata;
struct fdtdec_phandle_args args;
int node = dev_of_offset(dev);
int ret;
plat->bank = devfdt_get_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(gpio_mxs) = {
#ifdef CONFIG_MX28
.name = "fsl_imx28_gpio",
#else /* CONFIG_MX23 */
.name = "fsl_imx23_gpio",
#endif
.id = UCLASS_GPIO,
.ops = &gpio_mxs_ops,
.probe = mxs_gpio_probe,
.priv_auto_alloc_size = sizeof(struct mxs_gpio_priv),
.platdata_auto_alloc_size = sizeof(struct mxs_gpio_platdata),
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.of_match = mxs_gpio_ids,
.ofdata_to_platdata = mxs_ofdata_to_platdata,
#endif
};
#endif /* CONFIG_DM_GPIO */
@@ -0,0 +1,380 @@
// 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/gpio.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <malloc.h>
DECLARE_GLOBAL_DATA_PTR;
#define OMAP_GPIO_DIR_OUT 0
#define OMAP_GPIO_DIR_IN 1
#ifdef CONFIG_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
static inline int get_gpio_index(int gpio)
{
return gpio & 0x1f;
}
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;
}
#ifndef CONFIG_DM_GPIO
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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(dev);
fdt_addr_t base_addr;
if (plat)
return 0;
base_addr = devfdt_get_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 platdata.
*
* 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->platdata = 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_ofdata_to_platdata(struct udevice *dev)
{
struct omap_gpio_platdata *plat = dev_get_platdata(dev);
fdt_addr_t addr;
addr = devfdt_get_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,
.ofdata_to_platdata = of_match_ptr(omap_gpio_ofdata_to_platdata),
.platdata_auto_alloc_size = sizeof(struct omap_gpio_platdata),
#endif
#else
.bind = omap_gpio_bind,
#endif
.ops = &gpio_omap_ops,
.probe = omap_gpio_probe,
.priv_auto_alloc_size = sizeof(struct gpio_bank),
#if !CONFIG_IS_ENABLED(OF_CONTROL)
.flags = DM_FLAG_PRE_RELOC,
#endif
};
#endif /* CONFIG_DM_GPIO */
@@ -0,0 +1,293 @@
// 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 <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 cmd_tbl_t 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(cmd_tbl_t *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;
cmd_tbl_t *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 */
@@ -0,0 +1,381 @@
// 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 24 gpio pins
* 3. 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 <dt-bindings/gpio/gpio.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_platdata(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_platdata(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_platdata(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 == 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_platdata(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 == 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_platdata(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_platdata(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_platdata(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_platdata(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,
.platdata_auto_alloc_size = sizeof(struct pca953x_info),
.of_match = pca953x_ids,
};
@@ -0,0 +1,126 @@
// 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 <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);
}
@@ -0,0 +1,179 @@
// 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 <asm-generic/gpio.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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(struct udevice *dev)
{
struct pcf8575_chip *plat = dev_get_platdata(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,
.ofdata_to_platdata = pcf8575_ofdata_platdata,
.probe = pcf8575_gpio_probe,
.platdata_auto_alloc_size = sizeof(struct pcf8575_chip),
};
@@ -0,0 +1,164 @@
// 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/io.h>
#include <asm/gpio.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_alloc_size = sizeof(struct pic32_gpio_priv),
};
@@ -0,0 +1,300 @@
// 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 <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_ofdata_to_platdata(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,
.ofdata_to_platdata = pm8916_gpio_ofdata_to_platdata,
.probe = pm8916_gpio_probe,
.ops = &pm8916_gpio_ops,
.priv_auto_alloc_size = 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 = devfdt_get_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_ofdata_to_platdata(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,
.ofdata_to_platdata = pm8941_pwrkey_ofdata_to_platdata,
.probe = pm8941_pwrkey_probe,
.ops = &pm8941_pwrkey_ops,
.priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank),
};
@@ -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(&regs->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(&regs->swport_dr, mask, value ? mask : 0);
setbits_le32(&regs->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(&regs->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(&regs->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(&regs->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(&regs->swport_dr, 1 << gpio, value << gpio);
/* Set direction */
clrsetbits_le32(&regs->swport_ddr, 1 << gpio, 1 << gpio);
return 0;
}
#endif /* CONFIG_SPL_BUILD */
static int rockchip_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
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(gpio_rockchip) = {
.name = "gpio_rockchip",
.id = UCLASS_GPIO,
.of_match = rockchip_gpio_ids,
.ops = &gpio_rockchip_ops,
.priv_auto_alloc_size = sizeof(struct rockchip_gpio_priv),
.probe = rockchip_gpio_probe,
};
@@ -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 <malloc.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_platdata {
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->priv;
struct exynos_gpio_platdata *plat = dev->platdata;
/* 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_platdata *plat = parent->platdata;
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 = (struct s5p_gpio_bank *)devfdt_get_addr(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_platdata *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, -1, &dev);
if (ret)
return ret;
dev_set_of_offset(dev, node);
reg = devfdt_get_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_alloc_size = sizeof(struct exynos_bank_info),
.ops = &gpio_exynos_ops,
};
#endif
@@ -0,0 +1,239 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/gpio.h>
#include <dm/of.h>
#include <dt-bindings/gpio/gpio.h>
/* Flags for each GPIO */
#define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */
#define GPIOF_HIGH (1 << 1) /* Currently set high */
#define GPIOF_ODR (1 << 2) /* Currently set to open drain mode */
struct gpio_state {
const char *label; /* label given by requester */
u8 flags; /* flags (GPIOF_...) */
};
/* Access routines for GPIO state */
static u8 *get_gpio_flags(struct udevice *dev, unsigned 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) {
static u8 invalid_flags;
printf("sandbox_gpio: error: invalid gpio %u\n", offset);
return &invalid_flags;
}
return &state[offset].flags;
}
static int get_gpio_flag(struct udevice *dev, unsigned offset, int flag)
{
return (*get_gpio_flags(dev, offset) & flag) != 0;
}
static int set_gpio_flag(struct udevice *dev, unsigned offset, int flag,
int value)
{
u8 *gpio = get_gpio_flags(dev, offset);
if (value)
*gpio |= flag;
else
*gpio &= ~flag;
return 0;
}
/*
* Back-channel sandbox-internal-only access to GPIO state
*/
int sandbox_gpio_get_value(struct udevice *dev, unsigned offset)
{
if (get_gpio_flag(dev, offset, GPIOF_OUTPUT))
debug("sandbox_gpio: get_value on output gpio %u\n", offset);
return get_gpio_flag(dev, offset, GPIOF_HIGH);
}
int sandbox_gpio_set_value(struct udevice *dev, unsigned offset, int value)
{
return set_gpio_flag(dev, offset, GPIOF_HIGH, value);
}
int sandbox_gpio_get_open_drain(struct udevice *dev, unsigned offset)
{
return get_gpio_flag(dev, offset, GPIOF_ODR);
}
int sandbox_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value)
{
return set_gpio_flag(dev, offset, GPIOF_ODR, value);
}
int sandbox_gpio_get_direction(struct udevice *dev, unsigned offset)
{
return get_gpio_flag(dev, offset, GPIOF_OUTPUT);
}
int sandbox_gpio_set_direction(struct udevice *dev, unsigned offset, int output)
{
return set_gpio_flag(dev, offset, GPIOF_OUTPUT, output);
}
/*
* 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)
{
debug("%s: offset:%u, value = %d\n", __func__, offset, value);
return sandbox_gpio_set_direction(dev, offset, 1) |
sandbox_gpio_set_value(dev, offset, value);
}
/* 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)
{
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;
}
return sandbox_gpio_set_value(dev, offset, value);
}
/* read GPIO ODR value of port 'offset' */
static int sb_gpio_get_open_drain(struct udevice *dev, unsigned offset)
{
debug("%s: offset:%u\n", __func__, offset);
return sandbox_gpio_get_open_drain(dev, offset);
}
/* write GPIO ODR value to port 'offset' */
static int sb_gpio_set_open_drain(struct udevice *dev, unsigned offset, int value)
{
debug("%s: offset:%u, value = %d\n", __func__, offset, value);
if (!sandbox_gpio_get_direction(dev, offset)) {
printf("sandbox_gpio: error: set_open_drain on input gpio %u\n",
offset);
return -1;
}
return sandbox_gpio_set_open_drain(dev, offset, value);
}
static int sb_gpio_get_function(struct udevice *dev, unsigned offset)
{
if (get_gpio_flag(dev, offset, GPIOF_OUTPUT))
return GPIOF_OUTPUT;
return GPIOF_INPUT;
}
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;
if (args->args[1] & GPIO_ACTIVE_LOW)
desc->flags |= GPIOD_ACTIVE_LOW;
if (args->args[1] & 2)
desc->flags |= GPIOD_IS_IN;
if (args->args[1] & 4)
desc->flags |= GPIOD_IS_OUT;
if (args->args[1] & 8)
desc->flags |= GPIOD_IS_OUT_ACTIVE;
return 0;
}
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_open_drain = sb_gpio_get_open_drain,
.set_open_drain = sb_gpio_set_open_drain,
.get_function = sb_gpio_get_function,
.xlate = sb_gpio_xlate,
};
static int sandbox_gpio_ofdata_to_platdata(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_of_valid(dev))
/* Tell the uclass how many GPIOs we have */
uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT;
dev->priv = calloc(sizeof(struct gpio_state), uc_priv->gpio_count);
return 0;
}
static int gpio_sandbox_remove(struct udevice *dev)
{
free(dev->priv);
return 0;
}
static const struct udevice_id sandbox_gpio_ids[] = {
{ .compatible = "sandbox,gpio" },
{ }
};
U_BOOT_DRIVER(gpio_sandbox) = {
.name = "gpio_sandbox",
.id = UCLASS_GPIO,
.of_match = sandbox_gpio_ids,
.ofdata_to_platdata = sandbox_gpio_ofdata_to_platdata,
.probe = gpio_sandbox_probe,
.remove = gpio_sandbox_remove,
.ops = &gpio_sandbox_ops,
};
@@ -0,0 +1,635 @@
/*
* 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 <asm/bitops.h>
#include <asm/io.h>
#include <sh_pfc.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);
}
@@ -0,0 +1,177 @@
// 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>
static int sifive_gpio_probe(struct udevice *dev)
{
struct sifive_gpio_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_ofdata_to_platdata(struct udevice *dev)
{
struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
fdt_addr_t addr;
addr = devfdt_get_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,
.ofdata_to_platdata = of_match_ptr(sifive_gpio_ofdata_to_platdata),
.platdata_auto_alloc_size = sizeof(struct sifive_gpio_platdata),
.ops = &sifive_gpio_ops,
.probe = sifive_gpio_probe,
};
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2012 Stefan Roese <sr@denx.de>
*/
/*
* Driver for SPEAr600 GPIO controller
*/
#include <common.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(&regs->gpiodir);
if (direction == GPIO_DIRECTION_OUT)
val |= 1 << gpio;
else
val &= ~(1 << gpio);
writel(val, &regs->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, &regs->gpiodata[DATA_REG_ADDR(gpio)]);
else
writel(0, &regs->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(&regs->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;
}
@@ -0,0 +1,222 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <fdtdec.h>
#include <asm/arch/gpio.h>
#include <asm/arch/stm32.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/io.h>
#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))
#ifndef CONFIG_SPL_BUILD
/*
* 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 bits_index;
int mask;
int idx;
idx = stm32_offset_to_index(dev, offset);
if (idx < 0)
return idx;
bits_index = MODE_BITS(idx);
mask = MODE_BITS_MASK << bits_index;
clrsetbits_le32(&regs->moder, mask, STM32_GPIO_MODE_IN << bits_index);
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 bits_index;
int mask;
int idx;
idx = stm32_offset_to_index(dev, offset);
if (idx < 0)
return idx;
bits_index = MODE_BITS(idx);
mask = MODE_BITS_MASK << bits_index;
clrsetbits_le32(&regs->moder, mask, STM32_GPIO_MODE_OUT << bits_index);
writel(BSRR_BIT(idx, value), &regs->bsrr);
return 0;
}
static int stm32_gpio_get_value(struct udevice *dev, unsigned offset)
{
struct stm32_gpio_priv *priv = dev_get_priv(dev);
struct stm32_gpio_regs *regs = priv->regs;
int idx;
idx = stm32_offset_to_index(dev, offset);
if (idx < 0)
return idx;
return readl(&regs->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), &regs->bsrr);
return 0;
}
static int stm32_gpio_get_function(struct udevice *dev, unsigned int offset)
{
struct stm32_gpio_priv *priv = dev_get_priv(dev);
struct stm32_gpio_regs *regs = priv->regs;
int bits_index;
int mask;
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(&regs->moder) & mask) >> bits_index;
if (mode == STM32_GPIO_MODE_OUT)
return GPIOF_OUTPUT;
if (mode == STM32_GPIO_MODE_IN)
return GPIOF_INPUT;
if (mode == STM32_GPIO_MODE_AN)
return GPIOF_UNUSED;
return GPIOF_FUNC;
}
static 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,
};
#endif
static int gpio_stm32_probe(struct udevice *dev)
{
struct stm32_gpio_priv *priv = dev_get_priv(dev);
struct clk clk;
fdt_addr_t addr;
int ret;
addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = (struct stm32_gpio_regs *)addr;
#ifndef CONFIG_SPL_BUILD
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct ofnode_phandle_args args;
const char *name;
int i;
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 == -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);
}
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);
#endif
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;
}
debug("clock enabled for device %s\n", dev->name);
return 0;
}
U_BOOT_DRIVER(gpio_stm32) = {
.name = "gpio_stm32",
.id = UCLASS_GPIO,
.probe = gpio_stm32_probe,
#ifndef CONFIG_SPL_BUILD
.ops = &gpio_stm32_ops,
#endif
.flags = DM_UC_FLAG_SEQ_ALIAS,
.priv_auto_alloc_size = sizeof(struct stm32_gpio_priv),
};
@@ -0,0 +1,376 @@
// 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_platdata {
struct sunxi_gpio *regs;
const char *bank_name; /* Name of bank, e.g. "B" */
int gpio_count;
};
#ifndef CONFIG_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
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;
}
#ifdef CONFIG_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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = dev_get_platdata(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_platdata *plat = parent->platdata;
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 = (struct sunxi_gpio_reg *)devfdt_get_addr(parent);
for (bank = 0; bank < soc_data->no_banks; bank++) {
struct sunxi_gpio_platdata *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, -1, &dev);
if (ret)
return ret;
dev_set_of_offset(dev, dev_of_offset(parent));
}
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-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,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),
{ }
};
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
@@ -0,0 +1,333 @@
/*
* 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 <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 cmd_tbl_t 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(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
static uchar chip = CONFIG_SYS_I2C_TCA642X_ADDR;
int ret = CMD_RET_USAGE, val;
uint8_t gpio_bank = 0;
uint8_t bank_shift;
ulong ul_arg2 = 0;
ulong ul_arg3 = 0;
cmd_tbl_t *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;
if (ul_arg2 <= 7) {
gpio_bank = 0;
} else if ((ul_arg2 >= 10) && (ul_arg2 <= 17)) {
gpio_bank = 1;
} else if ((ul_arg2 >= 20) && (ul_arg2 <= 27)) {
gpio_bank = 2;
} else {
printf("Requested pin is not available\n");
ret = CMD_RET_FAILURE;
goto error;
}
}
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:
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:
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:
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 */
@@ -0,0 +1,284 @@
// 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_platdata {
const char *name;
uint32_t *regs;
};
static uint32_t *tegra186_gpio_reg(struct udevice *dev, uint32_t reg,
uint32_t gpio)
{
struct tegra186_gpio_platdata *plat = dev->platdata;
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_platdata *parent_plat = parent->platdata;
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_platdata *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,
-1, &dev);
if (ret)
return ret;
dev_set_of_offset(dev, dev_of_offset(parent));
}
return 0;
}
static int tegra186_gpio_probe(struct udevice *dev)
{
struct tegra186_gpio_platdata *plat = dev->platdata;
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,
};
@@ -0,0 +1,60 @@
/* 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.
*/
#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
@@ -0,0 +1,381 @@
// 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 <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_platdata {
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->priv;
struct tegra_gpio_platdata *plat = dev->platdata;
/* 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_platdata *plat = parent->platdata;
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_platdata *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, -1, &dev);
if (ret)
return ret;
dev_set_of_offset(dev, dev_of_offset(parent));
}
}
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_alloc_size = sizeof(struct tegra_port_info),
.ops = &gpio_tegra_ops,
};
@@ -0,0 +1,137 @@
// 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/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_platdata *plat = dev_get_platdata(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_platdata(struct udevice *dev)
{
struct vybrid_gpio_platdata *plat = dev_get_platdata(dev);
fdt_addr_t base_addr;
base_addr = devfdt_get_addr(dev);
if (base_addr == FDT_ADDR_T_NONE)
return -EINVAL;
plat->base = base_addr;
plat->chip = dev->req_seq;
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,
.ofdata_to_platdata = vybrid_gpio_odata_to_platdata,
.probe = vybrid_gpio_probe,
.priv_auto_alloc_size = sizeof(struct vybrid_gpios),
.platdata_auto_alloc_size = sizeof(struct vybrid_gpio_platdata),
};
@@ -0,0 +1,311 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2013 - 2018 Xilinx, Michal Simek
*/
#include <common.h>
#include <errno.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_platdata {
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_platdata *platdata = dev_get_platdata(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 = platdata->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_platdata *platdata = dev_get_platdata(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)platdata->regs, value, offset, bank, pin, val);
if (value)
val = val | (1 << pin);
else
val = val & ~(1 << pin);
writel(val, &platdata->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_platdata *platdata = dev_get_platdata(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)platdata->regs, offset, bank, pin);
if (platdata->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(&platdata->regs->gpiodata + bank * 2);
}
val = !!(val & (1 << pin));
return val;
};
static int xilinx_gpio_get_function(struct udevice *dev, unsigned offset)
{
struct xilinx_gpio_platdata *platdata = dev_get_platdata(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 (platdata->bank_input[bank])
return GPIOF_INPUT;
/* Check if all pins are outputs */
if (platdata->bank_output[bank])
return GPIOF_OUTPUT;
/* FIXME test on dual */
val = readl(&platdata->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_platdata *platdata = dev_get_platdata(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 (platdata->bank_input[bank])
return -EINVAL;
xilinx_gpio_set_value(dev, offset, value);
if (!platdata->bank_output[bank]) {
val = readl(&platdata->regs->gpiodir + bank * 2);
val = val & ~(1 << pin);
writel(val, &platdata->regs->gpiodir + bank * 2);
}
return 0;
}
static int xilinx_gpio_direction_input(struct udevice *dev, unsigned offset)
{
struct xilinx_gpio_platdata *platdata = dev_get_platdata(dev);
int val, ret;
u32 bank, pin;
ret = xilinx_gpio_get_bank_pin(offset, &bank, &pin, dev);
if (ret)
return ret;
/* Already input */
if (platdata->bank_input[bank])
return 0;
/* can't change it if all is output by default */
if (platdata->bank_output[bank])
return -EINVAL;
val = readl(&platdata->regs->gpiodir + bank * 2);
val = val | (1 << pin);
writel(val, &platdata->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_platdata *platdata = dev_get_platdata(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 (!platdata->bank_max[1]) {
printf("%s: %s has no second channel\n",
__func__, dev->name);
return -EINVAL;
}
desc->offset += platdata->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_platdata *platdata = dev_get_platdata(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 = platdata->bank_max[0] + platdata->bank_max[1];
priv->output_val[0] = platdata->dout_default[0];
if (platdata->bank_max[1])
priv->output_val[1] = platdata->dout_default[1];
return 0;
}
static int xilinx_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct xilinx_gpio_platdata *platdata = dev_get_platdata(dev);
int is_dual;
platdata->regs = (struct gpio_regs *)dev_read_addr(dev);
platdata->bank_max[0] = dev_read_u32_default(dev,
"xlnx,gpio-width", 0);
platdata->bank_input[0] = dev_read_u32_default(dev,
"xlnx,all-inputs", 0);
platdata->bank_output[0] = dev_read_u32_default(dev,
"xlnx,all-outputs", 0);
platdata->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) {
platdata->bank_max[1] = dev_read_u32_default(dev,
"xlnx,gpio2-width", 0);
platdata->bank_input[1] = dev_read_u32_default(dev,
"xlnx,all-inputs-2", 0);
platdata->bank_output[1] = dev_read_u32_default(dev,
"xlnx,all-outputs-2", 0);
platdata->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,
.ofdata_to_platdata = xilinx_gpio_ofdata_to_platdata,
.probe = xilinx_gpio_probe,
.platdata_auto_alloc_size = sizeof(struct xilinx_gpio_platdata),
.priv_auto_alloc_size = sizeof(struct xilinx_gpio_privdata),
};
@@ -0,0 +1,410 @@
// 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/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_platdata {
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_platdata *platdata = dev_get_platdata(dev);
u32 bank;
for (bank = 0; bank < platdata->p_data->max_bank; bank++) {
if (pin_num >= platdata->p_data->bank_min[bank] &&
pin_num <= platdata->p_data->bank_max[bank]) {
*bank_num = bank;
*bank_pin_num = pin_num -
platdata->p_data->bank_min[bank];
return;
}
}
if (bank >= platdata->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_platdata *platdata = dev_get_platdata(dev);
return gpio < platdata->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_platdata *platdata = dev_get_platdata(dev);
if (check_gpio(gpio, dev) < 0)
return -1;
zynq_gpio_get_bank_pin(gpio, &bank_num, &bank_pin_num, dev);
data = readl(platdata->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_platdata *platdata = dev_get_platdata(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, platdata->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_platdata *platdata = dev_get_platdata(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(platdata->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg &= ~BIT(bank_pin_num);
writel(reg, platdata->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_platdata *platdata = dev_get_platdata(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(platdata->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel(reg, platdata->base + ZYNQ_GPIO_DIRM_OFFSET(bank_num));
/* configure the output enable reg for the pin */
reg = readl(platdata->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num));
reg |= BIT(bank_pin_num);
writel(reg, platdata->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_platdata *platdata = dev_get_platdata(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(platdata->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_platdata *platdata = dev_get_platdata(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 (platdata->p_data)
uc_priv->gpio_count = platdata->p_data->ngpio;
return 0;
}
static int zynq_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct zynq_gpio_platdata *platdata = dev_get_platdata(dev);
platdata->base = (phys_addr_t)dev_read_addr(dev);
platdata->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,
.ofdata_to_platdata = zynq_gpio_ofdata_to_platdata,
.probe = zynq_gpio_probe,
.platdata_auto_alloc_size = sizeof(struct zynq_gpio_platdata),
};