GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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(®s->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(®s->data, 1 << pin);
|
||||
else
|
||||
clrbits_le32(®s->data, 1 << pin);
|
||||
/* change the data first, then the direction. to avoid glitch */
|
||||
setbits_le32(®s->direction, 1 << pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_pio_get_value(struct udevice *dev, unsigned pin)
|
||||
{
|
||||
struct altera_pio_platdata *plat = dev_get_platdata(dev);
|
||||
struct altera_pio_regs *const regs = plat->regs;
|
||||
|
||||
return !!(readl(®s->data) & (1 << pin));
|
||||
}
|
||||
|
||||
|
||||
static int altera_pio_set_value(struct udevice *dev, unsigned pin, int val)
|
||||
{
|
||||
struct altera_pio_platdata *plat = dev_get_platdata(dev);
|
||||
struct altera_pio_regs *const regs = plat->regs;
|
||||
|
||||
if (val)
|
||||
setbits_le32(®s->data, 1 << pin);
|
||||
else
|
||||
clrbits_le32(®s->data, 1 << pin);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int altera_pio_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct altera_pio_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(®s->gpio_pddr);
|
||||
|
||||
return val & (1 << offset) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset,
|
||||
enum imx_rgpio2p_direction direction)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = readl(®s->gpio_pddr);
|
||||
|
||||
switch (direction) {
|
||||
case IMX_RGPIO2P_DIRECTION_OUT:
|
||||
l |= 1 << offset;
|
||||
break;
|
||||
case IMX_RGPIO2P_DIRECTION_IN:
|
||||
l &= ~(1 << offset);
|
||||
}
|
||||
writel(l, ®s->gpio_pddr);
|
||||
}
|
||||
|
||||
static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset,
|
||||
int value)
|
||||
{
|
||||
if (value)
|
||||
writel((1 << offset), ®s->gpio_psor);
|
||||
else
|
||||
writel((1 << offset), ®s->gpio_pcor);
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset)
|
||||
{
|
||||
return (readl(®s->gpio_pdir) >> offset) & 0x01;
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
|
||||
/* Configure GPIO direction as input. */
|
||||
imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
|
||||
/* Configure GPIO output value. */
|
||||
imx_rgpio2p_bank_set_value(bank->regs, offset, value);
|
||||
|
||||
/* Configure GPIO direction as output. */
|
||||
imx_rgpio2p_bank_direction(bank->regs, offset, IMX_RGPIO2P_DIRECTION_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
|
||||
return imx_rgpio2p_bank_get_value(bank->regs, offset);
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
|
||||
imx_rgpio2p_bank_set_value(bank->regs, offset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_rgpio2p_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
|
||||
/* GPIOF_FUNC is not implemented yet */
|
||||
if (imx_rgpio2p_is_output(bank->regs, offset))
|
||||
return GPIOF_OUTPUT;
|
||||
else
|
||||
return GPIOF_INPUT;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops imx_rgpio2p_ops = {
|
||||
.direction_input = imx_rgpio2p_direction_input,
|
||||
.direction_output = imx_rgpio2p_direction_output,
|
||||
.get_value = imx_rgpio2p_get_value,
|
||||
.set_value = imx_rgpio2p_set_value,
|
||||
.get_function = imx_rgpio2p_get_function,
|
||||
};
|
||||
|
||||
static int imx_rgpio2p_probe(struct udevice *dev)
|
||||
{
|
||||
struct imx_rgpio2p_data *bank = dev_get_priv(dev);
|
||||
struct imx_rgpio2p_plat *plat = dev_get_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(®s->own[priv->bank]);
|
||||
if (!(val & (1UL << offset))) {
|
||||
debug("gpio is reserved for internal use\n");
|
||||
return -EPERM;
|
||||
}
|
||||
debug("ok\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct broadwell_bank_priv *priv = dev_get_priv(dev);
|
||||
struct pch_lp_gpio_regs *regs = priv->regs;
|
||||
|
||||
setio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct broadwell_bank_priv *priv = dev_get_priv(dev);
|
||||
struct pch_lp_gpio_regs *regs = priv->regs;
|
||||
|
||||
return inl(®s->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
|
||||
1 : 0;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct broadwell_bank_priv *priv = dev_get_priv(dev);
|
||||
struct pch_lp_gpio_regs *regs = priv->regs;
|
||||
|
||||
debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
|
||||
value);
|
||||
clrsetio_32(®s->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
|
||||
value ? CONFA_OUTPUT_HIGH : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct broadwell_bank_priv *priv = dev_get_priv(dev);
|
||||
struct pch_lp_gpio_regs *regs = priv->regs;
|
||||
|
||||
broadwell_gpio_set_value(dev, offset, value);
|
||||
clrio_32(®s->config[priv->offset + offset], CONFA_DIR_INPUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct broadwell_bank_priv *priv = dev_get_priv(dev);
|
||||
struct pch_lp_gpio_regs *regs = priv->regs;
|
||||
u32 mask = 1UL << offset;
|
||||
|
||||
if (!(inl(®s->own[priv->bank]) & mask))
|
||||
return GPIOF_FUNC;
|
||||
if (inl(®s->config[priv->offset + offset]) & CONFA_DIR_INPUT)
|
||||
return GPIOF_INPUT;
|
||||
else
|
||||
return GPIOF_OUTPUT;
|
||||
}
|
||||
|
||||
static int broadwell_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct broadwell_bank_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, ®s->p0_dir_clr);
|
||||
break;
|
||||
case 1:
|
||||
writel(mask, ®s->p1_dir_clr);
|
||||
break;
|
||||
case 2:
|
||||
/* ports 2 and 3 share a common direction */
|
||||
writel(mask, ®s->p2_p3_dir_clr);
|
||||
break;
|
||||
case 3:
|
||||
/* Setup direction only for GPIO_xx. */
|
||||
if ((mask >= 25) && (mask <= 30))
|
||||
writel(mask, ®s->p2_p3_dir_clr);
|
||||
break;
|
||||
case 4:
|
||||
/* GPI_xx; nothing to do. */
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* GPIO FUNCTION: SEE WARNING #2 */
|
||||
gpio_priv->function[offset] = GPIOF_INPUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a GPIO
|
||||
*/
|
||||
|
||||
static int lpc32xx_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
int port, rank, mask, value;
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
struct gpio_regs *regs = gpio_priv->regs;
|
||||
|
||||
port = GPIO_TO_PORT(offset);
|
||||
|
||||
switch (port) {
|
||||
case 0:
|
||||
value = readl(®s->p0_inp_state);
|
||||
break;
|
||||
case 1:
|
||||
value = readl(®s->p1_inp_state);
|
||||
break;
|
||||
case 2:
|
||||
value = readl(®s->p2_inp_state);
|
||||
break;
|
||||
case 3:
|
||||
/* Read GPO_xx and GPIO_xx (as output) using p3_outp_state. */
|
||||
value = readl(®s->p3_outp_state);
|
||||
break;
|
||||
case 4:
|
||||
/* Read GPI_xx and GPIO_xx (as input) using p3_inp_state. */
|
||||
value = readl(®s->p3_inp_state);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
rank = GPIO_TO_RANK(offset);
|
||||
mask = GPIO_TO_MASK(offset);
|
||||
|
||||
return (value & mask) >> rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a GPIO
|
||||
*/
|
||||
|
||||
static int gpio_set(struct udevice *dev, unsigned gpio)
|
||||
{
|
||||
int port, mask;
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
struct gpio_regs *regs = gpio_priv->regs;
|
||||
|
||||
port = GPIO_TO_PORT(gpio);
|
||||
mask = GPIO_TO_MASK(gpio);
|
||||
|
||||
switch (port) {
|
||||
case 0:
|
||||
writel(mask, ®s->p0_outp_set);
|
||||
break;
|
||||
case 1:
|
||||
writel(mask, ®s->p1_outp_set);
|
||||
break;
|
||||
case 2:
|
||||
writel(mask, ®s->p2_outp_set);
|
||||
break;
|
||||
case 3:
|
||||
writel(mask, ®s->p3_outp_set);
|
||||
break;
|
||||
case 4:
|
||||
/* GPI_xx; invalid. */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a GPIO
|
||||
*/
|
||||
|
||||
static int gpio_clr(struct udevice *dev, unsigned gpio)
|
||||
{
|
||||
int port, mask;
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
struct gpio_regs *regs = gpio_priv->regs;
|
||||
|
||||
port = GPIO_TO_PORT(gpio);
|
||||
mask = GPIO_TO_MASK(gpio);
|
||||
|
||||
switch (port) {
|
||||
case 0:
|
||||
writel(mask, ®s->p0_outp_clr);
|
||||
break;
|
||||
case 1:
|
||||
writel(mask, ®s->p1_outp_clr);
|
||||
break;
|
||||
case 2:
|
||||
writel(mask, ®s->p2_outp_clr);
|
||||
break;
|
||||
case 3:
|
||||
writel(mask, ®s->p3_outp_clr);
|
||||
break;
|
||||
case 4:
|
||||
/* GPI_xx; invalid. */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a GPIO
|
||||
*/
|
||||
|
||||
static int lpc32xx_gpio_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
if (value)
|
||||
return gpio_set(dev, offset);
|
||||
else
|
||||
return gpio_clr(dev, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a GPIO number 'offset' as output with given initial value.
|
||||
*/
|
||||
|
||||
static int lpc32xx_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
int port, mask;
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
struct gpio_regs *regs = gpio_priv->regs;
|
||||
|
||||
port = GPIO_TO_PORT(offset);
|
||||
mask = GPIO_TO_MASK(offset);
|
||||
|
||||
switch (port) {
|
||||
case 0:
|
||||
writel(mask, ®s->p0_dir_set);
|
||||
break;
|
||||
case 1:
|
||||
writel(mask, ®s->p1_dir_set);
|
||||
break;
|
||||
case 2:
|
||||
/* ports 2 and 3 share a common direction */
|
||||
writel(mask, ®s->p2_p3_dir_set);
|
||||
break;
|
||||
case 3:
|
||||
/* Setup direction only for GPIO_xx. */
|
||||
if ((mask >= 25) && (mask <= 30))
|
||||
writel(mask, ®s->p2_p3_dir_set);
|
||||
break;
|
||||
case 4:
|
||||
/* GPI_xx; invalid. */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* GPIO FUNCTION: SEE WARNING #2 */
|
||||
gpio_priv->function[offset] = GPIOF_OUTPUT;
|
||||
|
||||
return lpc32xx_gpio_set_value(dev, offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* GPIO functions are supposed to be computed from their current
|
||||
* configuration, but that's way too complicated in LPC32XX. A simpler
|
||||
* approach is used, where the GPIO functions are cached in an array.
|
||||
* When the GPIO is in use, its function is either "input" or "output"
|
||||
* depending on its direction, otherwise its function is "unknown".
|
||||
*
|
||||
* ** NOTE **
|
||||
*
|
||||
* THIS APPROACH WAS CHOSEN DU TO THE COMPLEX NATURE OF THE LPC32XX
|
||||
* GPIOS; DO NOT TAKE THIS AS AN EXAMPLE FOR NEW CODE.
|
||||
*/
|
||||
|
||||
static int lpc32xx_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
return gpio_priv->function[offset];
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops gpio_lpc32xx_ops = {
|
||||
.direction_input = lpc32xx_gpio_direction_input,
|
||||
.direction_output = lpc32xx_gpio_direction_output,
|
||||
.get_value = lpc32xx_gpio_get_value,
|
||||
.set_value = lpc32xx_gpio_set_value,
|
||||
.get_function = lpc32xx_gpio_get_function,
|
||||
};
|
||||
|
||||
static int lpc32xx_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct lpc32xx_gpio_priv *gpio_priv = dev_get_priv(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev->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(®s->io_conf, BIT(gpio));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_gpio_direction_output(struct udevice *dev, unsigned gpio,
|
||||
int value)
|
||||
{
|
||||
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mvebu_gpio_regs *regs = priv->regs;
|
||||
|
||||
if (value)
|
||||
setbits_le32(®s->data_out, BIT(gpio));
|
||||
else
|
||||
clrbits_le32(®s->data_out, BIT(gpio));
|
||||
clrbits_le32(®s->io_conf, BIT(gpio));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_gpio_get_function(struct udevice *dev, unsigned gpio)
|
||||
{
|
||||
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mvebu_gpio_regs *regs = priv->regs;
|
||||
u32 val;
|
||||
|
||||
val = readl(®s->io_conf) & BIT(gpio);
|
||||
if (val)
|
||||
return GPIOF_INPUT;
|
||||
else
|
||||
return GPIOF_OUTPUT;
|
||||
}
|
||||
|
||||
static int mvebu_gpio_set_value(struct udevice *dev, unsigned gpio,
|
||||
int value)
|
||||
{
|
||||
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mvebu_gpio_regs *regs = priv->regs;
|
||||
|
||||
if (value)
|
||||
setbits_le32(®s->data_out, BIT(gpio));
|
||||
else
|
||||
clrbits_le32(®s->data_out, BIT(gpio));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_gpio_get_value(struct udevice *dev, unsigned gpio)
|
||||
{
|
||||
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mvebu_gpio_regs *regs = priv->regs;
|
||||
|
||||
return !!(readl(®s->data_in) & BIT(gpio));
|
||||
}
|
||||
|
||||
static int mvebu_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct mvebu_gpio_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->regs = (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(®s->gpio_dir);
|
||||
|
||||
switch (direction) {
|
||||
case MXC_GPIO_DIRECTION_OUT:
|
||||
l |= 1 << gpio;
|
||||
break;
|
||||
case MXC_GPIO_DIRECTION_IN:
|
||||
l &= ~(1 << gpio);
|
||||
}
|
||||
writel(l, ®s->gpio_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set_value(unsigned gpio, int value)
|
||||
{
|
||||
unsigned int port = GPIO_TO_PORT(gpio);
|
||||
struct gpio_regs *regs;
|
||||
u32 l;
|
||||
|
||||
if (port >= ARRAY_SIZE(gpio_ports))
|
||||
return -1;
|
||||
|
||||
gpio &= 0x1f;
|
||||
|
||||
regs = (struct gpio_regs *)gpio_ports[port];
|
||||
|
||||
l = readl(®s->gpio_dr);
|
||||
if (value)
|
||||
l |= 1 << gpio;
|
||||
else
|
||||
l &= ~(1 << gpio);
|
||||
writel(l, ®s->gpio_dr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_get_value(unsigned gpio)
|
||||
{
|
||||
unsigned int port = GPIO_TO_PORT(gpio);
|
||||
struct gpio_regs *regs;
|
||||
u32 val;
|
||||
|
||||
if (port >= ARRAY_SIZE(gpio_ports))
|
||||
return -1;
|
||||
|
||||
gpio &= 0x1f;
|
||||
|
||||
regs = (struct gpio_regs *)gpio_ports[port];
|
||||
|
||||
val = (readl(®s->gpio_psr) >> gpio) & 0x01;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int gpio_request(unsigned gpio, const char *label)
|
||||
{
|
||||
unsigned int port = GPIO_TO_PORT(gpio);
|
||||
if (port >= ARRAY_SIZE(gpio_ports))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_free(unsigned gpio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_direction_input(unsigned gpio)
|
||||
{
|
||||
return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
|
||||
}
|
||||
|
||||
int gpio_direction_output(unsigned gpio, int value)
|
||||
{
|
||||
int ret = gpio_set_value(gpio, value);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DM_GPIO
|
||||
#include <fdtdec.h>
|
||||
static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(®s->gpio_dir);
|
||||
|
||||
return val & (1 << offset) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
|
||||
enum mxc_gpio_direction direction)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = readl(®s->gpio_dir);
|
||||
|
||||
switch (direction) {
|
||||
case MXC_GPIO_DIRECTION_OUT:
|
||||
l |= 1 << offset;
|
||||
break;
|
||||
case MXC_GPIO_DIRECTION_IN:
|
||||
l &= ~(1 << offset);
|
||||
}
|
||||
writel(l, ®s->gpio_dir);
|
||||
}
|
||||
|
||||
static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
|
||||
int value)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
l = readl(®s->gpio_dr);
|
||||
if (value)
|
||||
l |= 1 << offset;
|
||||
else
|
||||
l &= ~(1 << offset);
|
||||
writel(l, ®s->gpio_dr);
|
||||
}
|
||||
|
||||
static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
|
||||
{
|
||||
return (readl(®s->gpio_psr) >> offset) & 0x01;
|
||||
}
|
||||
|
||||
/* set GPIO pin 'gpio' as an input */
|
||||
static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
|
||||
/* Configure GPIO direction as input. */
|
||||
mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
|
||||
static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
|
||||
/* Configure GPIO output value. */
|
||||
mxc_gpio_bank_set_value(bank->regs, offset, value);
|
||||
|
||||
/* Configure GPIO direction as output. */
|
||||
mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* read GPIO IN value of pin 'gpio' */
|
||||
static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
|
||||
return mxc_gpio_bank_get_value(bank->regs, offset);
|
||||
}
|
||||
|
||||
/* write GPIO OUT value to pin 'gpio' */
|
||||
static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
|
||||
mxc_gpio_bank_set_value(bank->regs, offset, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
|
||||
/* GPIOF_FUNC is not implemented yet */
|
||||
if (mxc_gpio_is_output(bank->regs, offset))
|
||||
return GPIOF_OUTPUT;
|
||||
else
|
||||
return GPIOF_INPUT;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops gpio_mxc_ops = {
|
||||
.direction_input = mxc_gpio_direction_input,
|
||||
.direction_output = mxc_gpio_direction_output,
|
||||
.get_value = mxc_gpio_get_value,
|
||||
.set_value = mxc_gpio_set_value,
|
||||
.get_function = mxc_gpio_get_function,
|
||||
};
|
||||
|
||||
static int mxc_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct mxc_bank_info *bank = dev_get_priv(dev);
|
||||
struct mxc_gpio_plat *plat = dev_get_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) >> PAD_PIN(gpio)) & 1;
|
||||
}
|
||||
|
||||
void gpio_set_value(unsigned gpio, int value)
|
||||
{
|
||||
uint32_t bank = PAD_BANK(gpio);
|
||||
uint32_t offset = PINCTRL_DOUT(bank);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
||||
|
||||
if (value)
|
||||
writel(1 << PAD_PIN(gpio), ®->reg_set);
|
||||
else
|
||||
writel(1 << PAD_PIN(gpio), ®->reg_clr);
|
||||
}
|
||||
|
||||
int gpio_direction_input(unsigned gpio)
|
||||
{
|
||||
uint32_t bank = PAD_BANK(gpio);
|
||||
uint32_t offset = PINCTRL_DOE(bank);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
||||
|
||||
writel(1 << PAD_PIN(gpio), ®->reg_clr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_direction_output(unsigned gpio, int value)
|
||||
{
|
||||
uint32_t bank = PAD_BANK(gpio);
|
||||
uint32_t offset = PINCTRL_DOE(bank);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE + offset);
|
||||
|
||||
gpio_set_value(gpio, value);
|
||||
|
||||
writel(1 << PAD_PIN(gpio), ®->reg_set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_request(unsigned gpio, const char *label)
|
||||
{
|
||||
if (PAD_BANK(gpio) >= PINCTRL_BANKS)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_free(unsigned gpio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int name_to_gpio(const char *name)
|
||||
{
|
||||
unsigned bank, pin;
|
||||
char *end;
|
||||
|
||||
bank = simple_strtoul(name, &end, 10);
|
||||
|
||||
if (!*end || *end != ':')
|
||||
return bank;
|
||||
|
||||
pin = simple_strtoul(end + 1, NULL, 10);
|
||||
|
||||
return (bank << MXS_PAD_BANK_SHIFT) | (pin << MXS_PAD_PIN_SHIFT);
|
||||
}
|
||||
#else /* 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) >> offset) & 1;
|
||||
}
|
||||
|
||||
static int mxs_gpio_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
||||
PINCTRL_DOUT(priv->bank));
|
||||
if (value)
|
||||
writel(BIT(offset), ®->reg_set);
|
||||
else
|
||||
writel(BIT(offset), ®->reg_clr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_gpio_direction_input(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
||||
PINCTRL_DOE(priv->bank));
|
||||
|
||||
writel(BIT(offset), ®->reg_clr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
||||
PINCTRL_DOE(priv->bank));
|
||||
|
||||
mxs_gpio_set_value(dev, offset, value);
|
||||
|
||||
writel(BIT(offset), ®->reg_set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct mxs_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct mxs_register_32 *reg =
|
||||
(struct mxs_register_32 *)(MXS_PINCTRL_BASE +
|
||||
PINCTRL_DOE(priv->bank));
|
||||
bool is_output = !!(readl(®->reg) >> offset);
|
||||
|
||||
return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops gpio_mxs_ops = {
|
||||
.direction_input = mxs_gpio_direction_input,
|
||||
.direction_output = mxs_gpio_direction_output,
|
||||
.get_value = mxs_gpio_get_value,
|
||||
.set_value = mxs_gpio_set_value,
|
||||
.get_function = mxs_gpio_get_function,
|
||||
};
|
||||
|
||||
static int mxs_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct mxs_gpio_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(®s->swport_ddr, OFFSET_TO_BIT(offset));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct rockchip_gpio_regs *regs = priv->regs;
|
||||
int mask = OFFSET_TO_BIT(offset);
|
||||
|
||||
clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0);
|
||||
setbits_le32(®s->swport_ddr, mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct rockchip_gpio_regs *regs = priv->regs;
|
||||
|
||||
return readl(®s->ext_port) & OFFSET_TO_BIT(offset) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset,
|
||||
int value)
|
||||
{
|
||||
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct rockchip_gpio_regs *regs = priv->regs;
|
||||
int mask = OFFSET_TO_BIT(offset);
|
||||
|
||||
clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
return -ENODATA;
|
||||
#else
|
||||
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct rockchip_gpio_regs *regs = priv->regs;
|
||||
bool is_output;
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
is_output = readl(®s->swport_ddr) & OFFSET_TO_BIT(offset);
|
||||
|
||||
return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Simple SPL interface to GPIOs */
|
||||
#ifdef CONFIG_SPL_BUILD
|
||||
|
||||
enum {
|
||||
PULL_NONE_1V8 = 0,
|
||||
PULL_DOWN_1V8 = 1,
|
||||
PULL_UP_1V8 = 3,
|
||||
};
|
||||
|
||||
int spl_gpio_set_pull(void *vregs, uint gpio, int pull)
|
||||
{
|
||||
u32 *regs = vregs;
|
||||
uint val;
|
||||
|
||||
regs += gpio >> GPIO_BANK_SHIFT;
|
||||
gpio &= GPIO_OFFSET_MASK;
|
||||
switch (pull) {
|
||||
case GPIO_PULL_UP:
|
||||
val = PULL_UP_1V8;
|
||||
break;
|
||||
case GPIO_PULL_DOWN:
|
||||
val = PULL_DOWN_1V8;
|
||||
break;
|
||||
case GPIO_PULL_NORMAL:
|
||||
default:
|
||||
val = PULL_NONE_1V8;
|
||||
break;
|
||||
}
|
||||
clrsetbits_le32(regs, 3 << (gpio * 2), val << (gpio * 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_gpio_output(void *vregs, uint gpio, int value)
|
||||
{
|
||||
struct rockchip_gpio_regs * const regs = vregs;
|
||||
|
||||
clrsetbits_le32(®s->swport_dr, 1 << gpio, value << gpio);
|
||||
|
||||
/* Set direction */
|
||||
clrsetbits_le32(®s->swport_ddr, 1 << gpio, 1 << gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SPL_BUILD */
|
||||
|
||||
static int rockchip_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct rockchip_gpio_priv *priv = dev_get_priv(dev);
|
||||
char *end;
|
||||
int ret;
|
||||
|
||||
priv->regs = dev_read_addr_ptr(dev);
|
||||
ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK;
|
||||
end = strrchr(dev->name, '@');
|
||||
priv->bank = trailing_strtoln(dev->name, end);
|
||||
priv->name[0] = 'A' + priv->bank;
|
||||
uc_priv->bank_name = priv->name;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops gpio_rockchip_ops = {
|
||||
.direction_input = rockchip_gpio_direction_input,
|
||||
.direction_output = rockchip_gpio_direction_output,
|
||||
.get_value = rockchip_gpio_get_value,
|
||||
.set_value = rockchip_gpio_set_value,
|
||||
.get_function = rockchip_gpio_get_function,
|
||||
};
|
||||
|
||||
static const struct udevice_id rockchip_gpio_ids[] = {
|
||||
{ .compatible = "rockchip,gpio-bank" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(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(®s->gpiodir);
|
||||
|
||||
if (direction == GPIO_DIRECTION_OUT)
|
||||
val |= 1 << gpio;
|
||||
else
|
||||
val &= ~(1 << gpio);
|
||||
|
||||
writel(val, ®s->gpiodir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_set_value(unsigned gpio, int value)
|
||||
{
|
||||
struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE;
|
||||
|
||||
if (value)
|
||||
writel(1 << gpio, ®s->gpiodata[DATA_REG_ADDR(gpio)]);
|
||||
else
|
||||
writel(0, ®s->gpiodata[DATA_REG_ADDR(gpio)]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_get_value(unsigned gpio)
|
||||
{
|
||||
struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE;
|
||||
u32 val;
|
||||
|
||||
val = readl(®s->gpiodata[DATA_REG_ADDR(gpio)]);
|
||||
|
||||
return !!val;
|
||||
}
|
||||
|
||||
int gpio_request(unsigned gpio, const char *label)
|
||||
{
|
||||
if (gpio >= SPEAR_GPIO_COUNT)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_free(unsigned gpio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_toggle_value(unsigned gpio)
|
||||
{
|
||||
gpio_set_value(gpio, !gpio_get_value(gpio));
|
||||
}
|
||||
|
||||
int gpio_direction_input(unsigned gpio)
|
||||
{
|
||||
return gpio_direction(gpio, GPIO_DIRECTION_IN);
|
||||
}
|
||||
|
||||
int gpio_direction_output(unsigned gpio, int value)
|
||||
{
|
||||
int ret = gpio_direction(gpio, GPIO_DIRECTION_OUT);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gpio_set_value(gpio, value);
|
||||
return 0;
|
||||
}
|
||||
@@ -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(®s->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(®s->moder, mask, STM32_GPIO_MODE_OUT << bits_index);
|
||||
|
||||
writel(BSRR_BIT(idx, value), ®s->bsrr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_gpio_get_value(struct udevice *dev, unsigned offset)
|
||||
{
|
||||
struct stm32_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct stm32_gpio_regs *regs = priv->regs;
|
||||
int idx;
|
||||
|
||||
idx = stm32_offset_to_index(dev, offset);
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
return readl(®s->idr) & BIT(idx) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int stm32_gpio_set_value(struct udevice *dev, unsigned offset, int value)
|
||||
{
|
||||
struct stm32_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct stm32_gpio_regs *regs = priv->regs;
|
||||
int idx;
|
||||
|
||||
idx = stm32_offset_to_index(dev, offset);
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
writel(BSRR_BIT(idx, value), ®s->bsrr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_gpio_get_function(struct udevice *dev, unsigned int offset)
|
||||
{
|
||||
struct stm32_gpio_priv *priv = dev_get_priv(dev);
|
||||
struct stm32_gpio_regs *regs = priv->regs;
|
||||
int bits_index;
|
||||
int mask;
|
||||
int idx;
|
||||
u32 mode;
|
||||
|
||||
idx = stm32_offset_to_index(dev, offset);
|
||||
if (idx < 0)
|
||||
return idx;
|
||||
|
||||
bits_index = MODE_BITS(idx);
|
||||
mask = MODE_BITS_MASK << bits_index;
|
||||
|
||||
mode = (readl(®s->moder) & mask) >> bits_index;
|
||||
if (mode == STM32_GPIO_MODE_OUT)
|
||||
return GPIOF_OUTPUT;
|
||||
if (mode == STM32_GPIO_MODE_IN)
|
||||
return GPIOF_INPUT;
|
||||
if (mode == STM32_GPIO_MODE_AN)
|
||||
return GPIOF_UNUSED;
|
||||
|
||||
return GPIOF_FUNC;
|
||||
}
|
||||
|
||||
static 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),
|
||||
};
|
||||
Reference in New Issue
Block a user