Merge branch 'clockevents/4.2' of http://git.linaro.org/people/daniel.lezcano/linux...
[linux-2.6-block.git] / drivers / irqchip / irq-gic-common.c
CommitLineData
d51d0af4
MZ
1/*
2 * Copyright (C) 2002 ARM Limited, All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/interrupt.h>
18#include <linux/io.h>
19#include <linux/irq.h>
20#include <linux/irqchip/arm-gic.h>
21
22#include "irq-gic-common.h"
23
fb7e7deb 24int gic_configure_irq(unsigned int irq, unsigned int type,
d51d0af4
MZ
25 void __iomem *base, void (*sync_access)(void))
26{
27 u32 enablemask = 1 << (irq % 32);
28 u32 enableoff = (irq / 32) * 4;
29 u32 confmask = 0x2 << ((irq % 16) * 2);
30 u32 confoff = (irq / 16) * 4;
31 bool enabled = false;
fb7e7deb
LD
32 u32 val, oldval;
33 int ret = 0;
d51d0af4
MZ
34
35 /*
36 * Read current configuration register, and insert the config
37 * for "irq", depending on "type".
38 */
fb7e7deb
LD
39 val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
40 if (type & IRQ_TYPE_LEVEL_MASK)
d51d0af4 41 val &= ~confmask;
fb7e7deb 42 else if (type & IRQ_TYPE_EDGE_BOTH)
d51d0af4
MZ
43 val |= confmask;
44
45 /*
46 * As recommended by the spec, disable the interrupt before changing
47 * the configuration
48 */
49 if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) {
50 writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff);
51 if (sync_access)
52 sync_access();
53 enabled = true;
54 }
55
56 /*
57 * Write back the new configuration, and possibly re-enable
fb7e7deb
LD
58 * the interrupt. If we tried to write a new configuration and failed,
59 * return an error.
d51d0af4
MZ
60 */
61 writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
fb7e7deb
LD
62 if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
63 ret = -EINVAL;
d51d0af4
MZ
64
65 if (enabled)
66 writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
67
68 if (sync_access)
69 sync_access();
fb7e7deb
LD
70
71 return ret;
d51d0af4
MZ
72}
73
74void __init gic_dist_config(void __iomem *base, int gic_irqs,
75 void (*sync_access)(void))
76{
77 unsigned int i;
78
79 /*
80 * Set all global interrupts to be level triggered, active low.
81 */
82 for (i = 32; i < gic_irqs; i += 16)
e5f81539
FK
83 writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
84 base + GIC_DIST_CONFIG + i / 4);
d51d0af4
MZ
85
86 /*
87 * Set priority on all global interrupts.
88 */
89 for (i = 32; i < gic_irqs; i += 4)
e5f81539 90 writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
d51d0af4
MZ
91
92 /*
93 * Disable all interrupts. Leave the PPI and SGIs alone
94 * as they are enabled by redistributor registers.
95 */
96 for (i = 32; i < gic_irqs; i += 32)
e5f81539
FK
97 writel_relaxed(GICD_INT_EN_CLR_X32,
98 base + GIC_DIST_ENABLE_CLEAR + i / 8);
d51d0af4
MZ
99
100 if (sync_access)
101 sync_access();
102}
103
104void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
105{
106 int i;
107
108 /*
109 * Deal with the banked PPI and SGI interrupts - disable all
110 * PPI interrupts, ensure all SGI interrupts are enabled.
111 */
e5f81539
FK
112 writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR);
113 writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET);
d51d0af4
MZ
114
115 /*
116 * Set priority on PPI and SGI interrupts
117 */
118 for (i = 0; i < 32; i += 4)
e5f81539
FK
119 writel_relaxed(GICD_INT_DEF_PRI_X4,
120 base + GIC_DIST_PRI + i * 4 / 4);
d51d0af4
MZ
121
122 if (sync_access)
123 sync_access();
124}