[PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC

STALE4331d SUPERSEDED

Revision v2 of 7 in this series.

9 messages, 2 authors, 2014-12-08 · open the first message on its own page

[PATCH v2 0/5] Add gpio support to Broadcom Cygnus SoC

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:36:18

This patchset contains the initial GPIO support for the Broadcom Cygnus SoC.
Cygnus has 3 GPIO controllers: 1) the ASIU GPIO; 2) the chipCommonG GPIO;
and 3) the ALWAYS-ON GPIO. All 3 types of GPIO controllers are supported by
the same Cygnus GPIO driver

Changes from v1:
 - Get rid of inline qualifier
 - Get rid of redundant check in the ISR
 - Other minor fixes to imrove code readability

Ray Jui (5):
  gpio: Cygnus: define Broadcom Cygnus GPIO binding
  gpio: Cygnus: add GPIO driver
  ARM: mach-bcm: Enable GPIO support for Cygnus
  ARM: dts: enable GPIO for Broadcom Cygnus
  MAINTAINERS: Entry for Cygnus GPIO driver

 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 +++
 MAINTAINERS                                        |    7 +
 arch/arm/boot/dts/bcm-cygnus.dtsi                  |   30 +
 arch/arm/mach-bcm/Kconfig                          |    1 +
 drivers/gpio/Kconfig                               |   11 +
 drivers/gpio/Makefile                              |    1 +
 drivers/gpio/gpio-bcm-cygnus.c                     |  712 ++++++++++++++++++++
 7 files changed, 847 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c

-- 
1.7.9.5

[PATCH v2 2/5] gpio: Cygnus: add GPIO driver

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:36:23

This GPIO driver supports all 3 GPIO controllers in the Broadcom Cygnus
SoC. The 3 GPIO controllers are 1) the ASIU GPIO controller
("brcm,cygnus-asiu-gpio"), 2) the chipCommonG GPIO controller
("brcm,cygnus-ccm-gpio"), and 3) the ALWAYS-ON GPIO controller
("brcm,cygnus-crmu-gpio")

Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
 drivers/gpio/Kconfig           |   11 +
 drivers/gpio/Makefile          |    1 +
 drivers/gpio/gpio-bcm-cygnus.c |  712 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 724 insertions(+)
 create mode 100644 drivers/gpio/gpio-bcm-cygnus.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 633ec21..3e3b0342 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -126,6 +126,17 @@ config GPIO_74XX_MMIO
 	    8 bits:	74244 (Input), 74273 (Output)
 	    16 bits:	741624 (Input), 7416374 (Output)
 
+config GPIO_BCM_CYGNUS
+	bool "Broadcom Cygnus GPIO support"
+	depends on ARCH_BCM_CYGNUS && OF_GPIO
+	help
+	  Say yes here to turn on GPIO support for Broadcom Cygnus SoC
+
+	  The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU
+	  GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and
+	  the always-ON GPIO controller (CRMU). All 3 GPIO controllers are
+	  supported by this driver
+
 config GPIO_CLPS711X
 	tristate "CLPS711X GPIO support"
 	depends on ARCH_CLPS711X || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 81755f1..31eb7e0 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_ADP5520)	+= gpio-adp5520.o
 obj-$(CONFIG_GPIO_ADP5588)	+= gpio-adp5588.o
 obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
 obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+obj-$(CONFIG_GPIO_BCM_CYGNUS)	+= gpio-bcm-cygnus.o
 obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
 obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
 obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
