[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1366727607-27444-7-git-send-email-james.hogan@imgtec.com>
Date: Tue, 23 Apr 2013 15:33:25 +0100
From: James Hogan <james.hogan@...tec.com>
To: <linux-kernel@...r.kernel.org>
CC: Grant Likely <grant.likely@...retlab.ca>,
Rob Herring <rob.herring@...xeda.com>,
Linus Walleij <linus.walleij@...aro.org>,
<devicetree-discuss@...ts.ozlabs.org>,
James Hogan <james.hogan@...tec.com>,
Rob Landley <rob@...dley.net>, <linux-doc@...r.kernel.org>
Subject: [PATCH 6/8] gpio-tz1090: add TZ1090 gpio driver
Add a GPIO driver for the main GPIOs found in the TZ1090 (Comet) SoC.
This doesn't include low-power GPIOs as they're controlled separately
via the Powerdown Controller (PDC) registers.
The driver is instantiated by device tree and supports interrupts for
all GPIOs.
Signed-off-by: James Hogan <james.hogan@...tec.com>
Cc: Grant Likely <grant.likely@...retlab.ca>
Cc: Rob Herring <rob.herring@...xeda.com>
Cc: Rob Landley <rob@...dley.net>
Cc: Linus Walleij <linus.walleij@...aro.org>
Cc: linux-doc@...r.kernel.org
---
.../devicetree/bindings/gpio/gpio-tz1090.txt | 77 +++
arch/metag/Kconfig.soc | 1 +
arch/metag/boot/dts/tz1090.dtsi | 35 ++
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-tz1090.c | 628 +++++++++++++++++++++
6 files changed, 749 insertions(+)
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
create mode 100644 drivers/gpio/gpio-tz1090.c
diff --git a/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
new file mode 100644
index 0000000..5e426df
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-tz1090.txt
@@ -0,0 +1,77 @@
+ImgTec TZ1090 GPIO Controller
+
+Required properties:
+- compatible: Compatible property value should be "img,tz1090-gpio>".
+
+- reg: Physical base address of the controller and length of memory mapped
+ region.
+
+- #address-cells: Should be 1 (for bank subnodes)
+
+- #size-cells: Should be 0 (for bank subnodes)
+
+- Each bank of GPIOs should have a subnode to represent it.
+
+ Bank subnode required properties:
+ - reg: Index of bank in the range 0 to 2.
+
+ - gpio-controller: Specifies that the node is a gpio controller.
+
+ - #gpio-cells: Should be 2. The syntax of the gpio specifier used by client
+ nodes should have the following values.
+ <[phandle of the gpio controller node]
+ [gpio number within the gpio bank]
+ [standard Linux gpio flags]>
+
+ Values for gpio specifier:
+ - GPIO number: a value in the range 0 to 29.
+ - GPIO flags: standard Linux GPIO flags as found in of_gpio.h
+
+ Bank subnode optional properties:
+ - gpio-ranges: Mapping to pin controller pins
+
+ - interrupts: Interrupt for the entire bank
+
+ - interrupt-controller: Specifies that the node is an interrupt controller
+
+ - #interrupt-cells: Should be 2. The syntax of the interrupt specifier used by
+ client nodes should have the following values.
+ <[phandle of the interurupt controller]
+ [gpio number within the gpio bank]
+ [standard Linux irq flags]>
+
+ Values for irq specifier:
+ - GPIO number: a value in the range 0 to 29
+ - IRQ flags: standard Linux IRQ flags for edge and level triggering
+
+
+
+Example:
+
+ gpios: gpio-controller@...05800 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "img,tz1090-gpio";
+ reg = <0x02005800 0x90>;
+
+ /* bank 0 with an interrupt */
+ gpios0: bank@0 {
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <0>;
+ interrupts = <13 4 /* level */>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl 0 30>;
+ interrupt-controller;
+ };
+
+ /* bank 2 without interrupt */
+ gpios2: bank@2 {
+ #gpio-cells = <2>;
+ reg = <2>;
+ gpio-controller;
+ gpio-ranges = <&pinctrl 60 30>;
+ };
+ };
+
+
diff --git a/arch/metag/Kconfig.soc b/arch/metag/Kconfig.soc
index 944dc30..d61d687 100644
--- a/arch/metag/Kconfig.soc
+++ b/arch/metag/Kconfig.soc
@@ -16,6 +16,7 @@ config META21_FPGA
config SOC_TZ1090
bool "Toumaz Xenif TZ1090 SoC (Comet)"
+ select ARCH_WANT_OPTIONAL_GPIOLIB
select IMGPDC_IRQ
select METAG_LNKGET_AROUND_CACHE
select METAG_META21
diff --git a/arch/metag/boot/dts/tz1090.dtsi b/arch/metag/boot/dts/tz1090.dtsi
index ccbcbf1..be69c4f 100644
--- a/arch/metag/boot/dts/tz1090.dtsi
+++ b/arch/metag/boot/dts/tz1090.dtsi
@@ -47,5 +47,40 @@
compatible = "img,tz1090-pinctrl";
reg = <0x02005800 0xe4>;
};
+
+ gpios: gpios@...05800 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "img,tz1090-gpio";
+ reg = <0x02005800 0x90>;
+
+ gpios0: bank@0 {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <0>;
+ interrupts = <13 4 /* level */>;
+ gpio-ranges = <&pinctrl 0 30>;
+ };
+ gpios1: bank@1 {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <1>;
+ interrupts = <14 4 /* level */>;
+ gpio-ranges = <&pinctrl 30 30>;
+ };
+ gpios2: bank@2 {
+ gpio-controller;
+ interrupt-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ reg = <2>;
+ interrupts = <15 4 /* level */>;
+ gpio-ranges = <&pinctrl 60 30>;
+ };
+ };
};
};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 93aaadf..922fefa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -227,6 +227,13 @@ config GPIO_TS5500
blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600
LCD port.
+config GPIO_TZ1090
+ bool "Toumaz Xenif TZ1090 GPIO support"
+ depends on SOC_TZ1090
+ default y
+ help
+ Say yes here to support Toumaz Xenif TZ1090 GPIOs.
+
config GPIO_VT8500
bool "VIA/Wondermedia SoC GPIO Support"
depends on ARCH_VT8500
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 22e07bc..fab78c4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
+obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c
new file mode 100644
index 0000000..023d572
--- /dev/null
+++ b/drivers/gpio/gpio-tz1090.c
@@ -0,0 +1,628 @@
+/*
+ * Toumaz Xenif TZ1090 GPIO handling.
+ *
+ * Copyright (C) 2008-2013 Imagination Technologies Ltd.
+ *
+ * Based on ARM PXA code and others.
+ *
+ * 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 <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <asm/global_lock.h>
+
+/* Register offsets from bank base address */
+#define REG_GPIO_DIR 0x00
+#define REG_GPIO_IRQ_PLRT 0x20
+#define REG_GPIO_IRQ_TYPE 0x30
+#define REG_GPIO_IRQ_EN 0x40
+#define REG_GPIO_IRQ_STS 0x50
+#define REG_GPIO_BIT_EN 0x60
+#define REG_GPIO_DIN 0x70
+#define REG_GPIO_DOUT 0x80
+
+/* REG_GPIO_IRQ_PLRT */
+#define GPIO_POLARITY_LOW 0
+#define GPIO_POLARITY_HIGH 1
+
+/* REG_GPIO_IRQ_TYPE */
+#define GPIO_LEVEL_TRIGGERED 0
+#define GPIO_EDGE_TRIGGERED 1
+
+/**
+ * struct tz1090_gpio_bank - GPIO bank private data
+ * @chip: Generic GPIO chip for GPIO bank
+ * @domain: IRQ domain for GPIO bank (may be NULL)
+ * @reg: Base of registers, offset for this GPIO bank
+ * @irq: IRQ number for GPIO bank
+ * @label: Debug GPIO bank label, used for storage of chip->label
+ *
+ * This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
+ * and the callbacks for the gpio_chip can access the private data with the
+ * to_bank() macro below.
+ */
+struct tz1090_gpio_bank {
+ struct gpio_chip chip;
+ struct irq_domain *domain;
+ void __iomem *reg;
+ int irq;
+ char label[16];
+};
+#define to_bank(c) container_of(c, struct tz1090_gpio_bank, chip)
+
+/**
+ * struct tz1090_gpio - Overall GPIO device private data
+ * @dev: Device (from platform device)
+ * @reg: Base of GPIO registers
+ *
+ * Represents the overall GPIO device. This structure is actually only
+ * temporary, and used during init.
+ */
+struct tz1090_gpio {
+ struct device *dev;
+ void __iomem *reg;
+};
+
+/**
+ * struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
+ * @priv: Overall GPIO device private data
+ * @node: Device tree node specific to this GPIO bank
+ * @index: Index of bank in range 0-2
+ */
+struct tz1090_gpio_bank_info {
+ struct tz1090_gpio *priv;
+ struct device_node *node;
+ unsigned int index;
+};
+
+/* Convenience register accessors */
+static void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs, u32 data)
+{
+ iowrite32(data, bank->reg + reg_offs);
+}
+
+static u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs)
+{
+ return ioread32(bank->reg + reg_offs);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~(0x1 << offset);
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_clear_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value |= 0x1 << offset;
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_set_bit(bank, reg_offs, offset);
+ __global_unlock2(lstat);
+}
+
+/* caller must hold LOCK2 */
+static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ int val)
+{
+ u32 value;
+
+ value = tz1090_gpio_read(bank, reg_offs);
+ value &= ~(0x1 << offset);
+ value |= !!val << offset;
+ tz1090_gpio_write(bank, reg_offs, value);
+}
+
+static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset,
+ int val)
+{
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
+ __global_unlock2(lstat);
+}
+
+static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
+ unsigned int reg_offs,
+ unsigned int offset)
+{
+ return tz1090_gpio_read(bank, reg_offs) & (0x1 << offset);
+}
+
+/* GPIO chip callbacks */
+
+static int tz1090_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+
+ return 0;
+}
+
+static int tz1090_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int lstat;
+
+ __global_lock2(lstat);
+ _tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+ _tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
+ __global_unlock2(lstat);
+
+ return 0;
+}
+
+/*
+ * Return GPIO level
+ */
+static int tz1090_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ return tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
+}
+
+/*
+ * Set output GPIO level
+ */
+static void tz1090_gpio_set(struct gpio_chip *chip, unsigned offset,
+ int output_value)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
+}
+
+static int tz1090_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+ int ret;
+
+ ret = pinctrl_request_gpio(chip->base + offset);
+ if (ret)
+ return ret;
+
+ tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
+ tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
+
+ return 0;
+}
+
+static void tz1090_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ pinctrl_free_gpio(chip->base + offset);
+
+ tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
+}
+
+static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct tz1090_gpio_bank *bank = to_bank(chip);
+
+ if (!bank->domain)
+ return -EINVAL;
+
+ return irq_create_mapping(bank->domain, offset);
+}
+
+/* IRQ chip handlers */
+
+/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
+static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
+{
+ return (struct tz1090_gpio_bank *)data->domain->host_data;
+}
+
+static void tz1090_gpio_irq_clear(struct tz1090_gpio_bank *bank,
+ unsigned int offset)
+{
+ tz1090_gpio_clear_bit(bank, REG_GPIO_IRQ_STS, offset);
+}
+
+static void tz1090_gpio_irq_enable(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int enable)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_EN, offset, enable);
+}
+
+static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int polarity)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
+}
+
+static int tz1090_gpio_valid_handler(struct irq_desc *desc)
+{
+ return desc->handle_irq == handle_level_irq ||
+ desc->handle_irq == handle_edge_irq;
+}
+
+static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
+ unsigned int offset, unsigned int type)
+{
+ tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
+}
+
+/* set polarity to trigger on next edge, whether rising or falling */
+static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
+ unsigned int offset)
+{
+ unsigned int value_p, value_i;
+ int lstat;
+
+ __global_lock2(lstat);
+ /* irq_polarity[offset] = !input[offset] */
+ value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
+ value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
+ value_p &= ~(0x1 << offset);
+ value_p |= value_i & (0x1 << offset);
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
+ __global_unlock2(lstat);
+}
+
+static void gpio_ack_irq(struct irq_data *data)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+ tz1090_gpio_irq_clear(bank, data->hwirq);
+}
+
+static void gpio_mask_irq(struct irq_data *data)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+ tz1090_gpio_irq_enable(bank, data->hwirq, 0);
+}
+
+static void gpio_unmask_irq(struct irq_data *data)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+ tz1090_gpio_irq_enable(bank, data->hwirq, 1);
+}
+
+static unsigned int gpio_startup_irq(struct irq_data *data)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+ irq_hw_number_t hw = data->hwirq;
+ struct irq_desc *desc = irq_to_desc(data->irq);
+
+ /*
+ * This warning indicates that the type of the irq hasn't been set
+ * before enabling the irq. This would normally be done by passing some
+ * trigger flags to request_irq().
+ */
+ WARN(!tz1090_gpio_valid_handler(desc),
+ "irq type not set before enabling gpio irq %d", data->irq);
+
+ tz1090_gpio_irq_clear(bank, hw);
+ tz1090_gpio_irq_enable(bank, hw, 1);
+ return 0;
+}
+
+static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+ unsigned int type;
+ unsigned int polarity;
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ type = GPIO_EDGE_TRIGGERED;
+ polarity = GPIO_POLARITY_LOW;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ type = GPIO_EDGE_TRIGGERED;
+ polarity = GPIO_POLARITY_HIGH;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ type = GPIO_EDGE_TRIGGERED;
+ polarity = GPIO_POLARITY_LOW;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ type = GPIO_LEVEL_TRIGGERED;
+ polarity = GPIO_POLARITY_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ type = GPIO_LEVEL_TRIGGERED;
+ polarity = GPIO_POLARITY_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tz1090_gpio_irq_type(bank, data->hwirq, type);
+ if (type == GPIO_LEVEL_TRIGGERED)
+ __irq_set_handler_locked(data->irq, handle_level_irq);
+ else
+ __irq_set_handler_locked(data->irq, handle_edge_irq);
+
+ if (flow_type == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, data->hwirq);
+ else
+ tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
+
+ return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
+{
+ struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
+
+#ifdef CONFIG_PM_DEBUG
+ pr_info("irq_wake irq%d state:%d\n", data->irq, on);
+#endif
+
+ /* wake on gpio block interrupt */
+ return irq_set_irq_wake(bank->irq, on);
+}
+#else
+#define gpio_set_irq_wake NULL
+#endif
+
+/* gpio virtual interrupt functions */
+static struct irq_chip gpio_irq_chip = {
+ .irq_startup = gpio_startup_irq,
+ .irq_ack = gpio_ack_irq,
+ .irq_mask = gpio_mask_irq,
+ .irq_unmask = gpio_unmask_irq,
+ .irq_set_type = gpio_set_irq_type,
+ .irq_set_wake = gpio_set_irq_wake,
+ .flags = IRQCHIP_MASK_ON_SUSPEND,
+};
+
+static void tz1090_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ irq_hw_number_t hw;
+ unsigned int irq_stat, irq_no;
+ struct tz1090_gpio_bank *bank;
+ struct irq_desc *child_desc;
+
+ bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
+ irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
+ tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
+ 0x3FFFFFFF; /* 30 bits only */
+
+ for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
+ if (!(irq_stat & 1))
+ continue;
+
+ irq_no = irq_linear_revmap(bank->domain, hw);
+ child_desc = irq_to_desc(irq_no);
+
+ /* Toggle edge for pin with both edges triggering enabled */
+ if (irqd_get_trigger_type(&child_desc->irq_data)
+ == IRQ_TYPE_EDGE_BOTH)
+ tz1090_gpio_irq_next_edge(bank, hw);
+
+ BUG_ON(!tz1090_gpio_valid_handler(child_desc));
+ generic_handle_irq_desc(irq_no, child_desc);
+ }
+}
+
+static int tz1090_gpio_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip(irq, &gpio_irq_chip);
+ return 0;
+}
+
+static const struct irq_domain_ops tz1090_gpio_irq_domain_ops = {
+ .map = tz1090_gpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
+{
+ struct device_node *np = info->node;
+ struct device *dev = info->priv->dev;
+ struct tz1090_gpio_bank *bank;
+
+ bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
+ if (!bank) {
+ dev_err(dev, "unable to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ /* Offset the main registers to the first register in this bank */
+ bank->reg = info->priv->reg + info->index * 4;
+
+ /* Set up GPIO chip */
+ snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
+ info->index);
+ bank->chip.label = bank->label;
+ bank->chip.dev = dev;
+ bank->chip.direction_input = tz1090_gpio_direction_input;
+ bank->chip.direction_output = tz1090_gpio_direction_output;
+ bank->chip.get = tz1090_gpio_get;
+ bank->chip.set = tz1090_gpio_set;
+ bank->chip.free = tz1090_gpio_free;
+ bank->chip.request = tz1090_gpio_request;
+ bank->chip.to_irq = tz1090_gpio_to_irq;
+ bank->chip.of_node = np;
+
+ /* GPIO numbering from 0 */
+ bank->chip.base = info->index * 30;
+ bank->chip.ngpio = 30;
+
+ /* Add the GPIO bank */
+ gpiochip_add(&bank->chip);
+
+ /* Get the GPIO bank IRQ if provided */
+ bank->irq = irq_of_parse_and_map(np, 0);
+
+ /* The interrupt is optional (it may be used by another core on chip) */
+ if (bank->irq < 0) {
+ dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
+ info->index);
+ return 0;
+ }
+
+ dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
+ info->index);
+
+ /*
+ * Initialise all interrupts to disabled so we don't get
+ * spurious ones on a dirty boot and hit the BUG_ON in the
+ * handler.
+ */
+ tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
+
+ /* Add a virtual IRQ for each GPIO */
+ bank->domain = irq_domain_add_linear(np,
+ bank->chip.ngpio,
+ &tz1090_gpio_irq_domain_ops,
+ bank);
+
+ /* Setup chained handler for this GPIO bank */
+ irq_set_handler_data(bank->irq, bank);
+ irq_set_chained_handler(bank->irq, tz1090_gpio_irq_handler);
+
+ return 0;
+}
+
+static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
+{
+ struct device_node *np = priv->dev->of_node;
+ struct device_node *node;
+
+ for_each_available_child_of_node(np, node) {
+ struct tz1090_gpio_bank_info info;
+ const __be32 *addr;
+ int len, ret;
+
+ addr = of_get_property(node, "reg", &len);
+ if (!addr || (len < sizeof(int))) {
+ dev_err(priv->dev, "invalid reg on %s\n",
+ node->full_name);
+ continue;
+ }
+
+ info.index = be32_to_cpup(addr);
+ if (info.index >= 3) {
+ dev_err(priv->dev, "index %u in %s out of range\n",
+ info.index, node->full_name);
+ continue;
+ }
+ info.node = of_node_get(node);
+ info.priv = priv;
+
+ ret = tz1090_gpio_bank_probe(&info);
+ if (ret) {
+ dev_err(priv->dev, "failure registering %s\n",
+ node->full_name);
+ of_node_put(node);
+ continue;
+ }
+ }
+}
+
+static int tz1090_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res_regs;
+ struct tz1090_gpio priv;
+
+ if (!np) {
+ dev_err(&pdev->dev, "must be instantiated via devicetree\n");
+ return -ENOENT;
+ }
+
+ res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_regs) {
+ dev_err(&pdev->dev, "cannot find registers resource\n");
+ return -ENOENT;
+ }
+
+ priv.dev = &pdev->dev;
+
+ /* Ioremap the registers */
+ priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
+ res_regs->end - res_regs->start);
+ if (!priv.reg) {
+ dev_err(&pdev->dev, "unable to ioremap registers\n");
+ return -ENOMEM;
+ }
+
+ /* Look for banks */
+ tz1090_gpio_register_banks(&priv);
+
+ return 0;
+}
+
+static struct of_device_id tz1090_gpio_of_match[] = {
+ { .compatible = "img,tz1090-gpio" },
+ { },
+};
+
+static struct platform_driver tz1090_gpio_driver = {
+ .driver = {
+ .name = "tz1090-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = tz1090_gpio_of_match,
+ },
+ .probe = tz1090_gpio_probe,
+};
+
+static int __init tz1090_gpio_init(void)
+{
+ return platform_driver_register(&tz1090_gpio_driver);
+}
+postcore_initcall(tz1090_gpio_init);
--
1.8.1.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists