aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHeiko Stuebner <heiko@sntech.de>2013-04-04 01:55:10 -0400
committerKukjin Kim <kgene.kim@samsung.com>2013-04-04 01:56:30 -0400
commitf0774d41da0e607b70e54ecc50aeb6684f54c2b1 (patch)
treedc188d9b2d30ca34e3ef1d82f347c35062dc3a38
parentf5a25524508e68ac670b28db9112d3962dca4703 (diff)
irqchip: s3c24xx: add devicetree support
Add the necessary code to initialize the interrupt controller thru devicetree data using the irqchip infrastructure. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt53
-rw-r--r--drivers/irqchip/irq-s3c24xx.c231
2 files changed, 278 insertions, 6 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt
new file mode 100644
index 000000000000..c54c5a9a2a90
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt
@@ -0,0 +1,53 @@
1Samsung S3C24XX Interrupt Controllers
2
3The S3C24XX SoCs contain a custom set of interrupt controllers providing a
4varying number of interrupt sources. The set consists of a main- and sub-
5controller and on newer SoCs even a second main controller.
6
7Required properties:
8- compatible: Compatible property value should be "samsung,s3c2410-irq"
9 for machines before s3c2416 and "samsung,s3c2416-irq" for s3c2416 and later.
10
11- reg: Physical base address of the controller and length of memory mapped
12 region.
13
14- interrupt-controller : Identifies the node as an interrupt controller
15
16- #interrupt-cells : Specifies the number of cells needed to encode an
17 interrupt source. The value shall be 4 and interrupt descriptor shall
18 have the following format:
19 <ctrl_num parent_irq ctrl_irq type>
20
21 ctrl_num contains the controller to use:
22 - 0 ... main controller
23 - 1 ... sub controller
24 - 2 ... second main controller on s3c2416 and s3c2450
25 parent_irq contains the parent bit in the main controller and will be
26 ignored in main controllers
27 ctrl_irq contains the interrupt bit of the controller
28 type contains the trigger type to use
29
30Example:
31
32 interrupt-controller@4a000000 {
33 compatible = "samsung,s3c2410-irq";
34 reg = <0x4a000000 0x100>;
35 interrupt-controller;
36 #interrupt-cells=<4>;
37 };
38
39 [...]
40
41 serial@50000000 {
42 compatible = "samsung,s3c2410-uart";
43 reg = <0x50000000 0x4000>;
44 interrupt-parent = <&subintc>;
45 interrupts = <1 28 0 4>, <1 28 1 4>;
46 };
47
48 rtc@57000000 {
49 compatible = "samsung,s3c2410-rtc";
50 reg = <0x57000000 0x100>;
51 interrupt-parent = <&intc>;
52 interrupts = <0 30 0 3>, <0 8 0 3>;
53 };
diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c
index 02b82db086d8..5e40b3424df8 100644
--- a/drivers/irqchip/irq-s3c24xx.c
+++ b/drivers/irqchip/irq-s3c24xx.c
@@ -25,6 +25,9 @@
25#include <linux/ioport.h> 25#include <linux/ioport.h>
26#include <linux/device.h> 26#include <linux/device.h>
27#include <linux/irqdomain.h> 27#include <linux/irqdomain.h>
28#include <linux/of.h>
29#include <linux/of_irq.h>
30#include <linux/of_address.h>
28 31
29#include <asm/exception.h> 32#include <asm/exception.h>
30#include <asm/mach/irq.h> 33#include <asm/mach/irq.h>
@@ -36,6 +39,8 @@
36#include <plat/regs-irqtype.h> 39#include <plat/regs-irqtype.h>
37#include <plat/pm.h> 40#include <plat/pm.h>
38 41
42#include "irqchip.h"
43
39#define S3C_IRQTYPE_NONE 0 44#define S3C_IRQTYPE_NONE 0
40#define S3C_IRQTYPE_EINT 1 45#define S3C_IRQTYPE_EINT 1
41#define S3C_IRQTYPE_EDGE 2 46#define S3C_IRQTYPE_EDGE 2
@@ -94,7 +99,10 @@ static void s3c_irq_mask(struct irq_data *data)
94 if (parent_intc) { 99 if (parent_intc) {
95 parent_data = &parent_intc->irqs[irq_data->parent_irq]; 100 parent_data = &parent_intc->irqs[irq_data->parent_irq];
96 101
97 /* check to see if we need to mask the parent IRQ */ 102 /* check to see if we need to mask the parent IRQ
103 * The parent_irq is always in main_intc, so the hwirq
104 * for find_mapping does not need an offset in any case.
105 */
98 if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { 106 if ((mask & parent_data->sub_bits) == parent_data->sub_bits) {
99 irqno = irq_find_mapping(parent_intc->domain, 107 irqno = irq_find_mapping(parent_intc->domain,
100 irq_data->parent_irq); 108 irq_data->parent_irq);
@@ -294,10 +302,18 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc)
294{ 302{
295 struct irq_chip *chip = irq_desc_get_chip(desc); 303 struct irq_chip *chip = irq_desc_get_chip(desc);
296 struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); 304 struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
305 struct s3c_irq_intc *intc = irq_data->intc;
297 struct s3c_irq_intc *sub_intc = irq_data->sub_intc; 306 struct s3c_irq_intc *sub_intc = irq_data->sub_intc;
298 unsigned long src; 307 unsigned long src;
299 unsigned long msk; 308 unsigned long msk;
300 unsigned int n; 309 unsigned int n;
310 unsigned int offset;
311
312 /* we're using individual domains for the non-dt case
313 * and one big domain for the dt case where the subintc
314 * starts at hwirq number 32.
315 */
316 offset = (intc->domain->of_node) ? 32 : 0;
301 317
302 chained_irq_enter(chip, desc); 318 chained_irq_enter(chip, desc);
303 319
@@ -310,14 +326,15 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc)
310 while (src) { 326 while (src) {
311 n = __ffs(src); 327 n = __ffs(src);
312 src &= ~(1 << n); 328 src &= ~(1 << n);
313 generic_handle_irq(irq_find_mapping(sub_intc->domain, n)); 329 irq = irq_find_mapping(sub_intc->domain, offset + n);
330 generic_handle_irq(irq);
314 } 331 }
315 332
316 chained_irq_exit(chip, desc); 333 chained_irq_exit(chip, desc);
317} 334}
318 335
319static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, 336static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
320 struct pt_regs *regs) 337 struct pt_regs *regs, int intc_offset)
321{ 338{
322 int pnd; 339 int pnd;
323 int offset; 340 int offset;
@@ -327,6 +344,10 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
327 if (!pnd) 344 if (!pnd)
328 return false; 345 return false;
329 346
347 /* non-dt machines use individual domains */
348 if (!intc->domain->of_node)
349 intc_offset = 0;
350
330 /* We have a problem that the INTOFFSET register does not always 351 /* We have a problem that the INTOFFSET register does not always
331 * show one interrupt. Occasionally we get two interrupts through 352 * show one interrupt. Occasionally we get two interrupts through
332 * the prioritiser, and this causes the INTOFFSET register to show 353 * the prioritiser, and this causes the INTOFFSET register to show
@@ -343,7 +364,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
343 if (!(pnd & (1 << offset))) 364 if (!(pnd & (1 << offset)))
344 offset = __ffs(pnd); 365 offset = __ffs(pnd);
345 366
346 irq = irq_find_mapping(intc->domain, offset); 367 irq = irq_find_mapping(intc->domain, intc_offset + offset);
347 handle_IRQ(irq, regs); 368 handle_IRQ(irq, regs);
348 return true; 369 return true;
349} 370}
@@ -352,11 +373,11 @@ asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs)
352{ 373{
353 do { 374 do {
354 if (likely(s3c_intc[0])) 375 if (likely(s3c_intc[0]))
355 if (s3c24xx_handle_intc(s3c_intc[0], regs)) 376 if (s3c24xx_handle_intc(s3c_intc[0], regs, 0))
356 continue; 377 continue;
357 378
358 if (s3c_intc[2]) 379 if (s3c_intc[2])
359 if (s3c24xx_handle_intc(s3c_intc[2], regs)) 380 if (s3c24xx_handle_intc(s3c_intc[2], regs, 64))
360 continue; 381 continue;
361 382
362 break; 383 break;
@@ -1134,3 +1155,201 @@ void __init s3c2443_init_irq(void)
1134 s3c_intc[0], 0x4a000018); 1155 s3c_intc[0], 0x4a000018);
1135} 1156}
1136#endif 1157#endif
1158
1159#ifdef CONFIG_OF
1160static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
1161 irq_hw_number_t hw)
1162{
1163 unsigned int ctrl_num = hw / 32;
1164 unsigned int intc_hw = hw % 32;
1165 struct s3c_irq_intc *intc = s3c_intc[ctrl_num];
1166 struct s3c_irq_intc *parent_intc = intc->parent;
1167 struct s3c_irq_data *irq_data = &intc->irqs[intc_hw];
1168
1169 /* attach controller pointer to irq_data */
1170 irq_data->intc = intc;
1171 irq_data->offset = intc_hw;
1172
1173 if (!parent_intc)
1174 irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
1175 else
1176 irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
1177 handle_edge_irq);
1178
1179 irq_set_chip_data(virq, irq_data);
1180
1181 set_irq_flags(virq, IRQF_VALID);
1182
1183 return 0;
1184}
1185
1186/* Translate our of irq notation
1187 * format: <ctrl_num ctrl_irq parent_irq type>
1188 */
1189static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
1190 const u32 *intspec, unsigned int intsize,
1191 irq_hw_number_t *out_hwirq, unsigned int *out_type)
1192{
1193 struct s3c_irq_intc *intc;
1194 struct s3c_irq_intc *parent_intc;
1195 struct s3c_irq_data *irq_data;
1196 struct s3c_irq_data *parent_irq_data;
1197 int irqno;
1198
1199 if (WARN_ON(intsize < 4))
1200 return -EINVAL;
1201
1202 if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
1203 pr_err("controller number %d invalid\n", intspec[0]);
1204 return -EINVAL;
1205 }
1206 intc = s3c_intc[intspec[0]];
1207
1208 *out_hwirq = intspec[0] * 32 + intspec[2];
1209 *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;
1210
1211 parent_intc = intc->parent;
1212 if (parent_intc) {
1213 irq_data = &intc->irqs[intspec[2]];
1214 irq_data->parent_irq = intspec[1];
1215 parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
1216 parent_irq_data->sub_intc = intc;
1217 parent_irq_data->sub_bits |= (1UL << intspec[2]);
1218
1219 /* parent_intc is always s3c_intc[0], so no offset */
1220 irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
1221 if (irqno < 0) {
1222 pr_err("irq: could not map parent interrupt\n");
1223 return irqno;
1224 }
1225
1226 irq_set_chained_handler(irqno, s3c_irq_demux);
1227 }
1228
1229 return 0;
1230}
1231
1232static struct irq_domain_ops s3c24xx_irq_ops_of = {
1233 .map = s3c24xx_irq_map_of,
1234 .xlate = s3c24xx_irq_xlate_of,
1235};
1236
1237struct s3c24xx_irq_of_ctrl {
1238 char *name;
1239 unsigned long offset;
1240 struct s3c_irq_intc **handle;
1241 struct s3c_irq_intc **parent;
1242 struct irq_domain_ops *ops;
1243};
1244
1245static int __init s3c_init_intc_of(struct device_node *np,
1246 struct device_node *interrupt_parent,
1247 struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
1248{
1249 struct s3c_irq_intc *intc;
1250 struct s3c24xx_irq_of_ctrl *ctrl;
1251 struct irq_domain *domain;
1252 void __iomem *reg_base;
1253 int i;
1254
1255 reg_base = of_iomap(np, 0);
1256 if (!reg_base) {
1257 pr_err("irq-s3c24xx: could not map irq registers\n");
1258 return -EINVAL;
1259 }
1260
1261 domain = irq_domain_add_linear(np, num_ctrl * 32,
1262 &s3c24xx_irq_ops_of, NULL);
1263 if (!domain) {
1264 pr_err("irq: could not create irq-domain\n");
1265 return -EINVAL;
1266 }
1267
1268 for (i = 0; i < num_ctrl; i++) {
1269 ctrl = &s3c_ctrl[i];
1270
1271 pr_debug("irq: found controller %s\n", ctrl->name);
1272
1273 intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
1274 if (!intc)
1275 return -ENOMEM;
1276
1277 intc->domain = domain;
1278 intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32,
1279 GFP_KERNEL);
1280 if (!intc->irqs) {
1281 kfree(intc);
1282 return -ENOMEM;
1283 }
1284
1285 if (ctrl->parent) {
1286 intc->reg_pending = reg_base + ctrl->offset;
1287 intc->reg_mask = reg_base + ctrl->offset + 0x4;
1288
1289 if (*(ctrl->parent)) {
1290 intc->parent = *(ctrl->parent);
1291 } else {
1292 pr_warn("irq: parent of %s missing\n",
1293 ctrl->name);
1294 kfree(intc->irqs);
1295 kfree(intc);
1296 continue;
1297 }
1298 } else {
1299 intc->reg_pending = reg_base + ctrl->offset;
1300 intc->reg_mask = reg_base + ctrl->offset + 0x08;
1301 intc->reg_intpnd = reg_base + ctrl->offset + 0x10;
1302 }
1303
1304 s3c24xx_clear_intc(intc);
1305 s3c_intc[i] = intc;
1306 }
1307
1308 set_handle_irq(s3c24xx_handle_irq);
1309
1310 return 0;
1311}
1312
1313static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
1314 {
1315 .name = "intc",
1316 .offset = 0,
1317 }, {
1318 .name = "subintc",
1319 .offset = 0x18,
1320 .parent = &s3c_intc[0],
1321 }
1322};
1323
1324int __init s3c2410_init_intc_of(struct device_node *np,
1325 struct device_node *interrupt_parent,
1326 struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
1327{
1328 return s3c_init_intc_of(np, interrupt_parent,
1329 s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
1330}
1331IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);
1332
1333static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = {
1334 {
1335 .name = "intc",
1336 .offset = 0,
1337 }, {
1338 .name = "subintc",
1339 .offset = 0x18,
1340 .parent = &s3c_intc[0],
1341 }, {
1342 .name = "intc2",
1343 .offset = 0x40,
1344 }
1345};
1346
1347int __init s3c2416_init_intc_of(struct device_node *np,
1348 struct device_node *interrupt_parent,
1349 struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl)
1350{
1351 return s3c_init_intc_of(np, interrupt_parent,
1352 s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl));
1353}
1354IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of);
1355#endif