Skip to contentSkip to conversation

gpio/mxs: support irqs triggered by both edges

PATCH New Enrico Scholz 2 participants 5 messages Feb 20, 2012 – Mar 12, 2012

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 gpio/mxs: support irqs triggered by both edges

iMX28 silicon does not support irqs triggered by both gpio edges. Patch emulates this behavior by configuring such irqs as level triggered and by switching polarity in the interrupt handler.

---
 drivers/gpio/gpio-mxs.c |   31 +++++++++++++++++++++++++++++--
 1 files changed, 29 insertions(+), 2 deletions(-)
drivers/gpio/gpio-mxs.c +29 -2
index 385c58e..ab973be 100644
@@ -57,6 +57,7 @@ struct mxs_gpio_port {
 	int id;
 	int irq;
 	int virtual_irq_start;
+	unsigned long both_edge;
 	struct bgpio_chip bgc;
 };
 
@@ -68,9 +69,12 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
 	u32 pin_mask = 1 << (gpio & 31);
 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 	struct mxs_gpio_port *port = gc->private;
+	int lvl;
 	void __iomem *pin_addr;
 	int edge;
 
+	clear_bit(gpio & 31, &port->both_edge);
+
 	switch (type) {
 	case IRQ_TYPE_EDGE_RISING:
 		edge = GPIO_INT_RISE_EDGE;
@@ -84,6 +88,14 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
 	case IRQ_TYPE_LEVEL_HIGH:
 		edge = GPIO_INT_HIGH_LEV;
 		break;
+	case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
+		/* mx28 does not support triggering irq by both edges; emulate
+		 * behavior by configuring irq as level triggered and update
+		 * irq polarity in the irq handler */
+		set_bit(gpio & 31, &port->both_edge);
+		lvl  = readl(port->base + PINCTRL_DIN(port->id));
+		edge = (lvl & pin_mask) ? GPIO_INT_LOW_LEV : GPIO_INT_HIGH_LEV;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -114,11 +126,26 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
 	u32 irq_stat;
 	struct mxs_gpio_port *port = irq_get_handler_data(irq);
 	u32 gpio_irq_no_base = port->virtual_irq_start;
+	void __iomem *base = port->base;
+	u32 pol_msk;
 
 	desc->irq_data.chip->irq_ack(&desc->irq_data);
 
-	irq_stat = readl(port->base + PINCTRL_IRQSTAT(port->id)) &
-			readl(port->base + PINCTRL_IRQEN(port->id));
+	irq_stat = readl(base + PINCTRL_IRQSTAT(port->id)) &
+			readl(base + PINCTRL_IRQEN(port->id));
+
+	/* handle irqs triggered by both edges */
+	pol_msk = irq_stat & port->both_edge;
+	if (pol_msk) {
+		void __iomem *reg = base + PINCTRL_IRQPOL(port->id);
+		u32 port_lvl = readl(base + PINCTRL_DIN(port->id));
+
+		/* \todo write into the TOGGLE register (without reading port
+		 * level register); this requires a special unmask operation
+		 * which updates the IRQPOL register */
+		writel(pol_msk & port_lvl, reg + MXS_CLR);
+		writel(pol_msk & ~port_lvl, reg + MXS_SET);
+	}
 
 	while (irq_stat != 0) {
 		int irqoffset = fls(irq_stat) - 1;
Signature · 1 line
1.7.7.6
#1 lore raw

PATCH 1/1 gpio/mxs: support irqs triggered by both edges

Quoted 5 lines · Enrico Scholz

iMX28 silicon does not support irqs triggered by both gpio edges. Patch emulates this behavior by configuring such irqs as level triggered and by switching polarity in the interrupt handler.

That looks dodgy and hacky. This will definitely miss interrupts if they come in too fast. I don't like this approach. What do you need it for?

g.

Quoted 81 lines
---
 drivers/gpio/gpio-mxs.c |   31 +++++++++++++++++++++++++++++--
 1 files changed, 29 insertions(+), 2 deletions(-)
drivers/gpio/gpio-mxs.c +29 -2
index 385c58e..ab973be 100644
@@ -57,6 +57,7 @@ struct mxs_gpio_port {
 	int id;
 	int irq;
 	int virtual_irq_start;
+	unsigned long both_edge;
 	struct bgpio_chip bgc;
 };
 
@@ -68,9 +69,12 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
 	u32 pin_mask = 1 << (gpio & 31);
 	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 	struct mxs_gpio_port *port = gc->private;
+	int lvl;
 	void __iomem *pin_addr;
 	int edge;
 
+	clear_bit(gpio & 31, &port->both_edge);
+
 	switch (type) {
 	case IRQ_TYPE_EDGE_RISING:
 		edge = GPIO_INT_RISE_EDGE;
@@ -84,6 +88,14 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
 	case IRQ_TYPE_LEVEL_HIGH:
 		edge = GPIO_INT_HIGH_LEV;
 		break;
+	case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING:
+		/* mx28 does not support triggering irq by both edges; emulate
+		 * behavior by configuring irq as level triggered and update
+		 * irq polarity in the irq handler */
+		set_bit(gpio & 31, &port->both_edge);
+		lvl  = readl(port->base + PINCTRL_DIN(port->id));
+		edge = (lvl & pin_mask) ? GPIO_INT_LOW_LEV : GPIO_INT_HIGH_LEV;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -114,11 +126,26 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
 	u32 irq_stat;
 	struct mxs_gpio_port *port = irq_get_handler_data(irq);
 	u32 gpio_irq_no_base = port->virtual_irq_start;
+	void __iomem *base = port->base;
+	u32 pol_msk;
 
 	desc->irq_data.chip->irq_ack(&desc->irq_data);
 
-	irq_stat = readl(port->base + PINCTRL_IRQSTAT(port->id)) &
-			readl(port->base + PINCTRL_IRQEN(port->id));
+	irq_stat = readl(base + PINCTRL_IRQSTAT(port->id)) &
+			readl(base + PINCTRL_IRQEN(port->id));
+
+	/* handle irqs triggered by both edges */
+	pol_msk = irq_stat & port->both_edge;
+	if (pol_msk) {
+		void __iomem *reg = base + PINCTRL_IRQPOL(port->id);
+		u32 port_lvl = readl(base + PINCTRL_DIN(port->id));
+
+		/* \todo write into the TOGGLE register (without reading port
+		 * level register); this requires a special unmask operation
+		 * which updates the IRQPOL register */
+		writel(pol_msk & port_lvl, reg + MXS_CLR);
+		writel(pol_msk & ~port_lvl, reg + MXS_SET);
+	}
 
 	while (irq_stat != 0) {
 		int irqoffset = fls(irq_stat) - 1;
Signature · 7 lines
1.7.7.6


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Signature · 2 lines
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies,Ltd.
#2 lore raw

PATCH 1/1 gpio/mxs: support irqs triggered by both edges

Quoted 6 lines · Grant Likely
Quoted 3 lines

iMX28 silicon does not support irqs triggered by both gpio edges. Patch emulates this behavior by configuring such irqs as level triggered and by switching polarity in the interrupt handler.

That looks dodgy and hacky. This will definitely miss interrupts if they come in too fast.

Because patch calculates the trigger from the actual level, there should not be missed more interrupts than with native edge-both support.

Quoted 1 line

I don't like this approach. What do you need it for?

some drivers (e.g. gpio-keys, otg gpio-vbus, gpio-charger) are requesting gpio based interrupts with IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING flags. It is not possible to use such drivers without having EDGE_BOTH support.

Enrico

#3 lore raw

PATCH 1/1 gpio/mxs: support irqs triggered by both edges

Quoted 11 lines · Enrico Scholz
Quoted 6 lines · Grant Likely
Quoted 3 lines

iMX28 silicon does not support irqs triggered by both gpio edges. Patch emulates this behavior by configuring such irqs as level triggered and by switching polarity in the interrupt handler.

That looks dodgy and hacky. ?This will definitely miss interrupts if they come in too fast.

Because patch calculates the trigger from the actual level, there should not be missed more interrupts than with native edge-both support.

Edge interrupt controllers latch interrupt requests. so that nothing gets missed. Level interrupt controllers do not. So, if the irq line toggles 3 times before the handling code gets executed, then the kernel will still only recognize 1 irq, not 3.

Quoted 9 lines
Quoted 1 line

I don't like this approach. ?What do you need it for?

some drivers (e.g. gpio-keys, otg gpio-vbus, gpio-charger) are requesting gpio based interrupts with IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING flags. ?It is not possible to use such drivers without having EDGE_BOTH support.

Enrico

Signature · 2 lines
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
#4 lore raw

PATCH 1/1 gpio/mxs: support irqs triggered by both edges

Quoted 4 lines · Grant Likely
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

iMX28 silicon does not support irqs triggered by both gpio edges. Patch emulates this behavior by configuring such irqs as level triggered and by switching polarity in the interrupt handler.

Quoted 8 lines
Quoted 5 lines
Quoted 2 lines

That looks dodgy and hacky. ?This will definitely miss interrupts if they come in too fast.

Because patch calculates the trigger from the actual level, there should not be missed more interrupts than with native edge-both support.

Edge interrupt controllers latch interrupt requests. so that nothing gets missed. Level interrupt controllers do not.

iMX28 hardware sets interrupt status only but never clears it[1]. E.g. when the requested level has been seen, the interrupt is pending until software clears it. It does not matter whether level changes back or stays asserted.

Quoted 2 lines

So, if the irq line toggles 3 times before the handling code gets executed, then the kernel will still only recognize 1 irq, not 3.

There is no difference to edge triggered interrupts. When the trigger events happen too fast, not all of them can be handled. But both for edge and for level triggered interrupts, the interrupt handler will be executed at least once.

Enrico

Footnotes:
[1] see figure "Figure 9-5. GPIO Interrupt Generation", page 688 in "i.MX28 Applications Processor Reference Manual, Rev. 1, 2010"