diff --git a/drivers/gpio/gpio-bcm-cygnus.c b/drivers/gpio/gpio-bcm-cygnus.c
new file mode 100644
index 0000000..873dce2
--- /dev/null
+++ b/drivers/gpio/gpio-bcm-cygnus.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define CYGNUS_GPIO_DATA_IN_OFFSET   0x00
+#define CYGNUS_GPIO_DATA_OUT_OFFSET  0x04
+#define CYGNUS_GPIO_OUT_EN_OFFSET    0x08
+#define CYGNUS_GPIO_IN_TYPE_OFFSET   0x0c
+#define CYGNUS_GPIO_INT_DE_OFFSET    0x10
+#define CYGNUS_GPIO_INT_EDGE_OFFSET  0x14
+#define CYGNUS_GPIO_INT_MSK_OFFSET   0x18
+#define CYGNUS_GPIO_INT_STAT_OFFSET  0x1c
+#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20
+#define CYGNUS_GPIO_INT_CLR_OFFSET   0x24
+#define CYGNUS_GPIO_PAD_RES_OFFSET   0x34
+#define CYGNUS_GPIO_RES_EN_OFFSET    0x38
+
+/* drive strength control for ASIU GPIO */
+#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58
+
+/* drive strength control for CCM GPIO */
+#define CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET  0x00
+
+#define GPIO_BANK_SIZE 0x200
+#define NGPIOS_PER_BANK 32
+#define GPIO_BIT(pin) ((pin) % NGPIOS_PER_BANK)
+#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
+
+#define GPIO_FLAG_BIT_MASK           0xffff
+#define GPIO_PULL_BIT_SHIFT          16
+#define GPIO_PULL_BIT_MASK           0x3
+
+#define GPIO_DRV_STRENGTH_BIT_SHIFT  20
+#define GPIO_DRV_STRENGTH_BITS       3
+#define GPIO_DRV_STRENGTH_BIT_MASK   ((1 << GPIO_DRV_STRENGTH_BITS) - 1)
+
+/*
+ * For GPIO internal pull up/down registers
+ */
+enum gpio_pull {
+	GPIO_PULL_NONE = 0,
+	GPIO_PULL_UP,
+	GPIO_PULL_DOWN,
+	GPIO_PULL_INVALID,
+};
+
+/*
+ * GPIO drive strength
+ */
+enum gpio_drv_strength {
+	GPIO_DRV_STRENGTH_2MA = 0,
+	GPIO_DRV_STRENGTH_4MA,
+	GPIO_DRV_STRENGTH_6MA,
+	GPIO_DRV_STRENGTH_8MA,
+	GPIO_DRV_STRENGTH_10MA,
+	GPIO_DRV_STRENGTH_12MA,
+	GPIO_DRV_STRENGTH_14MA,
+	GPIO_DRV_STRENGTH_16MA,
+	GPIO_DRV_STRENGTH_INVALID,
+};
+
+struct bcm_cygnus_gpio {
+	struct device *dev;
+	void __iomem *base;
+	void __iomem *io_ctrl;
+	spinlock_t lock;
+	struct gpio_chip gc;
+	unsigned num_banks;
+	int irq;
+	struct irq_domain *irq_domain;
+};
+
+static unsigned int gpio_base_index;
+
+static struct bcm_cygnus_gpio *to_bcm_cygnus_gpio(struct gpio_chip *gc)
+{
+	return container_of(gc, struct bcm_cygnus_gpio, gc);
+}
+
+static int bcm_cygnus_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+
+	return irq_find_mapping(cygnus_gpio->irq_domain, offset);
+}
+
+static unsigned int __gpio_reg_offset(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio)
+{
+	return GPIO_BANK(gpio) * GPIO_BANK_SIZE;
+}
+
+static unsigned int __gpio_bitpos(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio)
+{
+	return GPIO_BIT(gpio);
+}
+
+static void bcm_cygnus_gpio_irq_handler(unsigned int irq,
+		struct irq_desc *desc)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int i, bit;
+
+	chained_irq_enter(chip, desc);
+
+	cygnus_gpio = irq_get_handler_data(irq);
+
+	/* go through the entire GPIO banks and handle all interrupts */
+	for (i = 0; i < cygnus_gpio->num_banks; i++) {
+		unsigned long val = readl(cygnus_gpio->base +
+				(i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_MSTAT_OFFSET);
+
+		for_each_set_bit(bit, &val, NGPIOS_PER_BANK) {
+			unsigned pin = NGPIOS_PER_BANK * i + bit;
+			int child_irq =
+				bcm_cygnus_gpio_to_irq(&cygnus_gpio->gc, pin);
+
+			/*
+			 * Clear the interrupt before invoking the
+			 * handler, so we do not leave any window
+			 */
+			writel(1 << bit,
+				cygnus_gpio->base + (i * GPIO_BANK_SIZE) +
+				CYGNUS_GPIO_INT_CLR_OFFSET);
+
+			generic_handle_irq(child_irq);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm_cygnus_gpio_irq_ack(struct irq_data *d)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int offset, shift;
+	u32 val;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_CLR_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	val = 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_mask(struct irq_data *d)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_MSK_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+}
+
+static void bcm_cygnus_gpio_irq_unmask(struct irq_data *d)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_MSK_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	val = readl(cygnus_gpio->base + offset);
+	val |= 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+}
+
+static int bcm_cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = irq_data_get_irq_chip_data(d);
+	unsigned gpio = d->hwirq;
+	unsigned int int_type, dual_edge, edge_lvl;
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	switch (type & IRQ_TYPE_SENSE_MASK) {
+	case IRQ_TYPE_EDGE_RISING:
+		int_type = 0;
+		dual_edge = 0;
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_EDGE_FALLING:
+		int_type = 0;
+		dual_edge = 0;
+		edge_lvl = 0;
+		break;
+
+	case IRQ_TYPE_EDGE_BOTH:
+		int_type = 0;
+		dual_edge = 1;
+		edge_lvl = 0;
+		break;
+
+	case IRQ_TYPE_LEVEL_HIGH:
+		int_type = 1;
+		dual_edge = 0;
+		edge_lvl = 1;
+		break;
+
+	case IRQ_TYPE_LEVEL_LOW:
+		int_type = 1;
+		dual_edge = 0;
+		edge_lvl = 0;
+		break;
+
+	default:
+		dev_err(cygnus_gpio->dev, "invalid GPIO irq type 0x%x\n", type);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_IN_TYPE_OFFSET;
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	val |= int_type << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_DE_OFFSET;
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	val |= dual_edge << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_INT_EDGE_OFFSET;
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	val |= edge_lvl << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	return 0;
+}
+
+static struct irq_chip bcm_cygnus_gpio_irq_chip = {
+	.name = "bcm-cygnus-gpio",
+	.irq_ack = bcm_cygnus_gpio_irq_ack,
+	.irq_mask = bcm_cygnus_gpio_irq_mask,
+	.irq_unmask = bcm_cygnus_gpio_irq_unmask,
+	.irq_set_type = bcm_cygnus_gpio_irq_set_type,
+};
+
+static int bcm_cygnus_gpio_direction_input(struct gpio_chip *gc,
+		unsigned gpio)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_OUT_EN_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+
+	return 0;
+}
+
+static int bcm_cygnus_gpio_direction_output(struct gpio_chip *gc,
+		unsigned gpio, int value)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_OUT_EN_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	val = readl(cygnus_gpio->base + offset);
+	val |= 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u\n", gpio,
+			offset, shift);
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_DATA_OUT_OFFSET;
+
+	val = readl(cygnus_gpio->base + offset);
+	if (value)
+		val |= 1 << shift;
+	else
+		val &= ~(1 << shift);
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	dev_dbg(cygnus_gpio->dev,
+		"gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+		gpio, offset, shift, val);
+
+	return 0;
+}
+
+static void bcm_cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio,
+		int value)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+	unsigned int offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_DATA_OUT_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	val = readl(cygnus_gpio->base + offset);
+	if (value)
+		val |= 1 << shift;
+	else
+		val &= ~(1 << shift);
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+
+	dev_dbg(cygnus_gpio->dev,
+		"gpio:%u offset:0x%04x shift:%u val:0x%08x\n",
+		gpio, offset, shift, val);
+}
+
+static int bcm_cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+	unsigned int offset, shift;
+	u32 val;
+
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_DATA_IN_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	val = readl(cygnus_gpio->base + offset);
+	val = (val >> shift) & 1;
+
+	dev_dbg(cygnus_gpio->dev, "gpio:%u offset:0x%04x shift:%u val:%u\n",
+			gpio, offset, shift, val);
+
+	return val;
+}
+
+static struct lock_class_key gpio_lock_class;
+
+static int bcm_cygnus_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+				 irq_hw_number_t hwirq)
+{
+	int ret;
+
+	ret = irq_set_chip_data(irq, d->host_data);
+	if (ret < 0)
+		return ret;
+	irq_set_lockdep_class(irq, &gpio_lock_class);
+	irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+			handle_simple_irq);
+	set_irq_flags(irq, IRQF_VALID);
+
+	return 0;
+}
+
+static void bcm_cygnus_gpio_irq_unmap(struct irq_domain *d, unsigned int irq)
+{
+	irq_set_chip_and_handler(irq, NULL, NULL);
+	irq_set_chip_data(irq, NULL);
+}
+
+static struct irq_domain_ops bcm_cygnus_irq_ops = {
+	.map = bcm_cygnus_gpio_irq_map,
+	.unmap = bcm_cygnus_gpio_irq_unmap,
+	.xlate = irq_domain_xlate_twocell,
+};
+
+#ifdef CONFIG_OF_GPIO
+static void bcm_cygnus_gpio_set_pull(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio, enum gpio_pull pull)
+{
+	unsigned int offset, shift;
+	u32 val, pullup;
+	unsigned long flags;
+
+	switch (pull) {
+	case GPIO_PULL_UP:
+		pullup = 1;
+		break;
+	case GPIO_PULL_DOWN:
+		pullup = 0;
+		break;
+	case GPIO_PULL_NONE:
+	case GPIO_PULL_INVALID:
+	default:
+		return;
+	}
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	/* set pull up/down */
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_PAD_RES_OFFSET;
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	val = readl(cygnus_gpio->base + offset);
+	val &= ~(1 << shift);
+	if (pullup)
+		val |= 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	/* enable pad */
+	offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+		CYGNUS_GPIO_RES_EN_OFFSET;
+	val = readl(cygnus_gpio->base + offset);
+	val |= 1 << shift;
+	writel(val, cygnus_gpio->base + offset);
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static void bcm_cygnus_gpio_set_strength(struct bcm_cygnus_gpio *cygnus_gpio,
+		unsigned gpio, enum gpio_drv_strength strength)
+{
+	struct device *dev = cygnus_gpio->dev;
+	void __iomem *base;
+	unsigned int i, offset, shift;
+	u32 val;
+	unsigned long flags;
+
+	if (of_device_is_compatible(dev->of_node, "brcm,cygnus-asiu-gpio")) {
+		base = cygnus_gpio->base;
+		offset = __gpio_reg_offset(cygnus_gpio, gpio) +
+			CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET;
+	} else if (of_device_is_compatible(dev->of_node,
+			"brcm,cygnus-ccm-gpio")) {
+		if (!cygnus_gpio->io_ctrl)
+			return;
+
+		base = cygnus_gpio->io_ctrl;
+		offset = CYGNUS_GPIO_CCM_DRV0_CTRL_OFFSET;
+	} else
+		return;
+
+	shift = __gpio_bitpos(cygnus_gpio, gpio);
+
+	spin_lock_irqsave(&cygnus_gpio->lock, flags);
+
+	for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) {
+		val = readl(base + offset);
+		val &= ~(1 << shift);
+		val |= ((strength >> i) & 0x1) << shift;
+		writel(val, base + offset);
+		offset += 4;
+	}
+
+	spin_unlock_irqrestore(&cygnus_gpio->lock, flags);
+}
+
+static int bcm_cygnus_gpio_of_xlate(struct gpio_chip *gc,
+		const struct of_phandle_args *gpiospec, u32 *flags)
+{
+	struct bcm_cygnus_gpio *cygnus_gpio = to_bcm_cygnus_gpio(gc);
+	enum gpio_pull pull;
+	enum gpio_drv_strength strength;
+
+	if (gc->of_gpio_n_cells < 2)
+		return -EINVAL;
+
+	if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+		return -EINVAL;
+
+	if (gpiospec->args[0] >= gc->ngpio)
+		return -EINVAL;
+
+	pull = (gpiospec->args[1] >> GPIO_PULL_BIT_SHIFT) & GPIO_PULL_BIT_MASK;
+	if (WARN_ON(pull >= GPIO_PULL_INVALID))
+		return -EINVAL;
+
+	strength = (gpiospec->args[1] >> GPIO_DRV_STRENGTH_BIT_SHIFT) &
+		GPIO_DRV_STRENGTH_BIT_MASK;
+
+	if (flags)
+		*flags = gpiospec->args[1] & GPIO_FLAG_BIT_MASK;
+
+	bcm_cygnus_gpio_set_pull(cygnus_gpio, gpiospec->args[0], pull);
+	bcm_cygnus_gpio_set_strength(cygnus_gpio, gpiospec->args[0], strength);
+
+	return gpiospec->args[0];
+}
+#endif
+
+static const struct of_device_id bcm_cygnus_gpio_of_match[] = {
+	{ .compatible = "brcm,cygnus-crmu-gpio" },
+	{ .compatible = "brcm,cygnus-asiu-gpio" },
+	{ .compatible = "brcm,cygnus-ccm-gpio" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, bcm_cygnus_gpio_of_match);
+
+static int bcm_cygnus_gpio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct resource *res;
+	struct bcm_cygnus_gpio *cygnus_gpio;
+	struct gpio_chip *gc;
+	u32 i, ngpios;
+	int ret;
+
+	match = of_match_device(bcm_cygnus_gpio_of_match, dev);
+	if (!match) {
+		dev_err(&pdev->dev, "failed to find GPIO controller\n");
+		return -ENODEV;
+	}
+
+	cygnus_gpio = devm_kzalloc(dev, sizeof(*cygnus_gpio), GFP_KERNEL);
+	if (!cygnus_gpio)
+		return -ENOMEM;
+
+	cygnus_gpio->dev = dev;
+	platform_set_drvdata(pdev, cygnus_gpio);
+
+	if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) {
+		dev_err(&pdev->dev, "missing ngpios device tree property\n");
+		return -ENODEV;
+	}
+	cygnus_gpio->num_banks = (ngpios + NGPIOS_PER_BANK - 1) /
+		NGPIOS_PER_BANK;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "unable to get I/O resource\n");
+		return -ENODEV;
+	}
+
+	cygnus_gpio->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cygnus_gpio->base)) {
+		dev_err(&pdev->dev, "unable to map I/O memory\n");
+		return PTR_ERR(cygnus_gpio->base);
+	}
+
+	/*
+	 * Only certain types of Cygnus GPIO interfaces have I/O control
+	 * registers
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		cygnus_gpio->io_ctrl = devm_ioremap_resource(dev, res);
+		if (IS_ERR(cygnus_gpio->io_ctrl)) {
+			dev_err(&pdev->dev, "unable to map I/O memory\n");
+			return PTR_ERR(cygnus_gpio->io_ctrl);
+		}
+	}
+
+	spin_lock_init(&cygnus_gpio->lock);
+
+	gc = &cygnus_gpio->gc;
+	gc->base = gpio_base_index;
+	gpio_base_index += ngpios;
+	gc->ngpio = ngpios;
+	gc->label = dev_name(dev);
+	gc->dev = dev;
+#ifdef CONFIG_OF_GPIO
+	gc->of_node = dev->of_node;
+	gc->of_gpio_n_cells = 2;
+	gc->of_xlate = bcm_cygnus_gpio_of_xlate;
+#endif
+	gc->direction_input = bcm_cygnus_gpio_direction_input;
+	gc->direction_output = bcm_cygnus_gpio_direction_output;
+	gc->set = bcm_cygnus_gpio_set;
+	gc->get = bcm_cygnus_gpio_get;
+	gc->to_irq = bcm_cygnus_gpio_to_irq;
+
+	ret = gpiochip_add(gc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to add GPIO chip\n");
+		goto err_dec_gpio_base;
+	}
+
+	/*
+	 * Some of the GPIO interfaces do not have interrupt wired to the main
+	 * processor
+	 */
+	if (of_find_property(dev->of_node, "no-interrupt", NULL))
+		return 0;
+
+	cygnus_gpio->irq_domain = irq_domain_add_linear(dev->of_node,
+			gc->ngpio, &bcm_cygnus_irq_ops, cygnus_gpio);
+	if (!cygnus_gpio->irq_domain) {
+		dev_err(&pdev->dev, "unable to allocate IRQ domain\n");
+		ret = -ENXIO;
+		goto err_rm_gpiochip;
+	}
+
+	cygnus_gpio->irq = platform_get_irq(pdev, 0);
+	if (cygnus_gpio->irq < 0) {
+		dev_err(&pdev->dev, "unable to get IRQ\n");
+		ret = cygnus_gpio->irq;
+		goto err_rm_irq_domain;
+	}
+
+	for (i = 0; i < gc->ngpio; i++) {
+		int irq = irq_create_mapping(cygnus_gpio->irq_domain, i);
+
+		irq_set_lockdep_class(irq, &gpio_lock_class);
+		irq_set_chip_data(irq, cygnus_gpio);
+		irq_set_chip_and_handler(irq, &bcm_cygnus_gpio_irq_chip,
+				handle_simple_irq);
+		set_irq_flags(irq, IRQF_VALID);
+	}
+
+	irq_set_chained_handler(cygnus_gpio->irq, bcm_cygnus_gpio_irq_handler);
+	irq_set_handler_data(cygnus_gpio->irq, cygnus_gpio);
+
+	return 0;
+
+err_rm_irq_domain:
+	irq_domain_remove(cygnus_gpio->irq_domain);
+
+err_rm_gpiochip:
+	gpiochip_remove(gc);
+
+err_dec_gpio_base:
+	gpio_base_index -= ngpios;
+	return ret;
+}
+
+static struct platform_driver bcm_cygnus_gpio_driver = {
+	.driver = {
+		.name = "bcm-cygnus-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = bcm_cygnus_gpio_of_match,
+	},
+	.probe = bcm_cygnus_gpio_probe,
+};
+
+module_platform_driver(bcm_cygnus_gpio_driver);
+
+MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom Cygnus GPIO Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5

[PATCH v2 4/5] ARM: dts: enable GPIO for Broadcom Cygnus

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:36:27

This enables all 3 GPIO controllers including the ASIU GPIO, the
chipcommonG GPIO, and the ALWAYS-ON GPIO, for Broadcom Cygnus SoC

Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
 arch/arm/boot/dts/bcm-cygnus.dtsi |   30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi
index 5126f9e..c7587c1 100644
--- a/arch/arm/boot/dts/bcm-cygnus.dtsi
+++ b/arch/arm/boot/dts/bcm-cygnus.dtsi
@@ -54,6 +54,36 @@
 
 	/include/ "bcm-cygnus-clock.dtsi"
 
+	gpio_ccm: gpio at 1800a000 {
+		compatible = "brcm,cygnus-ccm-gpio";
+		reg = <0x1800a000 0x50>,
+			<0x0301d164 0x20>;
+		ngpios = <24>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-controller;
+	};
+
+	gpio_asiu: gpio at 180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio at 03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
-- 
1.7.9.5

[PATCH v2 5/5] MAINTAINERS: Entry for Cygnus GPIO driver

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:36:40

Signed-off-by: Ray Jui <rjui@broadcom.com>
---
 MAINTAINERS |    7 +++++++
 1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e6bff3a..8473422 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2202,6 +2202,13 @@ N:	bcm9583*
 N:	bcm583*
 N:	bcm113*
 
