aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <lg@denx.de>2009-03-12 07:46:41 -0400
committerSascha Hauer <s.hauer@pengutronix.de>2009-05-07 10:16:06 -0400
commit910862ec092c156023b8a6e726e8c793da0a03f7 (patch)
treee7977eb5532ae53e2869bf39d877cafc20f5fd53
parentfde364742a1afec9e71e705e863d029295282fe5 (diff)
mxc: emulate GPIO interrupt on both-edges
MXC GPIO controller does not support generation of interrupts on both edges. Emulate this mode in software by reconfiguring the irq trigger polarity on each interrupt. This follows an example of drivers/mfd/asic3.c. Signed-off-by: Guennadi Liakhovetski <lg@denx.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--arch/arm/plat-mxc/gpio.c49
-rw-r--r--arch/arm/plat-mxc/include/mach/gpio.h1
2 files changed, 49 insertions, 1 deletions
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c
index 89e95798cc3b..7506d963be4b 100644
--- a/arch/arm/plat-mxc/gpio.c
+++ b/arch/arm/plat-mxc/gpio.c
@@ -64,6 +64,8 @@ static void gpio_unmask_irq(u32 irq)
64 _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1); 64 _set_gpio_irqenable(&mxc_gpio_ports[gpio / 32], gpio & 0x1f, 1);
65} 65}
66 66
67static int mxc_gpio_get(struct gpio_chip *chip, unsigned offset);
68
67static int gpio_set_irq_type(u32 irq, u32 type) 69static int gpio_set_irq_type(u32 irq, u32 type)
68{ 70{
69 u32 gpio = irq_to_gpio(irq); 71 u32 gpio = irq_to_gpio(irq);
@@ -72,6 +74,7 @@ static int gpio_set_irq_type(u32 irq, u32 type)
72 int edge; 74 int edge;
73 void __iomem *reg = port->base; 75 void __iomem *reg = port->base;
74 76
77 port->both_edges &= ~(1 << (gpio & 31));
75 switch (type) { 78 switch (type) {
76 case IRQ_TYPE_EDGE_RISING: 79 case IRQ_TYPE_EDGE_RISING:
77 edge = GPIO_INT_RISE_EDGE; 80 edge = GPIO_INT_RISE_EDGE;
@@ -79,13 +82,24 @@ static int gpio_set_irq_type(u32 irq, u32 type)
79 case IRQ_TYPE_EDGE_FALLING: 82 case IRQ_TYPE_EDGE_FALLING:
80 edge = GPIO_INT_FALL_EDGE; 83 edge = GPIO_INT_FALL_EDGE;
81 break; 84 break;
85 case IRQ_TYPE_EDGE_BOTH:
86 val = mxc_gpio_get(&port->chip, gpio & 31);
87 if (val) {
88 edge = GPIO_INT_LOW_LEV;
89 pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
90 } else {
91 edge = GPIO_INT_HIGH_LEV;
92 pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
93 }
94 port->both_edges |= 1 << (gpio & 31);
95 break;
82 case IRQ_TYPE_LEVEL_LOW: 96 case IRQ_TYPE_LEVEL_LOW:
83 edge = GPIO_INT_LOW_LEV; 97 edge = GPIO_INT_LOW_LEV;
84 break; 98 break;
85 case IRQ_TYPE_LEVEL_HIGH: 99 case IRQ_TYPE_LEVEL_HIGH:
86 edge = GPIO_INT_HIGH_LEV; 100 edge = GPIO_INT_HIGH_LEV;
87 break; 101 break;
88 default: /* this includes IRQ_TYPE_EDGE_BOTH */ 102 default:
89 return -EINVAL; 103 return -EINVAL;
90 } 104 }
91 105
@@ -98,6 +112,34 @@ static int gpio_set_irq_type(u32 irq, u32 type)
98 return 0; 112 return 0;
99} 113}
100 114
115static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
116{
117 void __iomem *reg = port->base;
118 u32 bit, val;
119 int edge;
120
121 reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
122 bit = gpio & 0xf;
123 val = __raw_readl(reg);
124 edge = (val >> (bit << 1)) & 3;
125 val &= ~(0x3 << (bit << 1));
126 switch (edge) {
127 case GPIO_INT_HIGH_LEV:
128 edge = GPIO_INT_LOW_LEV;
129 pr_debug("mxc: switch GPIO %d to low trigger\n", gpio);
130 break;
131 case GPIO_INT_LOW_LEV:
132 edge = GPIO_INT_HIGH_LEV;
133 pr_debug("mxc: switch GPIO %d to high trigger\n", gpio);
134 break;
135 default:
136 pr_err("mxc: invalid configuration for GPIO %d: %x\n",
137 gpio, edge);
138 return;
139 }
140 __raw_writel(val | (edge << (bit << 1)), reg);
141}
142
101/* handle n interrupts in one status register */ 143/* handle n interrupts in one status register */
102static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) 144static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
103{ 145{
@@ -105,11 +147,16 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
105 147
106 gpio_irq_no = port->virtual_irq_start; 148 gpio_irq_no = port->virtual_irq_start;
107 for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) { 149 for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) {
150 u32 gpio = irq_to_gpio(gpio_irq_no);
108 151
109 if ((irq_stat & 1) == 0) 152 if ((irq_stat & 1) == 0)
110 continue; 153 continue;
111 154
112 BUG_ON(!(irq_desc[gpio_irq_no].handle_irq)); 155 BUG_ON(!(irq_desc[gpio_irq_no].handle_irq));
156
157 if (port->both_edges & (1 << (gpio & 31)))
158 mxc_flip_edge(port, gpio);
159
113 irq_desc[gpio_irq_no].handle_irq(gpio_irq_no, 160 irq_desc[gpio_irq_no].handle_irq(gpio_irq_no,
114 &irq_desc[gpio_irq_no]); 161 &irq_desc[gpio_irq_no]);
115 } 162 }
diff --git a/arch/arm/plat-mxc/include/mach/gpio.h b/arch/arm/plat-mxc/include/mach/gpio.h
index ea509f1090fb..894d2f87c856 100644
--- a/arch/arm/plat-mxc/include/mach/gpio.h
+++ b/arch/arm/plat-mxc/include/mach/gpio.h
@@ -35,6 +35,7 @@ struct mxc_gpio_port {
35 int irq; 35 int irq;
36 int virtual_irq_start; 36 int virtual_irq_start;
37 struct gpio_chip chip; 37 struct gpio_chip chip;
38 u32 both_edges;
38}; 39};
39 40
40int mxc_gpio_init(struct mxc_gpio_port*, int); 41int mxc_gpio_init(struct mxc_gpio_port*, int);