From: heiko@sntech.de (Heiko Stübner)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 6/6] pinctrl: rockchip: emulate both edge triggered interrupts
Date: Tue, 15 Oct 2013 12:49:56 +0200 [thread overview]
Message-ID: <201310151249.56469.heiko@sntech.de> (raw)
In-Reply-To: <201310151246.49402.heiko@sntech.de>
The gpio interrupt controller on Rockchip socs can do edge triggers only
for single edges but not both. Nevertheless a lot of gpio users rely on
the availability of both-edge triggered interrupts - i.e. gpio-keys.
Therefore implement a solution similar to pinctrl-coh901 re-setting the
triggering edge depending on the gpio value in the interrupt demuxer.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/pinctrl/pinctrl-rockchip.c | 61 +++++++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 7 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 4db286c..e8988a1 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -101,7 +101,7 @@ struct rockchip_pin_bank {
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range grange;
spinlock_t slock;
-
+ u32 toggle_edge_mode;
};
#define PIN_BANK(id, pins, label) \
@@ -1076,7 +1076,9 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
{
struct irq_chip *chip = irq_get_chip(irq);
struct rockchip_pin_bank *bank = irq_get_handler_data(irq);
+ u32 polarity = 0, data = 0;
u32 pend;
+ bool edge_changed = false;
dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
@@ -1084,6 +1086,12 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
+ if (bank->toggle_edge_mode) {
+ polarity = readl_relaxed(bank->reg_base +
+ GPIO_INT_POLARITY);
+ data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
+ }
+
while (pend) {
unsigned int virq;
@@ -1098,9 +1106,30 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
+ /*
+ * Triggering IRQ on both rising and falling edge
+ * needs manual intervention.
+ */
+ if (bank->toggle_edge_mode & BIT(irq)) {
+ if (data & BIT(irq))
+ polarity &= ~BIT(irq);
+ else
+ polarity |= BIT(irq);
+
+ edge_changed = true;
+ }
+
generic_handle_irq(virq);
}
+ if (bank->toggle_edge_mode && edge_changed) {
+ /* Interrupt params should only be set with ints disabled */
+ data = readl_relaxed(bank->reg_base + GPIO_INTEN);
+ writel_relaxed(0, bank->reg_base + GPIO_INTEN);
+ writel(polarity, bank->reg_base + GPIO_INT_POLARITY);
+ writel(data, bank->reg_base + GPIO_INTEN);
+ }
+
chained_irq_exit(chip, desc);
}
@@ -1113,6 +1142,12 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
u32 level;
u32 data;
+ /* make sure the pin is configured as gpio input */
+ rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ data &= ~mask;
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
if (type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
else
@@ -1124,19 +1159,37 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ bank->toggle_edge_mode |= mask;
+ level |= mask;
+
+ /*
+ * Determine gpio state. If 1 next interrupt should be falling
+ * otherwise rising.
+ */
+ data = readl(bank->reg_base + GPIO_EXT_PORT);
+ if (data & mask)
+ polarity &= ~mask;
+ else
+ polarity |= mask;
+ break;
case IRQ_TYPE_EDGE_RISING:
+ bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity |= mask;
break;
case IRQ_TYPE_EDGE_FALLING:
+ bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity &= ~mask;
break;
case IRQ_TYPE_LEVEL_HIGH:
+ bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity |= mask;
break;
case IRQ_TYPE_LEVEL_LOW:
+ bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity &= ~mask;
break;
@@ -1150,12 +1203,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
irq_gc_unlock(gc);
- /* make sure the pin is configured as gpio input */
- rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
- data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
- data &= ~mask;
- writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
return 0;
}
--
1.7.10.4
WARNING: multiple messages have this Message-ID (diff)
From: "Heiko Stübner" <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
To: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Grant Likely
<grant.likely-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
Rob Herring <robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [PATCH 6/6] pinctrl: rockchip: emulate both edge triggered interrupts
Date: Tue, 15 Oct 2013 12:49:56 +0200 [thread overview]
Message-ID: <201310151249.56469.heiko@sntech.de> (raw)
In-Reply-To: <201310151246.49402.heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
The gpio interrupt controller on Rockchip socs can do edge triggers only
for single edges but not both. Nevertheless a lot of gpio users rely on
the availability of both-edge triggered interrupts - i.e. gpio-keys.
Therefore implement a solution similar to pinctrl-coh901 re-setting the
triggering edge depending on the gpio value in the interrupt demuxer.
Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
drivers/pinctrl/pinctrl-rockchip.c | 61 +++++++++++++++++++++++++++++++-----
1 file changed, 54 insertions(+), 7 deletions(-)
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 4db286c..e8988a1 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -101,7 +101,7 @@ struct rockchip_pin_bank {
struct gpio_chip gpio_chip;
struct pinctrl_gpio_range grange;
spinlock_t slock;
-
+ u32 toggle_edge_mode;
};
#define PIN_BANK(id, pins, label) \
@@ -1076,7 +1076,9 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
{
struct irq_chip *chip = irq_get_chip(irq);
struct rockchip_pin_bank *bank = irq_get_handler_data(irq);
+ u32 polarity = 0, data = 0;
u32 pend;
+ bool edge_changed = false;
dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
@@ -1084,6 +1086,12 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
+ if (bank->toggle_edge_mode) {
+ polarity = readl_relaxed(bank->reg_base +
+ GPIO_INT_POLARITY);
+ data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
+ }
+
while (pend) {
unsigned int virq;
@@ -1098,9 +1106,30 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc)
dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
+ /*
+ * Triggering IRQ on both rising and falling edge
+ * needs manual intervention.
+ */
+ if (bank->toggle_edge_mode & BIT(irq)) {
+ if (data & BIT(irq))
+ polarity &= ~BIT(irq);
+ else
+ polarity |= BIT(irq);
+
+ edge_changed = true;
+ }
+
generic_handle_irq(virq);
}
+ if (bank->toggle_edge_mode && edge_changed) {
+ /* Interrupt params should only be set with ints disabled */
+ data = readl_relaxed(bank->reg_base + GPIO_INTEN);
+ writel_relaxed(0, bank->reg_base + GPIO_INTEN);
+ writel(polarity, bank->reg_base + GPIO_INT_POLARITY);
+ writel(data, bank->reg_base + GPIO_INTEN);
+ }
+
chained_irq_exit(chip, desc);
}
@@ -1113,6 +1142,12 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
u32 level;
u32 data;
+ /* make sure the pin is configured as gpio input */
+ rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
+ data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
+ data &= ~mask;
+ writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
+
if (type & IRQ_TYPE_EDGE_BOTH)
__irq_set_handler_locked(d->irq, handle_edge_irq);
else
@@ -1124,19 +1159,37 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ bank->toggle_edge_mode |= mask;
+ level |= mask;
+
+ /*
+ * Determine gpio state. If 1 next interrupt should be falling
+ * otherwise rising.
+ */
+ data = readl(bank->reg_base + GPIO_EXT_PORT);
+ if (data & mask)
+ polarity &= ~mask;
+ else
+ polarity |= mask;
+ break;
case IRQ_TYPE_EDGE_RISING:
+ bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity |= mask;
break;
case IRQ_TYPE_EDGE_FALLING:
+ bank->toggle_edge_mode &= ~mask;
level |= mask;
polarity &= ~mask;
break;
case IRQ_TYPE_LEVEL_HIGH:
+ bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity |= mask;
break;
case IRQ_TYPE_LEVEL_LOW:
+ bank->toggle_edge_mode &= ~mask;
level &= ~mask;
polarity &= ~mask;
break;
@@ -1150,12 +1203,6 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
irq_gc_unlock(gc);
- /* make sure the pin is configured as gpio input */
- rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
- data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
- data &= ~mask;
- writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
return 0;
}
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2013-10-15 10:49 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-10-15 10:46 [PATCH 0/6] pinctrl: rockchip: add rk3188 support Heiko Stübner
2013-10-15 10:46 ` Heiko Stübner
2013-10-15 10:47 ` [PATCH 1/6] pinctrl: rockchip: separate different sub-types more Heiko Stübner
2013-10-15 10:47 ` Heiko Stübner
2013-10-15 10:47 ` [PATCH 2/6] of: add function to check against a list of compatible strings Heiko Stübner
2013-10-15 10:47 ` Heiko Stübner
2013-10-15 11:11 ` Mark Rutland
2013-10-15 11:11 ` Mark Rutland
2013-10-15 12:44 ` Heiko Stübner
2013-10-15 12:44 ` Heiko Stübner
2013-10-15 10:48 ` [PATCH 3/6] pinctrl: rockchip: add support for multiple bank types Heiko Stübner
2013-10-15 10:48 ` Heiko Stübner
2013-10-15 10:48 ` [PATCH 4/6] pinctrl: rockchip: remove redundant check Heiko Stübner
2013-10-15 10:48 ` Heiko Stübner
2013-10-15 10:49 ` [PATCH 5/6] pinctrl: rockchip: add rk3188 specifics Heiko Stübner
2013-10-15 10:49 ` Heiko Stübner
2013-10-15 10:49 ` Heiko Stübner [this message]
2013-10-15 10:49 ` [PATCH 6/6] pinctrl: rockchip: emulate both edge triggered interrupts Heiko Stübner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201310151249.56469.heiko@sntech.de \
--to=heiko@sntech.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.