+BROADCOM CYGNUS GPIO DRIVER
+M:	Ray Jui <rjui@broadcom.com>
+L:	bcm-kernel-feedback-list at broadcom.com
+S:	Supported
+F:	drivers/gpio/gpio-bcm-cygnus.c
+F:	Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
+
 BROADCOM KONA GPIO DRIVER
 M:	Ray Jui <rjui@broadcom.com>
 L:	bcm-kernel-feedback-list at broadcom.com
-- 
1.7.9.5

[PATCH v2 3/5] ARM: mach-bcm: Enable GPIO support for Cygnus

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:37:13

Enable GPIO driver for Broadcom Cygnus SoC by selecting GPIO_BCM_CYGNUS

Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
 arch/arm/mach-bcm/Kconfig |    1 +
 1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index aaeec78..5066d5d 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -29,6 +29,7 @@ config ARCH_BCM_IPROC
 config ARCH_BCM_CYGNUS
 	bool "Broadcom Cygnus Support" if ARCH_MULTI_V7
 	select ARCH_BCM_IPROC
+	select GPIO_BCM_CYGNUS
 	help
 	  Enable support for the Cygnus family,
 	  which includes the following variants:
-- 
1.7.9.5

[PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 02:37:43

Document the GPIO device tree binding for Broadcom Cygnus SoC

Signed-off-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
---
 .../devicetree/bindings/gpio/brcm,cygnus-gpio.txt  |   85 ++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
diff --git a/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
new file mode 100644
index 0000000..24a1513
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/brcm,cygnus-gpio.txt
@@ -0,0 +1,85 @@
+Broadcom Cygnus GPIO Controller
+
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
+
+- reg:
+    Define the base and range of the I/O address space that contain the Cygnus
+GPIO controller registers
+
+- ngpios:
+    Total number of GPIOs the controller provides
+
+- #gpio-cells:
+    Must be two. The first cell is the GPIO pin number (within the
+controller's domain) and the second cell is used for the following:
+    bit[0]: polarity (0 for normal and 1 for inverted)
+    bit[18:16]: internal pull up/down: 0 - pull up/down disabled
+                                       1 - pull up enabled
+                                       2 - pull down enabled
+    bit[22:20]: drive strength: 0 - 2 mA
+                                1 - 4 mA
+                                2 - 6 mA
+                                3 - 8 mA
+                                4 - 10 mA
+                                5 - 12 mA
+                                6 - 14 mA
+                                7 - 16 mA
+
+- gpio-controller:
+    Specifies that the node is a GPIO controller
+
+Optional properties:
+
+- interrupt-controller:
+    Specifies that the node is an interrupt controller. Not all Cygnus GPIO
+interfaces support interrupt, e.g., the CRMU GPIO controller does not have its
+interrupt routed to the main processor's GIC
+
+- interrupts:
+    The interrupt outputs from the GPIO controller.
+
+- no-interrupt:
+    Specifies that the GPIO interface does not support interrupt
+
+Example:
+	gpio_asiu: gpio at 180a5000 {
+		compatible = "brcm,cygnus-asiu-gpio";
+		reg = <0x180a5000 0x668>;
+		ngpios = <122>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		interrupt-controller;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+	};
+
+	gpio_crmu: gpio at 03024800 {
+		compatible = "brcm,cygnus-crmu-gpio";
+		reg = <0x03024800 0x50>;
+		ngpios = <6>;
+		#gpio-cells = <2>;
+		gpio-controller;
+		no-interrupt;
+	};
+
+	/*
+	 * Touchscreen that uses the ASIU GPIO 100, with internal pull-up
+	 * enabled
+	 */
+	tsc {
+		...
+		...
+		gpio-event = <&gpio_asiu 100 0x10000>;
+	};
+
+	/* Bluetooth that uses the CRMU GPIO 2, with polarity inverted */
+	bluetooth {
+		...
+		...
+		bcm,rfkill-bank-sel = <&gpio_crmu 2 1>
+	}
-- 
1.7.9.5

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

From: Arnd Bergmann <arnd@arndb.de>
Date: 2014-12-08 11:22:44

On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
quoted hunk
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
How different are these? If they are almost the same, would it
be better to use the same compatible string for all of them and
describe the differences in extra properties?

If they are rather different, maybe you should have a separate
binding and driver for each?

	Arnd

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

From: Ray Jui <rjui@broadcom.com>
Date: 2014-12-08 16:55:26


On 12/8/2014 3:22 AM, Arnd Bergmann wrote:
On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
quoted
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
How different are these? If they are almost the same, would it
be better to use the same compatible string for all of them and
describe the differences in extra properties?

If they are rather different, maybe you should have a separate
binding and driver for each?

	Arnd
They are quite similar with the following minor differences:
1) ChipcommonG GPIO controller uses a separate register block 
(0x0301d164) to control drive stregnth
2) Cannot control drive strength for the CMRU GPIO
3) CRMU GPIO controller's interrupt is not connected to GIC of A9. 
Currently that's taken care of by using a "no-interrupt" device tree 
property

