summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig17
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/adp5588_gpio.c208
-rw-r--r--drivers/gpio/gpio-adi-adsp.c179
-rw-r--r--drivers/gpio/pca953x_gpio.c2
5 files changed, 408 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 21361f56e69..e11109fb56d 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -97,6 +97,15 @@ config SPL_DM_GPIO_LOOKUP_LABEL
different gpios on different hardware versions
for the same functionality in board code.
+config ADI_GPIO
+ bool "ADI GPIO driver"
+ depends on DM_GPIO && ARCH_SC5XX
+ help
+ This driver supports GPIO banks on SC5xx processors. It
+ supports inputs and outputs but does not support pin
+ interrupt functionality (PINT) or other features in the
+ Linux version of the driver.
+
config ALTERA_PIO
bool "Altera PIO driver"
depends on DM_GPIO
@@ -545,6 +554,14 @@ config DM_PCA953X
Now, max 24 bits chips and PCA953X compatible chips are
supported
+config ADP5588_GPIO
+ bool "ADP5588 GPIO expander driver"
+ depends on DM_GPIO && DM_I2C
+ help
+ Say yes here to support GPIO functionality of ADI ADP5588 chips.
+
+ The ADP5588 is an 18-port I2C GPIO expander and keypad controller.
+
config SPL_DM_PCA953X
bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL"
depends on SPL_DM_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 3f882c065d8..d426d3a2d7b 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)DM_GPIO) += gpio-uclass.o
obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o
+obj-$(CONFIG_ADI_GPIO) += gpio-adi-adsp.o
obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o
obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o
obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
@@ -74,6 +75,7 @@ obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o
obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o
obj-$(CONFIG_$(XPL_)MAX77663_GPIO) += max77663_gpio.o
obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
+obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o
obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
obj-$(CONFIG_FTGPIO010) += ftgpio010.o
diff --git a/drivers/gpio/adp5588_gpio.c b/drivers/gpio/adp5588_gpio.c
new file mode 100644
index 00000000000..d081e169897
--- /dev/null
+++ b/drivers/gpio/adp5588_gpio.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * GPIO Chip driver for Analog Devices
+ * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
+ *
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ * Contact: Greg Malysa <greg.malysa@timesys.com>
+ *
+ * Based on Michael Hennerich's Linux driver:
+ * Michael Hennerich <michael.hennerich@analog.com>
+ *
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <asm-generic/gpio.h>
+
+#define ADP5588_MAXGPIO 18
+#define ADP5588_BANK(offs) ((offs) >> 3)
+#define ADP5588_BIT(offs) (1u << ((offs) & 0x7))
+
+#define DEV_ID 0x00 /* Device ID */
+#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */
+#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */
+#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */
+#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */
+#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */
+#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */
+#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */
+#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */
+#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */
+#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */
+#define GPIO_DIR1 0x23 /* GPIO Data Direction */
+#define GPIO_DIR2 0x24 /* GPIO Data Direction */
+#define GPIO_DIR3 0x25 /* GPIO Data Direction */
+#define GPIO_PULL1 0x2C /* GPIO Pull Disable */
+#define GPIO_PULL2 0x2D /* GPIO Pull Disable */
+#define GPIO_PULL3 0x2E /* GPIO Pull Disable */
+#define ID_MASK 0x0F
+
+struct adp5588_gpio {
+ u8 dat_out[3];
+ u8 dir[3];
+};
+
+static int adp5588_gpio_read(struct udevice *dev, u8 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = dm_i2c_read(dev, reg, &val, 1);
+
+ if (ret < 0) {
+ pr_err("%s: read error\n", __func__);
+ return ret;
+ }
+
+ return val;
+}
+
+static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, &val, 1);
+ if (ret < 0) {
+ pr_err("%s: write error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int adp5588_get_value(struct udevice *dev, u32 offset)
+{
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+ unsigned int bank = ADP5588_BANK(offset);
+ unsigned int bit = ADP5588_BIT(offset);
+ int val;
+
+ if (plat->dir[bank] & bit)
+ val = plat->dat_out[bank];
+ else
+ val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank);
+
+ return !!(val & bit);
+}
+
+static int adp5588_set_value(struct udevice *dev, u32 offset,
+ int32_t value)
+{
+ unsigned int bank, bit;
+ int ret;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+ bit = ADP5588_BIT(offset);
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
+ plat->dat_out[bank]);
+
+ return ret;
+}
+
+static int adp5588_direction_input(struct udevice *dev, u32 offset)
+{
+ int ret;
+ unsigned int bank;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+
+ plat->dir[bank] &= ~ADP5588_BIT(offset);
+ ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]);
+
+ return ret;
+}
+
+static int adp5588_direction_output(struct udevice *dev,
+ u32 offset, int value)
+{
+ int ret;
+ unsigned int bank, bit;
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+
+ bank = ADP5588_BANK(offset);
+ bit = ADP5588_BIT(offset);
+
+ plat->dir[bank] |= bit;
+
+ if (value)
+ plat->dat_out[bank] |= bit;
+ else
+ plat->dat_out[bank] &= ~bit;
+
+ ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank,
+ plat->dat_out[bank]);
+ ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank,
+ plat->dir[bank]);
+
+ return ret;
+}
+
+static int adp5588_ofdata_platdata(struct udevice *dev)
+{
+ struct adp5588_gpio *plat = dev_get_plat(dev);
+ struct gpio_dev_priv *priv = dev_get_uclass_priv(dev);
+ int node = dev_of_offset(dev);
+ int ret, i, revid;
+
+ priv->gpio_count = ADP5588_MAXGPIO;
+ priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL);
+
+ ret = adp5588_gpio_read(dev, DEV_ID);
+ if (ret < 0)
+ return ret;
+
+ revid = ret & ID_MASK;
+
+ printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid);
+
+ for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
+ plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i);
+ plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i);
+ ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0);
+ ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0);
+ ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0);
+ if (ret) {
+ pr_err("%s: Initialization error\n", __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct dm_gpio_ops adp5588_ops = {
+ .direction_input = adp5588_direction_input,
+ .direction_output = adp5588_direction_output,
+ .get_value = adp5588_get_value,
+ .set_value = adp5588_set_value,
+};
+
+static const struct udevice_id adp5588_of_match_list[] = {
+ { .compatible = "adi,adp5588"},
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(gpio_adp5588) = {
+ .name = "gpio_adp5588",
+ .id = UCLASS_GPIO,
+ .ops = &adp5588_ops,
+ .of_match = adp5588_of_match_list,
+ .of_to_plat = adp5588_ofdata_platdata,
+ .plat_auto = sizeof(struct adp5588_gpio),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/gpio/gpio-adi-adsp.c b/drivers/gpio/gpio-adi-adsp.c
new file mode 100644
index 00000000000..0ce00572e08
--- /dev/null
+++ b/drivers/gpio/gpio-adi-adsp.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * (C) Copyright 2022 - Analog Devices, Inc.
+ *
+ * Written and/or maintained by Timesys Corporation
+ *
+ * Author: Greg Malysa <greg.malysa@timesys.com>
+ * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
+ */
+
+#include <dm.h>
+#include <asm-generic/gpio.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#define ADSP_PORT_MMIO_SIZE 0x80
+#define ADSP_PORT_PIN_SIZE 16
+
+#define ADSP_PORT_REG_FER 0x00
+#define ADSP_PORT_REG_FER_SET 0x04
+#define ADSP_PORT_REG_FER_CLEAR 0x08
+#define ADSP_PORT_REG_DATA 0x0c
+#define ADSP_PORT_REG_DATA_SET 0x10
+#define ADSP_PORT_REG_DATA_CLEAR 0x14
+#define ADSP_PORT_REG_DIR 0x18
+#define ADSP_PORT_REG_DIR_SET 0x1c
+#define ADSP_PORT_REG_DIR_CLEAR 0x20
+#define ADSP_PORT_REG_INEN 0x24
+#define ADSP_PORT_REG_INEN_SET 0x28
+#define ADSP_PORT_REG_INEN_CLEAR 0x2c
+#define ADSP_PORT_REG_PORT_MUX 0x30
+#define ADSP_PORT_REG_DATA_TGL 0x34
+#define ADSP_PORT_REG_POLAR 0x38
+#define ADSP_PORT_REG_POLAR_SET 0x3c
+#define ADSP_PORT_REG_POLAR_CLEAR 0x40
+#define ADSP_PORT_REG_LOCK 0x44
+#define ADSP_PORT_REG_TRIG_TGL 0x48
+
+struct adsp_gpio_priv {
+ void __iomem *base;
+ int ngpio;
+};
+
+static u32 get_port(unsigned int pin)
+{
+ return pin / ADSP_PORT_PIN_SIZE;
+}
+
+static u32 get_offset(unsigned int pin)
+{
+ return pin % ADSP_PORT_PIN_SIZE;
+}
+
+static int adsp_gpio_input(struct udevice *udev, unsigned int pin)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_CLEAR);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_SET);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int adsp_gpio_output(struct udevice *udev, unsigned int pin, int value)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR);
+
+ if (value)
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
+ else
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
+
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_SET);
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_CLEAR);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int adsp_gpio_get_value(struct udevice *udev, unsigned int pin)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ u16 val;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ val = ioread16(portbase + ADSP_PORT_REG_DATA);
+ return !!(val & BIT(offset));
+ }
+
+ return 0;
+}
+
+static int adsp_gpio_set_value(struct udevice *udev, unsigned int pin, int value)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ u32 port, offset;
+ void __iomem *portbase;
+
+ if (pin < priv->ngpio) {
+ port = get_port(pin);
+ offset = get_offset(pin);
+ portbase = priv->base + port * ADSP_PORT_MMIO_SIZE;
+
+ if (value)
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET);
+ else
+ iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR);
+ }
+
+ return 0;
+}
+
+static const struct dm_gpio_ops adsp_gpio_ops = {
+ .direction_input = adsp_gpio_input,
+ .direction_output = adsp_gpio_output,
+ .get_value = adsp_gpio_get_value,
+ .set_value = adsp_gpio_set_value,
+};
+
+static int adsp_gpio_probe(struct udevice *udev)
+{
+ struct adsp_gpio_priv *priv = dev_get_priv(udev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
+
+ uc_priv->bank_name = "adsp gpio";
+ uc_priv->gpio_count = dev_read_u32_default(udev, "adi,ngpios", 0);
+
+ if (!uc_priv->gpio_count) {
+ dev_err(udev, "Missing adi,ngpios property!\n");
+ return -ENOENT;
+ }
+
+ priv->base = dev_read_addr_ptr(udev);
+ priv->ngpio = uc_priv->gpio_count;
+
+ return 0;
+}
+
+static const struct udevice_id adsp_gpio_match[] = {
+ { .compatible = "adi,adsp-gpio" },
+ { },
+};
+
+U_BOOT_DRIVER(adi_adsp_gpio) = {
+ .name = "adi_adsp_gpio",
+ .id = UCLASS_GPIO,
+ .ops = &adsp_gpio_ops,
+ .probe = adsp_gpio_probe,
+ .priv_auto = sizeof(struct adsp_gpio_priv),
+ .of_match = adsp_gpio_match,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
index e84038f312e..523ca8473a8 100644
--- a/drivers/gpio/pca953x_gpio.c
+++ b/drivers/gpio/pca953x_gpio.c
@@ -393,6 +393,8 @@ static const struct udevice_id pca953x_ids[] = {
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+ { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), },
+ { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), },
{ .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), },
{ .compatible = "maxim,max7310", .data = OF_953X(8, 0), },