aboutsummaryrefslogtreecommitdiffstats
path: root/arch/avr32
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-01-23 23:21:36 -0500
committerHaavard Skinnemoen <hskinnemoen@atmel.com>2007-02-09 09:01:57 -0500
commit58febc0b1374de7506277d3aa9e9cddaea62ba65 (patch)
treead9774f80d34d33e8cf125eeb92b4ca66d1c40eb /arch/avr32
parenta3d912c8fa709c4078ceaabf4d71001190e19325 (diff)
[AVR32] ext int fixes
Bugfixes for external irq handler set_irq_type(): - If set_irq_type() can't set the type, don't change anything! - It's not OK to change the flow handler as part of set_irq_type(), among other issues that violates spinlock rules. Instead, we can call the relevant handler when we demux the external interrupts. - The external irq demux has no need to grab the spinlock. And in fact grabbing it that way was wrong, since that code might be pre-empted by an irq at a different priority level, and that code might then have tried to grab that spinlock... Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32')
-rw-r--r--arch/avr32/mach-at32ap/extint.c36
1 files changed, 17 insertions, 19 deletions
diff --git a/arch/avr32/mach-at32ap/extint.c b/arch/avr32/mach-at32ap/extint.c
index b59272e81b9a..4a60eccfebd2 100644
--- a/arch/avr32/mach-at32ap/extint.c
+++ b/arch/avr32/mach-at32ap/extint.c
@@ -55,20 +55,11 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
55 unsigned long flags; 55 unsigned long flags;
56 int ret = 0; 56 int ret = 0;
57 57
58 flow_type &= IRQ_TYPE_SENSE_MASK;
58 if (flow_type == IRQ_TYPE_NONE) 59 if (flow_type == IRQ_TYPE_NONE)
59 flow_type = IRQ_TYPE_LEVEL_LOW; 60 flow_type = IRQ_TYPE_LEVEL_LOW;
60 61
61 desc = &irq_desc[irq]; 62 desc = &irq_desc[irq];
62 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
63 desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
64
65 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
66 desc->status |= IRQ_LEVEL;
67 set_irq_handler(irq, handle_level_irq);
68 } else {
69 set_irq_handler(irq, handle_edge_irq);
70 }
71
72 spin_lock_irqsave(&sm->lock, flags); 63 spin_lock_irqsave(&sm->lock, flags);
73 64
74 mode = sm_readl(sm, EIM_MODE); 65 mode = sm_readl(sm, EIM_MODE);
@@ -97,9 +88,16 @@ static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
97 break; 88 break;
98 } 89 }
99 90
100 sm_writel(sm, EIM_MODE, mode); 91 if (ret == 0) {
101 sm_writel(sm, EIM_EDGE, edge); 92 sm_writel(sm, EIM_MODE, mode);
102 sm_writel(sm, EIM_LEVEL, level); 93 sm_writel(sm, EIM_EDGE, edge);
94 sm_writel(sm, EIM_LEVEL, level);
95
96 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
97 flow_type |= IRQ_LEVEL;
98 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
99 desc->status |= flow_type;
100 }
103 101
104 spin_unlock_irqrestore(&sm->lock, flags); 102 spin_unlock_irqrestore(&sm->lock, flags);
105 103
@@ -122,8 +120,6 @@ static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
122 unsigned long status, pending; 120 unsigned long status, pending;
123 unsigned int i, ext_irq; 121 unsigned int i, ext_irq;
124 122
125 spin_lock(&sm->lock);
126
127 status = sm_readl(sm, EIM_ISR); 123 status = sm_readl(sm, EIM_ISR);
128 pending = status & sm_readl(sm, EIM_IMR); 124 pending = status & sm_readl(sm, EIM_IMR);
129 125
@@ -133,10 +129,11 @@ static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
133 129
134 ext_irq = i + sm->eim_first_irq; 130 ext_irq = i + sm->eim_first_irq;
135 ext_desc = irq_desc + ext_irq; 131 ext_desc = irq_desc + ext_irq;
136 ext_desc->handle_irq(ext_irq, ext_desc); 132 if (ext_desc->status & IRQ_LEVEL)
133 handle_level_irq(ext_irq, ext_desc);
134 else
135 handle_edge_irq(ext_irq, ext_desc);
137 } 136 }
138
139 spin_unlock(&sm->lock);
140} 137}
141 138
142static int __init eim_init(void) 139static int __init eim_init(void)
@@ -168,8 +165,9 @@ static int __init eim_init(void)
168 sm->eim_chip = &eim_chip; 165 sm->eim_chip = &eim_chip;
169 166
170 for (i = 0; i < nr_irqs; i++) { 167 for (i = 0; i < nr_irqs; i++) {
168 /* NOTE the handler we set here is ignored by the demux */
171 set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip, 169 set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip,
172 handle_edge_irq); 170 handle_level_irq);
173 set_irq_chip_data(sm->eim_first_irq + i, sm); 171 set_irq_chip_data(sm->eim_first_irq + i, sm);
174 } 172 }
175 173