I can change to use the common compatible string "brcm,cygnus-gpio". 
With an introduction of property "no-drv-stregnth" which should be set 
for CRMU GPIO controller. For ChipcommonG GPIO, it will have a second 
register block defined, so we'll know to use that second register block 
for drive strength configuration. For the rest, we assume normal drive 
strength configuration (i.e., ASIU in our case).

Looking at this again, it looks like the "no-interrupt" property is 
really redundant. For GPIO controller without interrupt connected to A9, 
we can simply leave its interrupt properties not defined. I'll get rid 
of it along with the above changes.

Re: [PATCH v2 1/5] gpio: Cygnus: define Broadcom Cygnus GPIO binding

From: Arnd Bergmann <arnd@arndb.de>
Date: 2014-12-08 17:12:14

On Monday 08 December 2014 08:55:20 Ray Jui wrote:
On 12/8/2014 3:22 AM, Arnd Bergmann wrote:
quoted
On Sunday 07 December 2014 18:38:32 Ray Jui wrote:
quoted
+Required properties:
+
+- compatible:
+    Currently supported Cygnus GPIO controllers include:
+    "brcm,cygnus-ccm-gpio": ChipcommonG GPIO controller
+    "brcm,cygnus-asiu-gpio": ASIU GPIO controller
+    "brcm,cygnus-crmu-gpio": CRMU GPIO controller
How different are these? If they are almost the same, would it
be better to use the same compatible string for all of them and
describe the differences in extra properties?

If they are rather different, maybe you should have a separate
binding and driver for each?

	Arnd
They are quite similar with the following minor differences:
1) ChipcommonG GPIO controller uses a separate register block 
(0x0301d164) to control drive stregnth
2) Cannot control drive strength for the CMRU GPIO
This can be deducted from having one or two register blocks I
assume.
3) CRMU GPIO controller's interrupt is not connected to GIC of A9. 
Currently that's taken care of by using a "no-interrupt" device tree 
property
No need for this property even, just see if there is an "interrupts"
property or not.
 
I can change to use the common compatible string "brcm,cygnus-gpio". 
With an introduction of property "no-drv-stregnth" which should be set 
for CRMU GPIO controller. 
Ok.
For ChipcommonG GPIO, it will have a second 
register block defined, so we'll know to use that second register block 
for drive strength configuration. For the rest, we assume normal drive 
strength configuration (i.e., ASIU in our case).
Maybe see if something older than cygnus was already using a compatible
gpio controller and then use the name of that.
Looking at this again, it looks like the "no-interrupt" property is 
really redundant. For GPIO controller without interrupt connected to A9, 
we can simply leave its interrupt properties not defined. I'll get rid 
of it along with the above changes.
Right.

	Arnd
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help