aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorLee Jones <lee.jones@linaro.org>2015-02-18 10:13:58 -0500
committerJason Cooper <jason@lakedaemon.net>2015-03-03 14:48:53 -0500
commit070884845baaeaa41a11eccc2fc2a4080de64946 (patch)
tree044c1883151cf9ac36e9ab7a19da6e1b0b8366a4 /drivers/irqchip
parent9967c3c6f5e81342d9b82c43638d695991939969 (diff)
irqchip: st: Supply new driver for STi based devices
This driver is used to enable System Configuration Register controlled External, CTI (Core Sight), PMU (Performance Management), and PL310 L2 Cache IRQs prior to use. Signed-off-by: Lee Jones <lee.jones@linaro.org> Link: https://lkml.kernel.org/r/1424272444-16230-3-git-send-email-lee.jones@linaro.org Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig7
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-st.c206
3 files changed, 214 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index cc79d2a5a8c2..c8d260e33a90 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -110,6 +110,13 @@ config RENESAS_IRQC
110 bool 110 bool
111 select IRQ_DOMAIN 111 select IRQ_DOMAIN
112 112
113config ST_IRQCHIP
114 bool
115 select REGMAP
116 select MFD_SYSCON
117 help
118 Enables SysCfg Controlled IRQs on STi based platforms.
119
113config TB10X_IRQC 120config TB10X_IRQC
114 bool 121 bool
115 select IRQ_DOMAIN 122 select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 42965d2476bb..9bb4fd191e94 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
33obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o 33obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
34obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o 34obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
35obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o 35obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
36obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
36obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 37obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
37obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 38obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
38obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 39obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
diff --git a/drivers/irqchip/irq-st.c b/drivers/irqchip/irq-st.c
new file mode 100644
index 000000000000..9af48a85c16f
--- /dev/null
+++ b/drivers/irqchip/irq-st.c
@@ -0,0 +1,206 @@
1/*
2 * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
3 *
4 * Author: Lee Jones <lee.jones@linaro.org>
5 *
6 * This is a re-write of Christophe Kerello's PMU driver.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <dt-bindings/interrupt-controller/irq-st.h>
14#include <linux/err.h>
15#include <linux/mfd/syscon.h>
16#include <linux/of_device.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19#include <linux/slab.h>
20
21#define STIH415_SYSCFG_642 0x0a8
22#define STIH416_SYSCFG_7543 0x87c
23#define STIH407_SYSCFG_5102 0x198
24#define STID127_SYSCFG_734 0x088
25
26#define ST_A9_IRQ_MASK 0x001FFFFF
27#define ST_A9_IRQ_MAX_CHANS 2
28
29#define ST_A9_IRQ_EN_CTI_0 BIT(0)
30#define ST_A9_IRQ_EN_CTI_1 BIT(1)
31#define ST_A9_IRQ_EN_PMU_0 BIT(2)
32#define ST_A9_IRQ_EN_PMU_1 BIT(3)
33#define ST_A9_IRQ_EN_PL310_L2 BIT(4)
34#define ST_A9_IRQ_EN_EXT_0 BIT(5)
35#define ST_A9_IRQ_EN_EXT_1 BIT(6)
36#define ST_A9_IRQ_EN_EXT_2 BIT(7)
37
38#define ST_A9_FIQ_N_SEL(dev, chan) (dev << (8 + (chan * 3)))
39#define ST_A9_IRQ_N_SEL(dev, chan) (dev << (14 + (chan * 3)))
40#define ST_A9_EXTIRQ_INV_SEL(dev) (dev << 20)
41
42struct st_irq_syscfg {
43 struct regmap *regmap;
44 unsigned int syscfg;
45 unsigned int config;
46 bool ext_inverted;
47};
48
49static const struct of_device_id st_irq_syscfg_match[] = {
50 {
51 .compatible = "st,stih415-irq-syscfg",
52 .data = (void *)STIH415_SYSCFG_642,
53 },
54 {
55 .compatible = "st,stih416-irq-syscfg",
56 .data = (void *)STIH416_SYSCFG_7543,
57 },
58 {
59 .compatible = "st,stih407-irq-syscfg",
60 .data = (void *)STIH407_SYSCFG_5102,
61 },
62 {
63 .compatible = "st,stid127-irq-syscfg",
64 .data = (void *)STID127_SYSCFG_734,
65 },
66 {}
67};
68
69static int st_irq_xlate(struct platform_device *pdev,
70 int device, int channel, bool irq)
71{
72 struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
73
74 /* Set the device enable bit. */
75 switch (device) {
76 case ST_IRQ_SYSCFG_EXT_0:
77 ddata->config |= ST_A9_IRQ_EN_EXT_0;
78 break;
79 case ST_IRQ_SYSCFG_EXT_1:
80 ddata->config |= ST_A9_IRQ_EN_EXT_1;
81 break;
82 case ST_IRQ_SYSCFG_EXT_2:
83 ddata->config |= ST_A9_IRQ_EN_EXT_2;
84 break;
85 case ST_IRQ_SYSCFG_CTI_0:
86 ddata->config |= ST_A9_IRQ_EN_CTI_0;
87 break;
88 case ST_IRQ_SYSCFG_CTI_1:
89 ddata->config |= ST_A9_IRQ_EN_CTI_1;
90 break;
91 case ST_IRQ_SYSCFG_PMU_0:
92 ddata->config |= ST_A9_IRQ_EN_PMU_0;
93 break;
94 case ST_IRQ_SYSCFG_PMU_1:
95 ddata->config |= ST_A9_IRQ_EN_PMU_1;
96 break;
97 case ST_IRQ_SYSCFG_pl310_L2:
98 ddata->config |= ST_A9_IRQ_EN_PL310_L2;
99 break;
100 case ST_IRQ_SYSCFG_DISABLED:
101 return 0;
102 default:
103 dev_err(&pdev->dev, "Unrecognised device %d\n", device);
104 return -EINVAL;
105 }
106
107 /* Select IRQ/FIQ channel for device. */
108 ddata->config |= irq ?
109 ST_A9_IRQ_N_SEL(device, channel) :
110 ST_A9_FIQ_N_SEL(device, channel);
111
112 return 0;
113}
114
115static int st_irq_syscfg_enable(struct platform_device *pdev)
116{
117 struct device_node *np = pdev->dev.of_node;
118 struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
119 int channels, ret, i;
120 u32 device, invert;
121
122 channels = of_property_count_u32_elems(np, "st,irq-device");
123 if (channels != ST_A9_IRQ_MAX_CHANS) {
124 dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n");
125 return -EINVAL;
126 }
127
128 channels = of_property_count_u32_elems(np, "st,fiq-device");
129 if (channels != ST_A9_IRQ_MAX_CHANS) {
130 dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n");
131 return -EINVAL;
132 }
133
134 for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) {
135 of_property_read_u32_index(np, "st,irq-device", i, &device);
136
137 ret = st_irq_xlate(pdev, device, i, true);
138 if (ret)
139 return ret;
140
141 of_property_read_u32_index(np, "st,fiq-device", i, &device);
142
143 ret = st_irq_xlate(pdev, device, i, false);
144 if (ret)
145 return ret;
146 }
147
148 /* External IRQs may be inverted. */
149 of_property_read_u32(np, "st,invert-ext", &invert);
150 ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert);
151
152 return regmap_update_bits(ddata->regmap, ddata->syscfg,
153 ST_A9_IRQ_MASK, ddata->config);
154}
155
156static int st_irq_syscfg_probe(struct platform_device *pdev)
157{
158 struct device_node *np = pdev->dev.of_node;
159 const struct of_device_id *match;
160 struct st_irq_syscfg *ddata;
161
162 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
163 if (!ddata)
164 return -ENOMEM;
165
166 match = of_match_device(st_irq_syscfg_match, &pdev->dev);
167 if (!match)
168 return -ENODEV;
169
170 ddata->syscfg = (unsigned int)match->data;
171
172 ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
173 if (IS_ERR(ddata->regmap)) {
174 dev_err(&pdev->dev, "syscfg phandle missing\n");
175 return PTR_ERR(ddata->regmap);
176 }
177
178 dev_set_drvdata(&pdev->dev, ddata);
179
180 return st_irq_syscfg_enable(pdev);
181}
182
183static int st_irq_syscfg_resume(struct device *dev)
184{
185 struct st_irq_syscfg *ddata = dev_get_drvdata(dev);
186
187 return regmap_update_bits(ddata->regmap, ddata->syscfg,
188 ST_A9_IRQ_MASK, ddata->config);
189}
190
191static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume);
192
193static struct platform_driver st_irq_syscfg_driver = {
194 .driver = {
195 .name = "st_irq_syscfg",
196 .pm = &st_irq_syscfg_pm_ops,
197 .of_match_table = st_irq_syscfg_match,
198 },
199 .probe = st_irq_syscfg_probe,
200};
201
202static int __init st_irq_syscfg_init(void)
203{
204 return platform_driver_register(&st_irq_syscfg_driver);
205}
206core_initcall(st_irq_syscfg_init);