blob: ad96ebb0c7abd882075f67162ff605b8969a792b [file] [log] [blame]
Marc Zyngierd51d0af2014-06-30 16:01:30 +01001/*
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
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000024int gic_configure_irq(unsigned int irq, unsigned int type,
Marc Zyngierd51d0af2014-06-30 16:01:30 +010025 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;
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000032 u32 val, oldval;
33 int ret = 0;
Marc Zyngierd51d0af2014-06-30 16:01:30 +010034
35 /*
36 * Read current configuration register, and insert the config
37 * for "irq", depending on "type".
38 */
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000039 val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
40 if (type & IRQ_TYPE_LEVEL_MASK)
Marc Zyngierd51d0af2014-06-30 16:01:30 +010041 val &= ~confmask;
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000042 else if (type & IRQ_TYPE_EDGE_BOTH)
Marc Zyngierd51d0af2014-06-30 16:01:30 +010043 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
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000058 * the interrupt. If we tried to write a new configuration and failed,
59 * return an error.
Marc Zyngierd51d0af2014-06-30 16:01:30 +010060 */
61 writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000062 if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
63 ret = -EINVAL;
Marc Zyngierd51d0af2014-06-30 16:01:30 +010064
65 if (enabled)
66 writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
67
68 if (sync_access)
69 sync_access();
Liviu Dudaufb7e7de2015-01-20 16:52:59 +000070
71 return ret;
Marc Zyngierd51d0af2014-06-30 16:01:30 +010072}
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)
Feng Kane5f81532014-07-30 14:56:58 -070083 writel_relaxed(GICD_INT_ACTLOW_LVLTRIG,
84 base + GIC_DIST_CONFIG + i / 4);
Marc Zyngierd51d0af2014-06-30 16:01:30 +010085
86 /*
87 * Set priority on all global interrupts.
88 */
89 for (i = 32; i < gic_irqs; i += 4)
Feng Kane5f81532014-07-30 14:56:58 -070090 writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
Marc Zyngierd51d0af2014-06-30 16:01:30 +010091
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)
Feng Kane5f81532014-07-30 14:56:58 -070097 writel_relaxed(GICD_INT_EN_CLR_X32,
98 base + GIC_DIST_ENABLE_CLEAR + i / 8);
Marc Zyngierd51d0af2014-06-30 16:01:30 +010099
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 */
Feng Kane5f81532014-07-30 14:56:58 -0700112 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);
Marc Zyngierd51d0af2014-06-30 16:01:30 +0100114
115 /*
116 * Set priority on PPI and SGI interrupts
117 */
118 for (i = 0; i < 32; i += 4)
Feng Kane5f81532014-07-30 14:56:58 -0700119 writel_relaxed(GICD_INT_DEF_PRI_X4,
120 base + GIC_DIST_PRI + i * 4 / 4);
Marc Zyngierd51d0af2014-06-30 16:01:30 +0100121
122 if (sync_access)
123 sync_access();
124}