summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch905
1 files changed, 598 insertions, 307 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
index 08be45ff7..146a725dd 100644
--- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch
@@ -1,18 +1,17 @@
-From 323066b26f02f043e694463a8e0cd4ada465984b Mon Sep 17 00:00:00 2001
+From e6923abbc90b5b00bc9ea401fbb2a28971d19cbe Mon Sep 17 00:00:00 2001
From: "Feist, James" <james.feist@intel.com>
-Date: Mon, 5 Jun 2017 11:13:52 -0700
-Subject: [PATCH] Add ASPEED SGPIO driver.
+Date: Tue, 4 Jun 2019 14:00:39 -0700
+Subject: [PATCH] gpio: aspeed: add ASPEED SGPIO driver
-Port aspeed sgpio driver to OBMC Kernel and
-enable it on Purley config. Based off AST sdk 4.0.
+Add SGPIO driver support for Aspeed SoCs.
Signed-off-by: James Feist <james.feist@linux.intel.com>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
---
drivers/gpio/Kconfig | 8 +
drivers/gpio/Makefile | 1 +
- drivers/gpio/sgpio-aspeed.c | 416 ++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 425 insertions(+)
+ drivers/gpio/sgpio-aspeed.c | 708 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 717 insertions(+)
create mode 100644 drivers/gpio/sgpio-aspeed.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
@@ -48,425 +47,717 @@ index 6700eee860b7..77c6ec0ee98f 100644
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/sgpio-aspeed.c
new file mode 100644
-index 000000000000..9c4add74602a
+index 000000000000..6fb402a3f74d
--- /dev/null
+++ b/drivers/gpio/sgpio-aspeed.c
-@@ -0,0 +1,416 @@
-+// SPDX-License-Identifier: GPL-2.0
-+// Copyright (C) 2012-2017 ASPEED Technology Inc.
-+// Copyright (c) 2018 Intel Corporation
+@@ -0,0 +1,708 @@
++// SPDX-License-Identifier: GPL-2.0+
++// Copyright (c) 2019 Intel Corporation
+
-+#include <asm/mach/irq.h>
+#include <linux/bitfield.h>
++#include <linux/clk.h>
+#include <linux/gpio/driver.h>
-+#include <linux/init.h>
-+#include <linux/io.h>
+#include <linux/module.h>
-+#include <linux/of_gpio.h>
++#include <linux/of.h>
+#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define ASPEED_SGPIO_CTRL 0x54
++#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
++#define ASPEED_SGPIO_CLK_DIV_MIN 1
++#define ASPEED_SGPIO_CLK_DIV_MAX 65535
++#define ASPEED_SGPIO_PINBYTES_MASK GENMASK(9, 6)
++#define ASPEED_SGPIO_PINBYTES_MIN 1
++#define ASPEED_SGPIO_PINBYTES_MAX 10
++#define ASPEED_SGPIO_ENABLE BIT(0)
++
++#define ASPEED_SGPIO_BUS_FREQ_DEFAULT 1000000
++
++struct aspeed_bank_props {
++ unsigned int bank;
++ u32 input;
++ u32 output;
++};
+
-+#ifdef ARCH_NR_GPIOS
-+#undef ARCH_NR_GPIOS
-+#endif
++struct aspeed_sgpio_config {
++ unsigned int nr_pgpios;
++ unsigned int nr_gpios;
++ const struct aspeed_bank_props *props;
++};
+
-+// TODO: move this to aspeed_sgpio_of_table
-+#if defined(CONFIG_MACH_ASPEED_G5)
-+#define GPIO_PORT_NUM 29
-+#elif defined(CONFIG_MACH_ASPEED_G4)
-+#define GPIO_PORT_NUM 28
-+#endif
++struct aspeed_sgpio {
++ struct gpio_chip chip;
++ spinlock_t lock;
++ void __iomem *base;
++ int irq;
++ const struct aspeed_sgpio_config *config;
+
-+// TODO: fix defines
-+#define GPIOS_PER_PORT 8
-+#define ARCH_NR_GPIOS (GPIOS_PER_PORT * GPIO_PORT_NUM)
-+#define ASPEED_SGPIO_CTRL 0x54
-+#define SGPIO_CHAIN_CHIP_BASE ARCH_NR_GPIOS
-+#define SGPIO_GROUP_NUMS 10
++ u32 *dcache;
++};
+
-+#define ASPEED_VIC_NUMS 64
-+#define ASPEED_FIQ_NUMS 64
-+#define ARCH_NR_I2C 14
++struct aspeed_sgpio_bank {
++ uint16_t val_reg;
++ uint16_t rdata_reg;
++ uint16_t tolerance_reg;
++ uint16_t irq_regs;
++ bool support_irq;
++ const char names[4][3];
++};
+
-+#define IRQ_I2C_CHAIN_START (ASPEED_VIC_NUMS + ASPEED_FIQ_NUMS)
-+#define IRQ_GPIO_CHAIN_START (IRQ_I2C_CHAIN_START + ARCH_NR_I2C)
-+#define IRQ_SGPIO_CHAIN_START (IRQ_GPIO_CHAIN_START + ARCH_NR_GPIOS)
++/*
++ * Note: The "val" register returns the input value sampled on the line.
++ * Or, it can be used for writing a value on the line.
++ *
++ * The "rdata" register returns the content of the write latch and thus
++ * can be used to read back what was last written reliably.
++ */
+
-+#define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6)
-+#define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
-+#define ASPEED_SGPIO_ENABLE BIT(0)
++static const struct aspeed_sgpio_bank aspeed_sgpio_banks[] = {
++ {
++ .val_reg = 0x0000,
++ .rdata_reg = 0x0070,
++ .tolerance_reg = 0x0018,
++ .irq_regs = 0x0004,
++ .support_irq = false,
++ .names = { "OA", "OB", "OC", "OD" },
++ },
++ {
++ .val_reg = 0x001C,
++ .rdata_reg = 0x0074,
++ .tolerance_reg = 0x0034,
++ .irq_regs = 0x0020,
++ .support_irq = false,
++ .names = { "OE", "OF", "OG", "OH" },
++ },
++ {
++ .val_reg = 0x0038,
++ .rdata_reg = 0x0078,
++ .tolerance_reg = 0x0050,
++ .irq_regs = 0x003C,
++ .support_irq = false,
++ .names = { "OI", "OJ" },
++ },
++ {
++ .val_reg = 0x0000,
++ .rdata_reg = 0x0070,
++ .tolerance_reg = 0x0018,
++ .irq_regs = 0x0004,
++ .support_irq = true,
++ .names = { "IA", "IB", "IC", "ID" },
++ },
++ {
++ .val_reg = 0x001C,
++ .rdata_reg = 0x0074,
++ .tolerance_reg = 0x0034,
++ .irq_regs = 0x0020,
++ .support_irq = true,
++ .names = { "IE", "IF", "IG", "IH" },
++ },
++ {
++ .val_reg = 0x0038,
++ .rdata_reg = 0x0078,
++ .tolerance_reg = 0x0050,
++ .irq_regs = 0x003C,
++ .support_irq = true,
++ .names = { "II", "IJ" },
++ },
++};
+
-+struct aspeed_sgpio {
-+ struct device *dev;
-+ int mirq; /* master irq */
-+ void __iomem *base;
-+// TODO: make below members as a struct member
-+ uint index;
-+ uint data_offset;
-+ uint data_read_offset;
-+ uint int_en_offset;
-+ uint int_type_offset;
-+ uint int_sts_offset;
-+ uint rst_tol_offset;
-+ struct gpio_chip chip;
++enum aspeed_sgpio_reg {
++ reg_val,
++ reg_rdata,
++ reg_irq_enable,
++ reg_irq_type0,
++ reg_irq_type1,
++ reg_irq_type2,
++ reg_irq_status,
++ reg_tolerance,
+};
+
-+uint aspeed_sgpio_to_irq(uint gpio)
++#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
++
++/* This will be resolved at compile time */
++static inline void __iomem *bank_reg(struct aspeed_sgpio *gpio,
++ const struct aspeed_sgpio_bank *bank,
++ const enum aspeed_sgpio_reg reg)
+{
-+ return (gpio + IRQ_SGPIO_CHAIN_START);
++ switch (reg) {
++ case reg_val:
++ return gpio->base + bank->val_reg;
++ case reg_rdata:
++ return gpio->base + bank->rdata_reg;
++ case reg_irq_enable:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_ENABLE;
++ case reg_irq_type0:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE0;
++ case reg_irq_type1:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE1;
++ case reg_irq_type2:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_TYPE2;
++ case reg_irq_status:
++ return gpio->base + bank->irq_regs + GPIO_IRQ_STATUS;
++ case reg_tolerance:
++ return gpio->base + bank->tolerance_reg;
++ default:
++ WARN_ON(1);
++ }
++
++ return NULL;
+}
-+EXPORT_SYMBOL(aspeed_sgpio_to_irq);
+
-+uint aspeed_irq_to_sgpio(uint irq)
++#define GPIO_BANK(x) ((x) >> 5)
++#define GPIO_OFFSET(x) ((x) & 0x1f)
++#define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
++
++static const struct aspeed_sgpio_bank *to_bank(unsigned int offset)
+{
-+ return (irq - IRQ_SGPIO_CHAIN_START);
++ unsigned int bank = GPIO_BANK(offset);
++
++ WARN_ON(bank >= ARRAY_SIZE(aspeed_sgpio_banks));
++ return &aspeed_sgpio_banks[bank];
+}
-+EXPORT_SYMBOL(aspeed_irq_to_sgpio);
+
-+static int aspeed_sgpio_get(struct gpio_chip *chip, unsigned offset)
++static inline bool is_bank_props_sentinel(const struct aspeed_bank_props *props)
+{
-+ struct aspeed_sgpio *priv = gpiochip_get_data(chip);
-+ uint bit_nr = priv->index * GPIOS_PER_PORT + offset;
-+ unsigned long flags;
-+ u32 v;
++ return !(props->input || props->output);
++}
+
-+ local_irq_save(flags);
++static inline const struct aspeed_bank_props *find_bank_props(
++ struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = gpio->config->props;
+
-+ v = readl(priv->base + priv->data_offset);
-+ v &= BIT(bit_nr);
++ while (!is_bank_props_sentinel(props)) {
++ if (props->bank == GPIO_BANK(offset))
++ return props;
++ props++;
++ }
+
-+ if (v)
-+ v = 1;
-+ else
-+ v = 0;
++ return NULL;
++}
+
-+ local_irq_restore(flags);
++static inline bool have_input(struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset);
+
-+ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label,
-+ offset, v);
++ return !props || (props->input & GPIO_BIT(offset));
++}
+
-+ return v;
++static inline bool have_output(struct aspeed_sgpio *gpio, unsigned int offset)
++{
++ const struct aspeed_bank_props *props = find_bank_props(gpio, offset);
++
++ return !props || (props->output & GPIO_BIT(offset));
+}
+
-+static void aspeed_sgpio_set(struct gpio_chip *chip, unsigned offset, int val)
++static int aspeed_sgpio_get(struct gpio_chip *gc, unsigned int offset)
+{
-+ struct aspeed_sgpio *priv = gpiochip_get_data(chip);
-+ uint bit_nr = priv->index * GPIOS_PER_PORT + offset;
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
++ const struct aspeed_sgpio_bank *bank = to_bank(offset);
++ enum aspeed_sgpio_reg reg;
++
++ if (have_output(gpio, offset))
++ reg = reg_rdata;
++ else
++ reg = reg_val;
++
++ return !!(ioread32(bank_reg(gpio, bank, reg)) & GPIO_BIT(offset));
++}
++
++static void aspeed_sgpio_set(struct gpio_chip *gc, unsigned int offset, int val)
++{
++ const struct aspeed_sgpio_bank *bank = to_bank(offset);
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+ unsigned long flags;
-+ u32 v;
++ u32 reg;
+
-+ local_irq_save(flags);
++ if (!have_output(gpio, offset))
++ return;
+
-+ v = readl(priv->base + priv->data_read_offset);
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ reg = ioread32(bank_reg(gpio, bank, reg_rdata));
+
+ if (val)
-+ v |= BIT(bit_nr);
++ reg |= GPIO_BIT(offset);
+ else
-+ v &= ~BIT(bit_nr);
-+
-+ writel(v, priv->base + priv->data_offset);
++ reg &= ~GPIO_BIT(offset);
+
-+ dev_dbg(priv->dev, "%s, %s[%d]: %d\n", __func__, chip->label,
-+ offset, val);
++ iowrite32(reg, bank_reg(gpio, bank, reg_val));
+
-+ local_irq_restore(flags);
++ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
-+#define SGPIO_BANK(name, index_no, data, read_data, int_en, int_type, \
-+ int_sts, rst_tol, chip_base_idx) { \
-+ .index = index_no, \
-+ .data_offset = data, \
-+ .data_read_offset = read_data, \
-+ .int_en_offset = int_en, \
-+ .int_type_offset = int_type, \
-+ .int_sts_offset = int_sts, \
-+ .rst_tol_offset = rst_tol, \
-+ .chip = { \
-+ .label = name, \
-+ .get = aspeed_sgpio_get, \
-+ .set = aspeed_sgpio_set, \
-+ .base = SGPIO_CHAIN_CHIP_BASE + chip_base_idx * 8, \
-+ .ngpio = GPIOS_PER_PORT, \
-+ }, \
-+}
++static int aspeed_sgpio_dir_in(struct gpio_chip *gc, unsigned int offset)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+
-+// TODO: use a single priv after changing it as an array member of the priv
-+static struct aspeed_sgpio aspeed_sgpio_gp[] = {
-+ SGPIO_BANK("SGPIOA", 0, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 0),
-+ SGPIO_BANK("SGPIOB", 1, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 1),
-+ SGPIO_BANK("SGPIOC", 2, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 2),
-+ SGPIO_BANK("SGPIOD", 3, 0x000, 0x070, 0x004, 0x008, 0x014, 0x018, 3),
-+ SGPIO_BANK("SGPIOE", 0, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 4),
-+ SGPIO_BANK("SGPIOF", 1, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 5),
-+ SGPIO_BANK("SGPIOG", 2, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 6),
-+ SGPIO_BANK("SGPIOH", 3, 0x01C, 0x074, 0x020, 0x024, 0x030, 0x034, 7),
-+ SGPIO_BANK("SGPIOI", 0, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 8),
-+ SGPIO_BANK("SGPIOJ", 1, 0x038, 0x078, 0x03C, 0x040, 0x04C, 0x050, 9),
-+};
++ if (!have_input(gpio, offset))
++ return -ENOTSUPP;
+
-+/**
-+ * We need to unmask the GPIO bank interrupt as soon as possible to avoid
-+ * missing GPIO interrupts for other lines in the bank. Then we need to
-+ * mask-read-clear-unmask the triggered GPIO lines in the bank to avoid missing
-+ * nested interrupts for a GPIO line. If we wait to unmask individual GPIO lines
-+ * in the bank after the line's interrupt handler has been run, we may miss some
-+ * nested interrupts.
-+ */
-+static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
++ return 0;
++}
++
++static int aspeed_sgpio_dir_out(struct gpio_chip *gc, unsigned int offset,
++ int val)
+{
-+ struct irq_chip *chip = irq_desc_get_chip(desc);
-+ struct aspeed_sgpio *priv = irq_desc_get_chip_data(desc);
-+ u32 isr;
-+ int i;
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+
-+ chained_irq_enter(chip, desc);
++ if (!have_output(gpio, offset))
++ return -ENOTSUPP;
+
-+ isr = readl(priv->base + priv->int_sts_offset);
-+ isr = (isr >> (GPIOS_PER_PORT * priv->index)) & 0xff;
++ return 0;
++}
+
-+ dev_dbg(priv->dev, "[%s] isr %x \n", priv->chip.label, isr);
++static int aspeed_sgpio_get_direction(struct gpio_chip *gc, unsigned int offset)
++{
++ struct aspeed_sgpio *gpio = gpiochip_get_data(gc);
+
-+ if (isr != 0) {
-+ for (i = 0; i < GPIOS_PER_PORT; i++) {
-+ if (BIT(i) & isr)
-+ generic_handle_irq(i * GPIOS_PER_PORT +
-+ i + IRQ_SGPIO_CHAIN_START);
-+ }
-+ }
++ if (have_output(gpio, offset))
++ return 0;
++ else if (have_input(gpio, offset))
++ return 1;
+
-+ chained_irq_exit(chip, desc);
++ return -ENOTSUPP;
+}
+
-+static void aspeed_sgpio_ack_irq(struct irq_data *d)
++static inline int
++irqd_to_aspeed_sgpio_data(struct irq_data *d, struct aspeed_sgpio **gpio,
++ const struct aspeed_sgpio_bank **bank,
++ u32 *bit, int *offset)
+{
-+ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq);
-+ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
-+ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++ struct aspeed_sgpio *internal;
++
++ *offset = irqd_to_hwirq(d);
++
++ internal = irq_data_get_irq_chip_data(d);
+
-+ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__,
-+ priv->chip.label, sgpio_irq);
++ *gpio = internal;
++ *bank = to_bank(*offset);
++ *bit = GPIO_BIT(*offset);
+
-+ writel(BIT(bit_nr), priv->base + priv->int_sts_offset);
++ return 0;
+}
+
-+static void aspeed_sgpio_mask_irq(struct irq_data *d)
++static void aspeed_sgpio_irq_ack(struct irq_data *d)
+{
-+ struct aspeed_sgpio *priv = irq_get_chip_data(d->irq);
-+ uint sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
-+ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++ const struct aspeed_sgpio_bank *bank;
++ struct aspeed_sgpio *gpio;
++ void __iomem *status_addr;
++ unsigned long flags;
++ int rc, offset;
++ u32 bit;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
++ return;
++
++ status_addr = bank_reg(gpio, bank, reg_irq_status);
++
++ spin_lock_irqsave(&gpio->lock, flags);
+
-+ /* Disable IRQ */
-+ writel(readl(priv->base + priv->int_en_offset) & ~BIT(bit_nr),
-+ priv->base + priv->int_en_offset);
++ iowrite32(bit, status_addr);
+
-+ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n ", d->irq, __func__,
-+ priv->chip.label, sgpio_irq);
++ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
-+static void aspeed_sgpio_unmask_irq(struct irq_data *d)
++static void aspeed_sgpio_irq_set_mask(struct irq_data *d, bool set)
+{
-+ struct aspeed_sgpio *priv = irq_data_get_irq_chip_data(d);
-+ u32 sgpio_irq = (d->irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
-+ uint bit_nr = priv->index * GPIOS_PER_PORT + sgpio_irq;
++ const struct aspeed_sgpio_bank *bank;
++ struct aspeed_sgpio *gpio;
++ unsigned long flags;
++ u32 reg, bit;
++ void __iomem *addr;
++ int rc, offset;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
++ return;
++
++ if (!bank->support_irq)
++ return;
++
++ addr = bank_reg(gpio, bank, reg_irq_enable);
++
++ spin_lock_irqsave(&gpio->lock, flags);
+
-+ /* Enable IRQ */
-+ writel(BIT(bit_nr), priv->base + priv->int_sts_offset);
-+ writel(readl(priv->base + priv->int_en_offset) | BIT(bit_nr),
-+ priv->base + priv->int_en_offset);
++ reg = ioread32(addr);
++ if (set)
++ reg |= bit;
++ else
++ reg &= ~bit;
++
++ iowrite32(reg, addr);
+
-+ dev_dbg(priv->dev, "irq %d: %s [%s] pin %d\n", d->irq, __func__,
-+ priv->chip.label, sgpio_irq);
++ spin_unlock_irqrestore(&gpio->lock, flags);
+}
+
-+static int aspeed_sgpio_irq_type(struct irq_data *d, unsigned int type)
++static void aspeed_sgpio_irq_mask(struct irq_data *d)
+{
-+ unsigned int irq = d->irq;
-+ struct aspeed_sgpio *priv = irq_get_chip_data(irq);
-+ u32 sgpio_irq = (irq - IRQ_SGPIO_CHAIN_START) % GPIOS_PER_PORT;
-+ u32 type0, type1, type2;
++ aspeed_sgpio_irq_set_mask(d, false);
++}
+
-+ dev_dbg(priv->dev, "irq: %d, sgpio_irq: %d , irq_type: 0x%x\n",
-+ irq, sgpio_irq, type);
++static void aspeed_sgpio_irq_unmask(struct irq_data *d)
++{
++ aspeed_sgpio_irq_set_mask(d, true);
++}
+
-+ if (type & ~IRQ_TYPE_SENSE_MASK)
++static int aspeed_sgpio_set_type(struct irq_data *d, unsigned int type)
++{
++ u32 type0 = 0;
++ u32 type1 = 0;
++ u32 type2 = 0;
++ u32 bit, reg;
++ const struct aspeed_sgpio_bank *bank;
++ irq_flow_handler_t handler;
++ struct aspeed_sgpio *gpio;
++ unsigned long flags;
++ void __iomem *addr;
++ int rc, offset;
++
++ rc = irqd_to_aspeed_sgpio_data(d, &gpio, &bank, &bit, &offset);
++ if (rc)
+ return -EINVAL;
+
-+ type0 = readl(priv->base + priv->int_type_offset);
-+ type1 = readl(priv->base + priv->int_type_offset + 0x04);
-+ type2 = readl(priv->base + priv->int_type_offset + 0x08);
++ if (!bank->support_irq)
++ return -ENOTSUPP;
+
-+ switch (type) {
-+ /* Edge rising type */
++ switch (type & IRQ_TYPE_SENSE_MASK) {
++ case IRQ_TYPE_EDGE_BOTH:
++ type2 |= bit;
++ /* fall through */
+ case IRQ_TYPE_EDGE_RISING:
-+ type0 |= BIT(sgpio_irq);
-+ type1 &= ~BIT(sgpio_irq);
-+ type2 &= ~BIT(sgpio_irq);
-+ break;
-+ /* Edge falling type */
++ type0 |= bit;
++ /* fall through */
+ case IRQ_TYPE_EDGE_FALLING:
-+ type2 |= BIT(sgpio_irq);
-+ break;
-+ case IRQ_TYPE_EDGE_BOTH:
-+ type0 &= ~BIT(sgpio_irq);
-+ type1 |= BIT(sgpio_irq);
-+ type2 &= ~BIT(sgpio_irq);
++ handler = handle_edge_irq;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
-+ type0 |= BIT(sgpio_irq);
-+ type1 |= BIT(sgpio_irq);
-+ type2 &= ~BIT(sgpio_irq);
-+ break;
++ type0 |= bit;
++ /* fall through */
+ case IRQ_TYPE_LEVEL_LOW:
-+ type0 &= ~BIT(sgpio_irq);
-+ type1 |= BIT(sgpio_irq);
-+ type2 &= ~BIT(sgpio_irq);
++ type1 |= bit;
++ handler = handle_level_irq;
+ break;
+ default:
-+ dev_dbg(priv->dev, "not supported trigger type: %d", type);
+ return -EINVAL;
-+ break;
+ }
+
-+ writel(type0, priv->base + priv->int_type_offset);
-+ writel(type1, priv->base + priv->int_type_offset + 0x04);
-+ writel(type2, priv->base + priv->int_type_offset + 0x08);
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ addr = bank_reg(gpio, bank, reg_irq_type0);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type0;
++ iowrite32(reg, addr);
++
++ addr = bank_reg(gpio, bank, reg_irq_type1);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type1;
++ iowrite32(reg, addr);
++
++ addr = bank_reg(gpio, bank, reg_irq_type2);
++ reg = ioread32(addr);
++ reg = (reg & ~bit) | type2;
++ iowrite32(reg, addr);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
++
++ irq_set_handler_locked(d, handler);
+
+ return 0;
+}
+
-+static struct irq_chip aspeed_sgpio_irq_chip = {
-+ .name = "aspeed-sgpio",
-+ .irq_ack = aspeed_sgpio_ack_irq,
-+ .irq_mask = aspeed_sgpio_mask_irq,
-+ .irq_unmask = aspeed_sgpio_unmask_irq,
-+ .irq_set_type = aspeed_sgpio_irq_type,
++static void aspeed_sgpio_irq_handler(struct irq_desc *desc)
++{
++ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
++ struct aspeed_sgpio *data = gpiochip_get_data(gc);
++ struct irq_chip *ic = irq_desc_get_chip(desc);
++ unsigned int i, p, girq;
++ unsigned long reg;
++
++ chained_irq_enter(ic, desc);
++
++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
++ const struct aspeed_sgpio_bank *bank = &aspeed_sgpio_banks[i];
++
++ if (!bank->support_irq)
++ continue;
++
++ reg = ioread32(bank_reg(data, bank, reg_irq_status));
++
++ for_each_set_bit(p, &reg, 32) {
++ girq = irq_find_mapping(gc->irq.domain, i * 32 + p);
++ generic_handle_irq(girq);
++ }
++ }
++
++ chained_irq_exit(ic, desc);
++}
++
++static struct irq_chip aspeed_sgpio_irqchip = {
++ .name = "aspeed-sgpio",
++ .irq_ack = aspeed_sgpio_irq_ack,
++ .irq_mask = aspeed_sgpio_irq_mask,
++ .irq_unmask = aspeed_sgpio_irq_unmask,
++ .irq_set_type = aspeed_sgpio_set_type,
+};
+
-+static int aspeed_sgpio_config(struct aspeed_sgpio *priv)
++static void set_irq_valid_mask(struct aspeed_sgpio *gpio)
+{
-+ /**
-+ * There is a limitation that SGPIO clock division has to be larger or
-+ * equal to 1. And the value of clock division read back is left shift
-+ * 1 bit from actual value.
-+ *
-+ * GPIO254[31:16] - Serial GPIO clock division
-+ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1)
-+ *
-+ * SGPIO master controller updates every data input when SGPMLD is low.
-+ * For example, SGPIO clock is 1MHz and number of SGPIO is 80 then each
-+ * SGPIO will be updated in every 80us.
-+ */
-+ writel(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, 10) |
-+ FIELD_PREP(ASPEED_SGPIO_PINS_MASK, SGPIO_GROUP_NUMS) |
-+ ASPEED_SGPIO_ENABLE,
-+ priv->base + ASPEED_SGPIO_CTRL);
-+ dev_dbg(priv->dev, "sgpio config reg: 0x%08X\n",
-+ readl(priv->base + ASPEED_SGPIO_CTRL));
++ const struct aspeed_bank_props *props = gpio->config->props;
+
-+ return 0;
++ while (!is_bank_props_sentinel(props)) {
++ unsigned int offset;
++ const unsigned long int input = props->input;
++
++ /* Pretty crummy approach, but similar to GPIO core */
++ for_each_clear_bit(offset, &input, 32) {
++ unsigned int i = props->bank * 32 + offset;
++
++ if (i >= gpio->config->nr_gpios)
++ break;
++
++ clear_bit(i, gpio->chip.irq.valid_mask);
++ }
++
++ props++;
++ }
+}
+
-+static int aspeed_sgpio_probe(struct platform_device *pdev)
++static int aspeed_sgpio_setup_irqs(struct aspeed_sgpio *gpio,
++ struct platform_device *pdev)
+{
-+ int i, j;
-+ uint irq;
-+ struct resource *res;
-+ struct aspeed_sgpio *priv;
-+ void __iomem *base;
-+ int mirq;
-+
-+ // aspeed_scu_multi_func_sgpio(); done via pinctl
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+
-+ base = devm_ioremap_resource(&pdev->dev, res);
-+ if (IS_ERR(base))
-+ return PTR_ERR(base);
-+
-+ mirq = platform_get_irq(pdev, 0);
-+ if (!mirq)
-+ return -ENODEV;
-+
-+ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_gp); i++) {
-+ // TODO: use heap allocation and use a single priv
-+ priv = &aspeed_sgpio_gp[i];
-+ priv->dev = &pdev->dev;
-+ priv->base = base;
-+ priv->mirq = mirq;
-+ dev_set_drvdata(&pdev->dev, priv);
-+
-+ dev_dbg(priv->dev, "add gpio_chip [%s]: %d\n", priv->chip.label,
-+ i);
-+
-+ devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
-+
-+ /* Disable Interrupt & Clear Status & Set Level-High Trigger */
-+ writel(0x00000000, priv->base + priv->int_en_offset);
-+ writel(0xffffffff, priv->base + priv->int_sts_offset);
-+ writel(0xffffffff, priv->base + priv->int_type_offset);
-+ writel(0xffffffff, priv->base + priv->int_type_offset + 0x04);
-+ writel(0x00000000, priv->base + priv->int_type_offset + 0x08);
-+
-+ // TODO: no this many chip registration is needed. fix it.
-+ for (j = 0; j < GPIOS_PER_PORT; j++) {
-+ irq = i * GPIOS_PER_PORT + j + IRQ_SGPIO_CHAIN_START;
-+ dev_dbg(priv->dev, "inst chip data %d\n", irq);
-+ irq_set_chip_data(irq, priv);
-+ irq_set_chip_and_handler(irq, &aspeed_sgpio_irq_chip,
-+ handle_level_irq);
-+ irq_clear_status_flags(irq, IRQ_NOREQUEST);
-+ }
++ const struct aspeed_sgpio_bank *bank;
++ int rc, i;
++
++ /* Initialize IRQ and tolerant settings */
++ for (i = 0; i < ARRAY_SIZE(aspeed_sgpio_banks); i++) {
++ bank = &aspeed_sgpio_banks[i];
++
++ /* Value will be reset by WDT reset */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_tolerance));
++
++ if (!bank->support_irq)
++ continue;
++
++ /* disable irq enable bits */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_enable));
++ /* clear status bits */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_status));
++ /* set rising or level-high irq */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type0));
++ /* trigger type is level */
++ iowrite32(0xffffffff, bank_reg(gpio, bank, reg_irq_type1));
++ /* single trigger mode */
++ iowrite32(0x00000000, bank_reg(gpio, bank, reg_irq_type2));
+ }
+
-+ irq_set_chained_handler(priv->mirq, aspeed_sgpio_irq_handler);
++ rc = platform_get_irq(pdev, 0);
++ if (rc < 0)
++ return rc;
++
++ gpio->irq = rc;
+
-+ aspeed_sgpio_config(priv);
++ set_irq_valid_mask(gpio);
+
-+ dev_info(&pdev->dev, "sgpio controller registered, irq %d\n",
-+ priv->mirq);
++ rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_sgpio_irqchip,
++ 0, handle_bad_irq, IRQ_TYPE_NONE);
++ if (rc) {
++ dev_info(&pdev->dev, "Could not add irqchip\n");
++ return rc;
++ }
++
++ gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_sgpio_irqchip,
++ gpio->irq, aspeed_sgpio_irq_handler);
+
+ return 0;
+}
+
-+static int aspeed_sgpio_remove(struct platform_device *pdev)
++static int aspeed_sgpio_reset_tolerance(struct gpio_chip *chip,
++ unsigned int offset, bool enable)
+{
-+ struct aspeed_sgpio *priv =
-+ &aspeed_sgpio_gp[ARRAY_SIZE(aspeed_sgpio_gp) - 1];
++ struct aspeed_sgpio *gpio = gpiochip_get_data(chip);
++ unsigned long flags;
++ void __iomem *treg;
++ u32 val;
+
-+ irq_set_chained_handler(priv->mirq, NULL);
++ treg = bank_reg(gpio, to_bank(offset), reg_tolerance);
++
++ spin_lock_irqsave(&gpio->lock, flags);
++
++ val = readl(treg);
++
++ if (enable)
++ val |= GPIO_BIT(offset);
++ else
++ val &= ~GPIO_BIT(offset);
++
++ writel(val, treg);
++
++ spin_unlock_irqrestore(&gpio->lock, flags);
+
+ return 0;
+}
+
++static int aspeed_sgpio_set_config(struct gpio_chip *chip, unsigned int offset,
++ unsigned long config)
++{
++ unsigned long param = pinconf_to_config_param(config);
++ u32 arg = pinconf_to_config_argument(config);
++
++ if (param == PIN_CONFIG_PERSIST_STATE)
++ return aspeed_sgpio_reset_tolerance(chip, offset, arg);
++
++ return -ENOTSUPP;
++}
++
++/*
++ * Any banks not specified in a struct aspeed_bank_props array are assumed to
++ * have the properties:
++ *
++ * { .input = 0xffffffff, .output = 0xffffffff }
++ */
++
++static const struct aspeed_bank_props ast_sgpio_bank_props[] = {
++ /* input output */
++ { 0, 0x00000000, 0xffffffff }, /* OA/OB/OC/OD */
++ { 1, 0x00000000, 0xffffffff }, /* OE/OF/OG/OH */
++ { 2, 0x00000000, 0x0000ffff }, /* OI/OJ */
++ { 3, 0xffffffff, 0x00000000 }, /* IA/IB/IC/ID */
++ { 4, 0xffffffff, 0x00000000 }, /* IE/IF/IG/IH */
++ { 5, 0x0000ffff, 0x00000000 }, /* II/IJ */
++ { }
++};
++
++/*
++ * This H/W has 80 bidirectional lines so this driver provides total 160 lines
++ * for 80 outputs and 80 inputs. To simplify bank register manipulation, it
++ * uses 96 lines per each input and output set so total 192 lines it has.
++ */
++static const struct aspeed_sgpio_config ast2400_config =
++ { .nr_pgpios = 224, .nr_gpios = 192, .props = ast_sgpio_bank_props };
++
++static const struct aspeed_sgpio_config ast2500_config =
++ { .nr_pgpios = 232, .nr_gpios = 192, .props = ast_sgpio_bank_props };
++
+static const struct of_device_id aspeed_sgpio_of_table[] = {
-+ { .compatible = "aspeed,ast2400-sgpio", },
-+ { .compatible = "aspeed,ast2500-sgpio", },
-+ { },
++ { .compatible = "aspeed,ast2400-sgpio", .data = &ast2400_config },
++ { .compatible = "aspeed,ast2500-sgpio", .data = &ast2500_config },
++ { }
+};
+MODULE_DEVICE_TABLE(of, aspeed_sgpio_of_table);
+
++static int __init aspeed_sgpio_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *gpio_id;
++ u32 sgpio_freq, clk_div, nb_gpios;
++ struct aspeed_sgpio *gpio;
++ unsigned long src_freq;
++ struct clk *clk;
++ int rc, banks;
++
++ gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
++ if (!gpio)
++ return -ENOMEM;
++
++ gpio->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(gpio->base))
++ return PTR_ERR(gpio->base);
++
++ spin_lock_init(&gpio->lock);
++
++ gpio_id = of_match_node(aspeed_sgpio_of_table, pdev->dev.of_node);
++ if (!gpio_id)
++ return -EINVAL;
++
++ gpio->config = gpio_id->data;
++
++ rc = device_property_read_u32(&pdev->dev, "bus-frequency", &sgpio_freq);
++ if (rc < 0) {
++ dev_warn(&pdev->dev, "Could not read bus-frequency property. Use default.\n");
++ sgpio_freq = ASPEED_SGPIO_BUS_FREQ_DEFAULT;
++ }
++
++ clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(clk)) {
++ dev_err(&pdev->dev, "Failed to get clk source.\n");
++ return PTR_ERR(clk);
++ }
++
++ /*
++ * There is a limitation that SGPIO clock division has to be larger or
++ * equal to 1. And a read back value of clock division is 1-bit left
++ * shifted from the actual value.
++ *
++ * GPIO254[31:16] - Serial GPIO clock division:
++ * Serial GPIO clock period = period of PCLK * 2 * (GPIO254[31:16] + 1)
++ *
++ * SGPIO master controller updates every data input when SGPMLD is low.
++ * For an example, SGPIO clock is 1MHz and number of SGPIO is 80. Each
++ * SGPIO will be updated every 80us.
++ */
++ src_freq = clk_get_rate(clk);
++ clk_div = src_freq / (2 * sgpio_freq) - 1;
++ if (clk_div < ASPEED_SGPIO_CLK_DIV_MIN)
++ clk_div = ASPEED_SGPIO_CLK_DIV_MIN;
++ else if (clk_div > ASPEED_SGPIO_CLK_DIV_MAX)
++ clk_div = ASPEED_SGPIO_CLK_DIV_MAX;
++
++ nb_gpios = gpio->config->nr_gpios / 16;
++ if (nb_gpios < ASPEED_SGPIO_PINBYTES_MIN)
++ nb_gpios = ASPEED_SGPIO_PINBYTES_MIN;
++ else if (nb_gpios > ASPEED_SGPIO_PINBYTES_MAX)
++ nb_gpios = ASPEED_SGPIO_PINBYTES_MAX;
++
++ iowrite32(FIELD_PREP(ASPEED_SGPIO_CLK_DIV_MASK, clk_div) |
++ FIELD_PREP(ASPEED_SGPIO_PINBYTES_MASK, nb_gpios) |
++ ASPEED_SGPIO_ENABLE,
++ gpio->base + ASPEED_SGPIO_CTRL);
++
++ gpio->chip.parent = &pdev->dev;
++ gpio->chip.ngpio = gpio->config->nr_gpios;
++
++ gpio->chip.direction_input = aspeed_sgpio_dir_in;
++ gpio->chip.direction_output = aspeed_sgpio_dir_out;
++ gpio->chip.get_direction = aspeed_sgpio_get_direction;
++ gpio->chip.get = aspeed_sgpio_get;
++ gpio->chip.set = aspeed_sgpio_set;
++ gpio->chip.set_config = aspeed_sgpio_set_config;
++ gpio->chip.label = dev_name(&pdev->dev);
++ gpio->chip.base = -1;
++ gpio->chip.irq.need_valid_mask = true;
++
++ /* Allocate a cache of the output registers */
++ banks = gpio->config->nr_gpios >> 5;
++
++ gpio->dcache = devm_kcalloc(&pdev->dev,
++ banks, sizeof(u32), GFP_KERNEL);
++
++ if (!gpio->dcache)
++ return -ENOMEM;
++
++ rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio);
++ if (rc < 0)
++ return rc;
++
++ return aspeed_sgpio_setup_irqs(gpio, pdev);
++}
++
+static struct platform_driver aspeed_sgpio_driver = {
-+ .probe = aspeed_sgpio_probe,
-+ .remove = aspeed_sgpio_remove,
+ .driver = {
-+ .name = "sgpio-aspeed",
-+ .of_match_table = of_match_ptr(aspeed_sgpio_of_table),
++ .name = KBUILD_MODNAME,
++ .of_match_table = aspeed_sgpio_of_table,
+ },
+};
+
-+static int __init aspeed_sgpio_init(void)
-+{
-+ return platform_driver_register(&aspeed_sgpio_driver);
-+}
-+subsys_initcall(aspeed_sgpio_init);
-+
-+static void __exit aspeed_sgpio_exit(void)
-+{
-+ platform_driver_unregister(&aspeed_sgpio_driver);
-+}
-+module_exit(aspeed_sgpio_exit);
++module_platform_driver_probe(aspeed_sgpio_driver, aspeed_sgpio_probe);
+
-+MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
+MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
-+MODULE_DESCRIPTION("ASPEED SGPIO driver");
++MODULE_DESCRIPTION("Aspeed SGPIO Master Driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4