Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Heiko Stuebner and committed by
Kukjin Kim
f0774d41 f5a25524

+278 -6
+53
Documentation/devicetree/bindings/interrupt-controller/samsung,s3c24xx-irq.txt
··· 1 + Samsung S3C24XX Interrupt Controllers 2 + 3 + The S3C24XX SoCs contain a custom set of interrupt controllers providing a 4 + varying number of interrupt sources. The set consists of a main- and sub- 5 + controller and on newer SoCs even a second main controller. 6 + 7 + Required 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 + 30 + Example: 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 + };
+225 -6
drivers/irqchip/irq-s3c24xx.c
··· 25 25 #include <linux/ioport.h> 26 26 #include <linux/device.h> 27 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 32 #include <asm/exception.h> 30 33 #include <asm/mach/irq.h> ··· 38 35 #include <plat/cpu.h> 39 36 #include <plat/regs-irqtype.h> 40 37 #include <plat/pm.h> 38 + 39 + #include "irqchip.h" 41 40 42 41 #define S3C_IRQTYPE_NONE 0 43 42 #define S3C_IRQTYPE_EINT 1 ··· 99 94 if (parent_intc) { 100 95 parent_data = &parent_intc->irqs[irq_data->parent_irq]; 101 96 102 - /* check to see if we need to mask the parent IRQ */ 97 + /* check to see if we need to mask the parent IRQ 98 + * The parent_irq is always in main_intc, so the hwirq 99 + * for find_mapping does not need an offset in any case. 100 + */ 103 101 if ((mask & parent_data->sub_bits) == parent_data->sub_bits) { 104 102 irqno = irq_find_mapping(parent_intc->domain, 105 103 irq_data->parent_irq); ··· 302 294 { 303 295 struct irq_chip *chip = irq_desc_get_chip(desc); 304 296 struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc); 297 + struct s3c_irq_intc *intc = irq_data->intc; 305 298 struct s3c_irq_intc *sub_intc = irq_data->sub_intc; 306 299 unsigned long src; 307 300 unsigned long msk; 308 301 unsigned int n; 302 + unsigned int offset; 303 + 304 + /* we're using individual domains for the non-dt case 305 + * and one big domain for the dt case where the subintc 306 + * starts at hwirq number 32. 307 + */ 308 + offset = (intc->domain->of_node) ? 32 : 0; 309 309 310 310 chained_irq_enter(chip, desc); 311 311 ··· 326 310 while (src) { 327 311 n = __ffs(src); 328 312 src &= ~(1 << n); 329 - generic_handle_irq(irq_find_mapping(sub_intc->domain, n)); 313 + irq = irq_find_mapping(sub_intc->domain, offset + n); 314 + generic_handle_irq(irq); 330 315 } 331 316 332 317 chained_irq_exit(chip, desc); 333 318 } 334 319 335 320 static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, 336 - struct pt_regs *regs) 321 + struct pt_regs *regs, int intc_offset) 337 322 { 338 323 int pnd; 339 324 int offset; ··· 343 326 pnd = __raw_readl(intc->reg_intpnd); 344 327 if (!pnd) 345 328 return false; 329 + 330 + /* non-dt machines use individual domains */ 331 + if (!intc->domain->of_node) 332 + intc_offset = 0; 346 333 347 334 /* We have a problem that the INTOFFSET register does not always 348 335 * show one interrupt. Occasionally we get two interrupts through ··· 364 343 if (!(pnd & (1 << offset))) 365 344 offset = __ffs(pnd); 366 345 367 - irq = irq_find_mapping(intc->domain, offset); 346 + irq = irq_find_mapping(intc->domain, intc_offset + offset); 368 347 handle_IRQ(irq, regs); 369 348 return true; 370 349 } ··· 373 352 { 374 353 do { 375 354 if (likely(s3c_intc[0])) 376 - if (s3c24xx_handle_intc(s3c_intc[0], regs)) 355 + if (s3c24xx_handle_intc(s3c_intc[0], regs, 0)) 377 356 continue; 378 357 379 358 if (s3c_intc[2]) 380 - if (s3c24xx_handle_intc(s3c_intc[2], regs)) 359 + if (s3c24xx_handle_intc(s3c_intc[2], regs, 64)) 381 360 continue; 382 361 383 362 break; ··· 1154 1133 s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0], 1155 1134 s3c_intc[0], 0x4a000018); 1156 1135 } 1136 + #endif 1137 + 1138 + #ifdef CONFIG_OF 1139 + static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq, 1140 + irq_hw_number_t hw) 1141 + { 1142 + unsigned int ctrl_num = hw / 32; 1143 + unsigned int intc_hw = hw % 32; 1144 + struct s3c_irq_intc *intc = s3c_intc[ctrl_num]; 1145 + struct s3c_irq_intc *parent_intc = intc->parent; 1146 + struct s3c_irq_data *irq_data = &intc->irqs[intc_hw]; 1147 + 1148 + /* attach controller pointer to irq_data */ 1149 + irq_data->intc = intc; 1150 + irq_data->offset = intc_hw; 1151 + 1152 + if (!parent_intc) 1153 + irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq); 1154 + else 1155 + irq_set_chip_and_handler(virq, &s3c_irq_level_chip, 1156 + handle_edge_irq); 1157 + 1158 + irq_set_chip_data(virq, irq_data); 1159 + 1160 + set_irq_flags(virq, IRQF_VALID); 1161 + 1162 + return 0; 1163 + } 1164 + 1165 + /* Translate our of irq notation 1166 + * format: <ctrl_num ctrl_irq parent_irq type> 1167 + */ 1168 + static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n, 1169 + const u32 *intspec, unsigned int intsize, 1170 + irq_hw_number_t *out_hwirq, unsigned int *out_type) 1171 + { 1172 + struct s3c_irq_intc *intc; 1173 + struct s3c_irq_intc *parent_intc; 1174 + struct s3c_irq_data *irq_data; 1175 + struct s3c_irq_data *parent_irq_data; 1176 + int irqno; 1177 + 1178 + if (WARN_ON(intsize < 4)) 1179 + return -EINVAL; 1180 + 1181 + if (intspec[0] > 2 || !s3c_intc[intspec[0]]) { 1182 + pr_err("controller number %d invalid\n", intspec[0]); 1183 + return -EINVAL; 1184 + } 1185 + intc = s3c_intc[intspec[0]]; 1186 + 1187 + *out_hwirq = intspec[0] * 32 + intspec[2]; 1188 + *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; 1189 + 1190 + parent_intc = intc->parent; 1191 + if (parent_intc) { 1192 + irq_data = &intc->irqs[intspec[2]]; 1193 + irq_data->parent_irq = intspec[1]; 1194 + parent_irq_data = &parent_intc->irqs[irq_data->parent_irq]; 1195 + parent_irq_data->sub_intc = intc; 1196 + parent_irq_data->sub_bits |= (1UL << intspec[2]); 1197 + 1198 + /* parent_intc is always s3c_intc[0], so no offset */ 1199 + irqno = irq_create_mapping(parent_intc->domain, intspec[1]); 1200 + if (irqno < 0) { 1201 + pr_err("irq: could not map parent interrupt\n"); 1202 + return irqno; 1203 + } 1204 + 1205 + irq_set_chained_handler(irqno, s3c_irq_demux); 1206 + } 1207 + 1208 + return 0; 1209 + } 1210 + 1211 + static struct irq_domain_ops s3c24xx_irq_ops_of = { 1212 + .map = s3c24xx_irq_map_of, 1213 + .xlate = s3c24xx_irq_xlate_of, 1214 + }; 1215 + 1216 + struct s3c24xx_irq_of_ctrl { 1217 + char *name; 1218 + unsigned long offset; 1219 + struct s3c_irq_intc **handle; 1220 + struct s3c_irq_intc **parent; 1221 + struct irq_domain_ops *ops; 1222 + }; 1223 + 1224 + static int __init s3c_init_intc_of(struct device_node *np, 1225 + struct device_node *interrupt_parent, 1226 + struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl) 1227 + { 1228 + struct s3c_irq_intc *intc; 1229 + struct s3c24xx_irq_of_ctrl *ctrl; 1230 + struct irq_domain *domain; 1231 + void __iomem *reg_base; 1232 + int i; 1233 + 1234 + reg_base = of_iomap(np, 0); 1235 + if (!reg_base) { 1236 + pr_err("irq-s3c24xx: could not map irq registers\n"); 1237 + return -EINVAL; 1238 + } 1239 + 1240 + domain = irq_domain_add_linear(np, num_ctrl * 32, 1241 + &s3c24xx_irq_ops_of, NULL); 1242 + if (!domain) { 1243 + pr_err("irq: could not create irq-domain\n"); 1244 + return -EINVAL; 1245 + } 1246 + 1247 + for (i = 0; i < num_ctrl; i++) { 1248 + ctrl = &s3c_ctrl[i]; 1249 + 1250 + pr_debug("irq: found controller %s\n", ctrl->name); 1251 + 1252 + intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL); 1253 + if (!intc) 1254 + return -ENOMEM; 1255 + 1256 + intc->domain = domain; 1257 + intc->irqs = kzalloc(sizeof(struct s3c_irq_data) * 32, 1258 + GFP_KERNEL); 1259 + if (!intc->irqs) { 1260 + kfree(intc); 1261 + return -ENOMEM; 1262 + } 1263 + 1264 + if (ctrl->parent) { 1265 + intc->reg_pending = reg_base + ctrl->offset; 1266 + intc->reg_mask = reg_base + ctrl->offset + 0x4; 1267 + 1268 + if (*(ctrl->parent)) { 1269 + intc->parent = *(ctrl->parent); 1270 + } else { 1271 + pr_warn("irq: parent of %s missing\n", 1272 + ctrl->name); 1273 + kfree(intc->irqs); 1274 + kfree(intc); 1275 + continue; 1276 + } 1277 + } else { 1278 + intc->reg_pending = reg_base + ctrl->offset; 1279 + intc->reg_mask = reg_base + ctrl->offset + 0x08; 1280 + intc->reg_intpnd = reg_base + ctrl->offset + 0x10; 1281 + } 1282 + 1283 + s3c24xx_clear_intc(intc); 1284 + s3c_intc[i] = intc; 1285 + } 1286 + 1287 + set_handle_irq(s3c24xx_handle_irq); 1288 + 1289 + return 0; 1290 + } 1291 + 1292 + static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = { 1293 + { 1294 + .name = "intc", 1295 + .offset = 0, 1296 + }, { 1297 + .name = "subintc", 1298 + .offset = 0x18, 1299 + .parent = &s3c_intc[0], 1300 + } 1301 + }; 1302 + 1303 + int __init s3c2410_init_intc_of(struct device_node *np, 1304 + struct device_node *interrupt_parent, 1305 + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) 1306 + { 1307 + return s3c_init_intc_of(np, interrupt_parent, 1308 + s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl)); 1309 + } 1310 + IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of); 1311 + 1312 + static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = { 1313 + { 1314 + .name = "intc", 1315 + .offset = 0, 1316 + }, { 1317 + .name = "subintc", 1318 + .offset = 0x18, 1319 + .parent = &s3c_intc[0], 1320 + }, { 1321 + .name = "intc2", 1322 + .offset = 0x40, 1323 + } 1324 + }; 1325 + 1326 + int __init s3c2416_init_intc_of(struct device_node *np, 1327 + struct device_node *interrupt_parent, 1328 + struct s3c24xx_irq_of_ctrl *ctrl, int num_ctrl) 1329 + { 1330 + return s3c_init_intc_of(np, interrupt_parent, 1331 + s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl)); 1332 + } 1333 + IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of); 1157 1334 #endif