gpio: Add Aspeed GPIO driver
authorAndrew Jeffery <andrew@aj.id.au>
Tue, 15 Feb 2022 23:56:56 +0000 (10:26 +1030)
committerTom Rini <trini@konsulko.com>
Mon, 7 Mar 2022 16:36:12 +0000 (11:36 -0500)
The Aspeed GPIO driver supports the GPIO controllers found in the
AST2400, AST2500 and AST2600 BMC SoCs. The implementation is a cut-down
copy of the upstream Linux kernel driver, adapted for u-boot.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
drivers/gpio/Kconfig
drivers/gpio/Makefile
drivers/gpio/gpio-aspeed.c [new file with mode: 0644]

index 1c963c2..be64303 100644 (file)
@@ -127,6 +127,13 @@ config ATMEL_PIO4
          may be dedicated as a general purpose I/O or be assigned to
          a function of an embedded peripheral.
 
+config ASPEED_GPIO
+       bool "Aspeed GPIO Driver"
+       help
+         Say yes here to support the Aspeed GPIO driver. The controller
+         is found in the AST2400, AST2500 and AST2600 BMC SoCs and
+         provides access to over 200 GPIOs on each chip.
+
 config DA8XX_GPIO
        bool "DA8xx GPIO Driver"
        help
index 63e9be6..7202979 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_GPIO) += gpio-uclass.o
 
 obj-$(CONFIG_$(SPL_)DM_PCA953X)        += pca953x_gpio.o
 
