2 * System Manager driver for AT32AP CPUs
4 * Copyright (C) 2006 Atmel Corporation
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.
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>
23 #include <asm/arch/sm.h>
27 #define SM_EIM_IRQ_RESOURCE 1
28 #define SM_PM_IRQ_RESOURCE 2
29 #define SM_RTC_IRQ_RESOURCE 3
31 #define to_eim(irqc) container_of(irqc, struct at32_sm, irqc)
33 struct at32_sm system_manager
;
35 int __init
at32_sm_init(void)
37 struct resource
*regs
;
38 struct at32_sm
*sm
= &system_manager
;
41 regs
= platform_get_resource(&at32_sm_device
, IORESOURCE_MEM
, 0);
45 spin_lock_init(&sm
->lock
);
46 sm
->pdev
= &at32_sm_device
;
49 sm
->regs
= ioremap(regs
->start
, regs
->end
- regs
->start
+ 1);
56 printk(KERN_ERR
"Failed to initialize System Manager: %d\n", ret
);
61 * External Interrupt Module (EIM).
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.
70 static irqreturn_t
spurious_eim_interrupt(int irq
, void *dev_id
,
73 printk(KERN_WARNING
"Spurious EIM interrupt %d\n", irq
);
78 static struct irqaction eim_spurious_action
= {
79 .handler
= spurious_eim_interrupt
,
82 static irqreturn_t
eim_handle_irq(int irq
, void *dev_id
, struct pt_regs
*regs
)
84 struct irq_controller
* irqc
= dev_id
;
85 struct at32_sm
*sm
= to_eim(irqc
);
86 unsigned long pending
;
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
96 pending
= intc_get_pending(sm
->irqc
.irq_group
);
97 if (unlikely(!pending
)) {
98 printk(KERN_ERR
"EIM (group %u): No interrupts pending!\n",
104 struct irqaction
*action
;
107 i
= fls(pending
) - 1;
108 pending
&= ~(1 << i
);
109 action
= sm
->action
[i
];
111 /* Acknowledge the interrupt */
112 sm_writel(sm
, EIM_ICR
, 1 << i
);
114 spin_unlock(&sm
->lock
);
116 if (action
->flags
& SA_INTERRUPT
)
118 action
->handler(sm
->irqc
.first_irq
+ i
, action
->dev_id
, regs
);
120 spin_lock(&sm
->lock
);
121 if (action
->flags
& SA_SAMPLE_RANDOM
)
122 add_interrupt_randomness(sm
->irqc
.first_irq
+ i
);
126 spin_unlock(&sm
->lock
);
130 static void eim_mask(struct irq_controller
*irqc
, unsigned int irq
)
132 struct at32_sm
*sm
= to_eim(irqc
);
135 i
= irq
- sm
->irqc
.first_irq
;
136 sm_writel(sm
, EIM_IDR
, 1 << i
);
139 static void eim_unmask(struct irq_controller
*irqc
, unsigned int irq
)
141 struct at32_sm
*sm
= to_eim(irqc
);
144 i
= irq
- sm
->irqc
.first_irq
;
145 sm_writel(sm
, EIM_IER
, 1 << i
);
148 static int eim_setup(struct irq_controller
*irqc
, unsigned int irq
,
149 struct irqaction
*action
)
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
);
159 static void eim_free(struct irq_controller
*irqc
, unsigned int irq
,
162 struct at32_sm
*sm
= to_eim(irqc
);
164 sm
->action
[irq
- sm
->irqc
.first_irq
] = &eim_spurious_action
;
167 static int eim_set_type(struct irq_controller
*irqc
, unsigned int irq
,
170 struct at32_sm
*sm
= to_eim(irqc
);
174 spin_lock_irqsave(&sm
->lock
, flags
);
176 pattern
= 1 << (irq
- sm
->irqc
.first_irq
);
178 value
= sm_readl(sm
, EIM_MODE
);
179 if (type
& IRQ_TYPE_LEVEL
)
183 sm_writel(sm
, EIM_MODE
, value
);
184 value
= sm_readl(sm
, EIM_EDGE
);
185 if (type
& IRQ_EDGE_RISING
)
189 sm_writel(sm
, EIM_EDGE
, value
);
190 value
= sm_readl(sm
, EIM_LEVEL
);
191 if (type
& IRQ_LEVEL_HIGH
)
195 sm_writel(sm
, EIM_LEVEL
, value
);
197 spin_unlock_irqrestore(&sm
->lock
, flags
);
202 static unsigned int eim_get_type(struct irq_controller
*irqc
,
205 struct at32_sm
*sm
= to_eim(irqc
);
207 unsigned int type
= 0;
208 u32 mode
, edge
, level
, pattern
;
210 pattern
= 1 << (irq
- sm
->irqc
.first_irq
);
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
);
219 type
|= IRQ_TYPE_LEVEL
;
221 type
|= IRQ_EDGE_RISING
;
223 type
|= IRQ_LEVEL_HIGH
;
228 static struct irq_controller_class eim_irq_class
= {
230 .handle
= eim_handle_irq
,
234 .unmask
= eim_unmask
,
235 .set_type
= eim_set_type
,
236 .get_type
= eim_get_type
,
239 static int __init
eim_init(void)
241 struct at32_sm
*sm
= &system_manager
;
247 * The EIM is really the same module as SM, so register
248 * mapping, etc. has been taken care of already.
252 * Find out how many interrupt lines that are actually
253 * implemented in hardware.
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
);
261 sm
->action
= kmalloc(sizeof(*sm
->action
) * sm
->irqc
.nr_irqs
,
266 for (i
= 0; i
< sm
->irqc
.nr_irqs
; i
++)
267 sm
->action
[i
] = &eim_spurious_action
;
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
;
273 ret
= intc_register_controller(&sm
->irqc
);
275 goto out_free_actions
;
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
);
289 arch_initcall(eim_init
);