[PATCH] avr32 architecture
[linux-2.6.git] / arch / avr32 / mach-at32ap / sm.c
1 /*
2  * System Manager driver for AT32AP CPUs
3  *
4  * Copyright (C) 2006 Atmel Corporation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/errno.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/kernel.h>
15 #include <linux/platform_device.h>
16 #include <linux/random.h>
17 #include <linux/spinlock.h>
18
19 #include <asm/intc.h>
20 #include <asm/io.h>
21 #include <asm/irq.h>
22
23 #include <asm/arch/sm.h>
24
25 #include "sm.h"
26
27 #define SM_EIM_IRQ_RESOURCE     1
28 #define SM_PM_IRQ_RESOURCE      2
29 #define SM_RTC_IRQ_RESOURCE     3
30
31 #define to_eim(irqc) container_of(irqc, struct at32_sm, irqc)
32
33 struct at32_sm system_manager;
34
35 int __init at32_sm_init(void)
36 {
37         struct resource *regs;
38         struct at32_sm *sm = &system_manager;
39         int ret = -ENXIO;
40
41         regs = platform_get_resource(&at32_sm_device, IORESOURCE_MEM, 0);
42         if (!regs)
43                 goto fail;
44
45         spin_lock_init(&sm->lock);
46         sm->pdev = &at32_sm_device;
47
48         ret = -ENOMEM;
49         sm->regs = ioremap(regs->start, regs->end - regs->start + 1);
50         if (!sm->regs)
51                 goto fail;
52
53         return 0;
54
55 fail:
56         printk(KERN_ERR "Failed to initialize System Manager: %d\n", ret);
57         return ret;
58 }
59
60 /*
61  * External Interrupt Module (EIM).
62  *
63  * EIM gets level- or edge-triggered interrupts of either polarity
64  * from the outside and converts it to active-high level-triggered
65  * interrupts that the internal interrupt controller can handle. EIM
66  * also provides masking/unmasking of interrupts, as well as
67  * acknowledging of edge-triggered interrupts.
68  */
69
70 static irqreturn_t spurious_eim_interrupt(int irq, void *dev_id,
71                                           struct pt_regs *regs)
72 {
73         printk(KERN_WARNING "Spurious EIM interrupt %d\n", irq);
74         disable_irq(irq);
75         return IRQ_NONE;
76 }
77
78 static struct irqaction eim_spurious_action = {
79         .handler = spurious_eim_interrupt,
80 };
81
82 static irqreturn_t eim_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
83 {
84         struct irq_controller * irqc = dev_id;
85         struct at32_sm *sm = to_eim(irqc);
86         unsigned long pending;
87
88         /*
89          * No need to disable interrupts globally.  The interrupt
90          * level relevant to this group must be masked all the time,
91          * so we know that this particular EIM instance will not be
92          * re-entered.
93          */
94         spin_lock(&sm->lock);
95
96         pending = intc_get_pending(sm->irqc.irq_group);
97         if (unlikely(!pending)) {
98                 printk(KERN_ERR "EIM (group %u): No interrupts pending!\n",
99                        sm->irqc.irq_group);
100                 goto unlock;
101         }
102
103         do {
104                 struct irqaction *action;
105                 unsigned int i;
106
107                 i = fls(pending) - 1;
108                 pending &= ~(1 << i);
109                 action = sm->action[i];
110
111                 /* Acknowledge the interrupt */
112                 sm_writel(sm, EIM_ICR, 1 << i);
113
114                 spin_unlock(&sm->lock);
115
116                 if (action->flags & SA_INTERRUPT)
117                         local_irq_disable();
118                 action->handler(sm->irqc.first_irq + i, action->dev_id, regs);
119                 local_irq_enable();
120                 spin_lock(&sm->lock);
121                 if (action->flags & SA_SAMPLE_RANDOM)
122                         add_interrupt_randomness(sm->irqc.first_irq + i);
123         } while (pending);
124
125 unlock:
126         spin_unlock(&sm->lock);
127         return IRQ_HANDLED;
128 }
129
130 static void eim_mask(struct irq_controller *irqc, unsigned int irq)
131 {
132         struct at32_sm *sm = to_eim(irqc);
133         unsigned int i;
134
135         i = irq - sm->irqc.first_irq;
136         sm_writel(sm, EIM_IDR, 1 << i);
137 }
138
139 static void eim_unmask(struct irq_controller *irqc, unsigned int irq)
140 {
141         struct at32_sm *sm = to_eim(irqc);
142         unsigned int i;
143
144         i = irq - sm->irqc.first_irq;
145         sm_writel(sm, EIM_IER, 1 << i);
146 }
147
148 static int eim_setup(struct irq_controller *irqc, unsigned int irq,
149                 struct irqaction *action)
150 {
151         struct at32_sm *sm = to_eim(irqc);
152         sm->action[irq - sm->irqc.first_irq] = action;
153         /* Acknowledge earlier interrupts */
154         sm_writel(sm, EIM_ICR, (1<<(irq - sm->irqc.first_irq)));
155         eim_unmask(irqc, irq);
156         return 0;
157 }
158
159 static void eim_free(struct irq_controller *irqc, unsigned int irq,
160                 void *dev)
161 {
162         struct at32_sm *sm = to_eim(irqc);
163         eim_mask(irqc, irq);
164         sm->action[irq - sm->irqc.first_irq] = &eim_spurious_action;
165 }
166
167 static int eim_set_type(struct irq_controller *irqc, unsigned int irq,
168                         unsigned int type)
169 {
170         struct at32_sm *sm = to_eim(irqc);
171         unsigned long flags;
172         u32 value, pattern;
173
174         spin_lock_irqsave(&sm->lock, flags);
175
176         pattern = 1 << (irq - sm->irqc.first_irq);
177
178         value = sm_readl(sm, EIM_MODE);
179         if (type & IRQ_TYPE_LEVEL)
180                 value |= pattern;
181         else
182                 value &= ~pattern;
183         sm_writel(sm, EIM_MODE, value);
184         value = sm_readl(sm, EIM_EDGE);
185         if (type & IRQ_EDGE_RISING)
186                 value |= pattern;
187         else
188                 value &= ~pattern;
189         sm_writel(sm, EIM_EDGE, value);
190         value = sm_readl(sm, EIM_LEVEL);
191         if (type & IRQ_LEVEL_HIGH)
192                 value |= pattern;
193         else
194                 value &= ~pattern;
195         sm_writel(sm, EIM_LEVEL, value);
196
197         spin_unlock_irqrestore(&sm->lock, flags);
198
199         return 0;
200 }
201
202 static unsigned int eim_get_type(struct irq_controller *irqc,
203                                  unsigned int irq)
204 {
205         struct at32_sm *sm = to_eim(irqc);
206         unsigned long flags;
207         unsigned int type = 0;
208         u32 mode, edge, level, pattern;
209
210         pattern = 1 << (irq - sm->irqc.first_irq);
211
212         spin_lock_irqsave(&sm->lock, flags);
213         mode = sm_readl(sm, EIM_MODE);
214         edge = sm_readl(sm, EIM_EDGE);
215         level = sm_readl(sm, EIM_LEVEL);
216         spin_unlock_irqrestore(&sm->lock, flags);
217
218         if (mode & pattern)
219                 type |= IRQ_TYPE_LEVEL;
220         if (edge & pattern)
221                 type |= IRQ_EDGE_RISING;
222         if (level & pattern)
223                 type |= IRQ_LEVEL_HIGH;
224
225         return type;
226 }
227
228 static struct irq_controller_class eim_irq_class = {
229         .typename       = "EIM",
230         .handle         = eim_handle_irq,
231         .setup          = eim_setup,
232         .free           = eim_free,
233         .mask           = eim_mask,
234         .unmask         = eim_unmask,
235         .set_type       = eim_set_type,
236         .get_type       = eim_get_type,
237 };
238
239 static int __init eim_init(void)
240 {
241         struct at32_sm *sm = &system_manager;
242         unsigned int i;
243         u32 pattern;
244         int ret;
245
246         /*
247          * The EIM is really the same module as SM, so register
248          * mapping, etc. has been taken care of already.
249          */
250
251         /*
252          * Find out how many interrupt lines that are actually
253          * implemented in hardware.
254          */
255         sm_writel(sm, EIM_IDR, ~0UL);
256         sm_writel(sm, EIM_MODE, ~0UL);
257         pattern = sm_readl(sm, EIM_MODE);
258         sm->irqc.nr_irqs = fls(pattern);
259
260         ret = -ENOMEM;
261         sm->action = kmalloc(sizeof(*sm->action) * sm->irqc.nr_irqs,
262                              GFP_KERNEL);
263         if (!sm->action)
264                 goto out;
265
266         for (i = 0; i < sm->irqc.nr_irqs; i++)
267                 sm->action[i] = &eim_spurious_action;
268
269         spin_lock_init(&sm->lock);
270         sm->irqc.irq_group = sm->pdev->resource[SM_EIM_IRQ_RESOURCE].start;
271         sm->irqc.class = &eim_irq_class;
272
273         ret = intc_register_controller(&sm->irqc);
274         if (ret < 0)
275                 goto out_free_actions;
276
277         printk("EIM: External Interrupt Module at 0x%p, IRQ group %u\n",
278                sm->regs, sm->irqc.irq_group);
279         printk("EIM: Handling %u external IRQs, starting with IRQ%u\n",
280                sm->irqc.nr_irqs, sm->irqc.first_irq);
281
282         return 0;
283
284 out_free_actions:
285         kfree(sm->action);
286 out:
287         return ret;
288 }
289 arch_initcall(eim_init);