+obj-$(CONFIG_ASPEED_GPIO)      += gpio-aspeed.o
 obj-$(CONFIG_AT91_GPIO)        += at91_gpio.o
 obj-$(CONFIG_ATMEL_PIO4)       += atmel_pio4.o
 obj-$(CONFIG_BCM6345_GPIO)     += bcm6345_gpio.o
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
new file mode 100644 (file)
index 0000000..a8a2afc
--- /dev/null
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2015 IBM Corp.
+ * Joel Stanley <joel@jms.id.au>
+ * Ryan Chen <ryan_chen@aspeedtech.com>
+ *
+ * Implementation extracted from the Linux kernel and adapted for u-boot.
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+#include <config.h>
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <asm/io.h>
+#include <linux/bug.h>
+#include <linux/sizes.h>
+
+struct aspeed_gpio_priv {
+       void *regs;
+};
+
+struct aspeed_gpio_bank {
+       u16             val_regs;       /* +0: Rd: read input value, Wr: set write latch
+                                        * +4: Rd/Wr: Direction (0=in, 1=out)
+                                        */
+       u16             rdata_reg;      /*     Rd: read write latch, Wr: <none>  */
+       u16             irq_regs;
+       u16             debounce_regs;
+       u16             tolerance_regs;
+       u16             cmdsrc_regs;
+       const char      names[4][3];
+};
+
+static const struct aspeed_gpio_bank aspeed_gpio_banks[] = {
+       {
+               .val_regs = 0x0000,
+               .rdata_reg = 0x00c0,
+               .irq_regs = 0x0008,
+               .debounce_regs = 0x0040,
+               .tolerance_regs = 0x001c,
+               .cmdsrc_regs = 0x0060,
+               .names = { "A", "B", "C", "D" },
+       },
+       {
+               .val_regs = 0x0020,
+               .rdata_reg = 0x00c4,
+               .irq_regs = 0x0028,
+               .debounce_regs = 0x0048,
+               .tolerance_regs = 0x003c,
+               .cmdsrc_regs = 0x0068,
+               .names = { "E", "F", "G", "H" },
+       },
+       {
+               .val_regs = 0x0070,
+               .rdata_reg = 0x00c8,
+               .irq_regs = 0x0098,
+               .debounce_regs = 0x00b0,
+               .tolerance_regs = 0x00ac,
+               .cmdsrc_regs = 0x0090,
+               .names = { "I", "J", "K", "L" },
+       },
+       {
+               .val_regs = 0x0078,
+               .rdata_reg = 0x00cc,
+               .irq_regs = 0x00e8,
+               .debounce_regs = 0x0100,
+               .tolerance_regs = 0x00fc,
+               .cmdsrc_regs = 0x00e0,
+               .names = { "M", "N", "O", "P" },
+       },
+       {
+               .val_regs = 0x0080,
+               .rdata_reg = 0x00d0,
+               .irq_regs = 0x0118,
+               .debounce_regs = 0x0130,
+               .tolerance_regs = 0x012c,
+               .cmdsrc_regs = 0x0110,
+               .names = { "Q", "R", "S", "T" },
+       },
+       {
+               .val_regs = 0x0088,
+               .rdata_reg = 0x00d4,
+               .irq_regs = 0x0148,
+               .debounce_regs = 0x0160,
+               .tolerance_regs = 0x015c,
+               .cmdsrc_regs = 0x0140,
+               .names = { "U", "V", "W", "X" },
+       },
+       {
+               .val_regs = 0x01E0,
+               .rdata_reg = 0x00d8,
+               .irq_regs = 0x0178,
+               .debounce_regs = 0x0190,
+               .tolerance_regs = 0x018c,
+               .cmdsrc_regs = 0x0170,
+               .names = { "Y", "Z", "AA", "AB" },
+       },
+       {
+               .val_regs = 0x01e8,
+               .rdata_reg = 0x00dc,
+               .irq_regs = 0x01a8,
+               .debounce_regs = 0x01c0,
+               .tolerance_regs = 0x01bc,
+               .cmdsrc_regs = 0x01a0,
+               .names = { "AC", "", "", "" },
+       },
+};
+
+enum aspeed_gpio_reg {
+       reg_val,
+       reg_rdata,
+       reg_dir,
+       reg_irq_enable,
+       reg_irq_type0,
+       reg_irq_type1,
+       reg_irq_type2,
+       reg_irq_status,
+       reg_debounce_sel1,
+       reg_debounce_sel2,
+       reg_tolerance,
+       reg_cmdsrc0,
+       reg_cmdsrc1,
+};
+
+#define GPIO_VAL_VALUE 0x00
+#define GPIO_VAL_DIR   0x04
+
+#define GPIO_IRQ_ENABLE        0x00
+#define GPIO_IRQ_TYPE0 0x04
+#define GPIO_IRQ_TYPE1 0x08
+#define GPIO_IRQ_TYPE2 0x0c
+#define GPIO_IRQ_STATUS        0x10
+
+#define GPIO_DEBOUNCE_SEL1 0x00
+#define GPIO_DEBOUNCE_SEL2 0x04
+
+#define GPIO_CMDSRC_0  0x00
+#define GPIO_CMDSRC_1  0x04
+#define  GPIO_CMDSRC_ARM               0
+#define  GPIO_CMDSRC_LPC               1
+#define  GPIO_CMDSRC_COLDFIRE          2
+#define  GPIO_CMDSRC_RESERVED          3
+
+/* This will be resolved at compile time */
+static inline void __iomem *bank_reg(struct aspeed_gpio_priv *gpio,
+                                    const struct aspeed_gpio_bank *bank,
+                                    const enum aspeed_gpio_reg reg)
+{
+       switch (reg) {
+       case reg_val:
+               return gpio->regs + bank->val_regs + GPIO_VAL_VALUE;
+       case reg_rdata:
+               return gpio->regs + bank->rdata_reg;
+       case reg_dir:
+               return gpio->regs + bank->val_regs + GPIO_VAL_DIR;
+       case reg_irq_enable:
+               return gpio->regs + bank->irq_regs + GPIO_IRQ_ENABLE;
+       case reg_irq_type0:
+               return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE0;
+       case reg_irq_type1:
+               return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE1;
+       case reg_irq_type2:
+               return gpio->regs + bank->irq_regs + GPIO_IRQ_TYPE2;
+       case reg_irq_status:
+               return gpio->regs + bank->irq_regs + GPIO_IRQ_STATUS;
+       case reg_debounce_sel1:
+               return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL1;
+       case reg_debounce_sel2:
+               return gpio->regs + bank->debounce_regs + GPIO_DEBOUNCE_SEL2;
+       case reg_tolerance:
+               return gpio->regs + bank->tolerance_regs;
+       case reg_cmdsrc0:
+               return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_0;
+       case reg_cmdsrc1:
+               return gpio->regs + bank->cmdsrc_regs + GPIO_CMDSRC_1;
+       }
+       BUG();
+}
+
+#define GPIO_BANK(x)   ((x) >> 5)
+#define GPIO_OFFSET(x) ((x) & 0x1f)
+#define GPIO_BIT(x)    BIT(GPIO_OFFSET(x))
+
+static const struct aspeed_gpio_bank *to_bank(unsigned int offset)
+{
+       unsigned int bank = GPIO_BANK(offset);
+
+       WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks));
+       return &aspeed_gpio_banks[bank];
+}
+
+static int
+aspeed_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+       const struct aspeed_gpio_bank *bank = to_bank(offset);
+       u32 dir = readl(bank_reg(priv, bank, reg_dir));
+
+       dir &= ~GPIO_BIT(offset);
+       writel(dir, bank_reg(priv, bank, reg_dir));
+
+       return 0;
+}
+
+static int aspeed_gpio_direction_output(struct udevice *dev, unsigned int offset,
+                                       int value)
+{
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+       const struct aspeed_gpio_bank *bank = to_bank(offset);
+       u32 dir = readl(bank_reg(priv, bank, reg_dir));
+       u32 output = readl(bank_reg(priv, bank, reg_val));
+
+       dir |= GPIO_BIT(offset);
+       writel(dir, bank_reg(priv, bank, reg_dir));
+
+       if (value)
+               output |= GPIO_BIT(offset);
+       else
+               output &= ~GPIO_BIT(offset);
+
+       writel(output, bank_reg(priv, bank, reg_val));
+
+       return 0;
+}
+
+static int aspeed_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+       const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+       return !!(readl(bank_reg(priv, bank, reg_val)) & GPIO_BIT(offset));
+}
+
+static int
+aspeed_gpio_set_value(struct udevice *dev, unsigned int offset, int value)
+{
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+       const struct aspeed_gpio_bank *bank = to_bank(offset);
+       u32 data = readl(bank_reg(priv, bank, reg_val));
+
+       if (value)
+               data |= GPIO_BIT(offset);
+       else
+               data &= ~GPIO_BIT(offset);
+
+       writel(data, bank_reg(priv, bank, reg_val));
+
+       return 0;
+}
+
+static int aspeed_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+       const struct aspeed_gpio_bank *bank = to_bank(offset);
+
+       if (readl(bank_reg(priv, bank, reg_dir)) & GPIO_BIT(offset))
+               return GPIOF_OUTPUT;
+
+       return GPIOF_INPUT;
+}
+
+static const struct dm_gpio_ops aspeed_gpio_ops = {
+       .direction_input        = aspeed_gpio_direction_input,
+       .direction_output       = aspeed_gpio_direction_output,
+       .get_value              = aspeed_gpio_get_value,
+       .set_value              = aspeed_gpio_set_value,
+       .get_function           = aspeed_gpio_get_function,
+};
+
+static int aspeed_gpio_probe(struct udevice *dev)
+{
+       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+       struct aspeed_gpio_priv *priv = dev_get_priv(dev);
+
+       uc_priv->bank_name = dev->name;
+       ofnode_read_u32(dev_ofnode(dev), "ngpios", &uc_priv->gpio_count);
+       priv->regs = devfdt_get_addr_ptr(dev);
+
+       return 0;
+}
+
+static const struct udevice_id aspeed_gpio_ids[] = {
+       { .compatible = "aspeed,ast2400-gpio",  },
+       { .compatible = "aspeed,ast2500-gpio",  },
+       { .compatible = "aspeed,ast2600-gpio",  },
+       { }
+};
+
+U_BOOT_DRIVER(gpio_aspeed) = {
+       .name   = "gpio-aspeed",
+       .id     = UCLASS_GPIO,
+       .of_match = aspeed_gpio_ids,
+       .ops    = &aspeed_gpio_ops,
+       .probe  = aspeed_gpio_probe,
+       .priv_auto = sizeof(struct aspeed_gpio_priv),
+};