diff options
Diffstat (limited to 'arch/mips/cavium-octeon/octeon-irq.c')
-rw-r--r-- | arch/mips/cavium-octeon/octeon-irq.c | 568 |
1 files changed, 544 insertions, 24 deletions
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c index 274cd4fad30c..aba085b2c0d5 100644 --- a/arch/mips/cavium-octeon/octeon-irq.c +++ b/arch/mips/cavium-octeon/octeon-irq.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/of.h> | 16 | #include <linux/of.h> |
17 | 17 | ||
18 | #include <asm/octeon/octeon.h> | 18 | #include <asm/octeon/octeon.h> |
19 | #include <asm/octeon/cvmx-ciu2-defs.h> | ||
19 | 20 | ||
20 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock); | 21 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock); |
21 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock); | 22 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock); |
@@ -29,8 +30,9 @@ union octeon_ciu_chip_data { | |||
29 | void *p; | 30 | void *p; |
30 | unsigned long l; | 31 | unsigned long l; |
31 | struct { | 32 | struct { |
32 | unsigned int line:6; | 33 | unsigned long line:6; |
33 | unsigned int bit:6; | 34 | unsigned long bit:6; |
35 | unsigned long gpio_line:6; | ||
34 | } s; | 36 | } s; |
35 | }; | 37 | }; |
36 | 38 | ||
@@ -45,7 +47,7 @@ struct octeon_core_chip_data { | |||
45 | 47 | ||
46 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; | 48 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; |
47 | 49 | ||
48 | static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, | 50 | static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, int gpio_line, |
49 | struct irq_chip *chip, | 51 | struct irq_chip *chip, |
50 | irq_flow_handler_t handler) | 52 | irq_flow_handler_t handler) |
51 | { | 53 | { |
@@ -56,6 +58,7 @@ static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, | |||
56 | cd.l = 0; | 58 | cd.l = 0; |
57 | cd.s.line = line; | 59 | cd.s.line = line; |
58 | cd.s.bit = bit; | 60 | cd.s.bit = bit; |
61 | cd.s.gpio_line = gpio_line; | ||
59 | 62 | ||
60 | irq_set_chip_data(irq, cd.p); | 63 | irq_set_chip_data(irq, cd.p); |
61 | octeon_irq_ciu_to_irq[line][bit] = irq; | 64 | octeon_irq_ciu_to_irq[line][bit] = irq; |
@@ -435,7 +438,7 @@ static void octeon_irq_ciu_ack(struct irq_data *data) | |||
435 | u64 mask; | 438 | u64 mask; |
436 | union octeon_ciu_chip_data cd; | 439 | union octeon_ciu_chip_data cd; |
437 | 440 | ||
438 | cd.p = data->chip_data; | 441 | cd.p = irq_data_get_irq_chip_data(data); |
439 | mask = 1ull << (cd.s.bit); | 442 | mask = 1ull << (cd.s.bit); |
440 | 443 | ||
441 | if (cd.s.line == 0) { | 444 | if (cd.s.line == 0) { |
@@ -458,7 +461,7 @@ static void octeon_irq_ciu_disable_all_v2(struct irq_data *data) | |||
458 | 461 | ||
459 | wmb(); /* Make sure flag changes arrive before register updates. */ | 462 | wmb(); /* Make sure flag changes arrive before register updates. */ |
460 | 463 | ||
461 | cd.p = data->chip_data; | 464 | cd.p = irq_data_get_irq_chip_data(data); |
462 | mask = 1ull << (cd.s.bit); | 465 | mask = 1ull << (cd.s.bit); |
463 | 466 | ||
464 | if (cd.s.line == 0) { | 467 | if (cd.s.line == 0) { |
@@ -486,7 +489,7 @@ static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) | |||
486 | u64 mask; | 489 | u64 mask; |
487 | union octeon_ciu_chip_data cd; | 490 | union octeon_ciu_chip_data cd; |
488 | 491 | ||
489 | cd.p = data->chip_data; | 492 | cd.p = irq_data_get_irq_chip_data(data); |
490 | mask = 1ull << (cd.s.bit); | 493 | mask = 1ull << (cd.s.bit); |
491 | 494 | ||
492 | if (cd.s.line == 0) { | 495 | if (cd.s.line == 0) { |
@@ -521,7 +524,7 @@ static void octeon_irq_gpio_setup(struct irq_data *data) | |||
521 | cfg.s.fil_cnt = 7; | 524 | cfg.s.fil_cnt = 7; |
522 | cfg.s.fil_sel = 3; | 525 | cfg.s.fil_sel = 3; |
523 | 526 | ||
524 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), cfg.u64); | 527 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), cfg.u64); |
525 | } | 528 | } |
526 | 529 | ||
527 | static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data) | 530 | static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data) |
@@ -549,7 +552,7 @@ static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data) | |||
549 | union octeon_ciu_chip_data cd; | 552 | union octeon_ciu_chip_data cd; |
550 | 553 | ||
551 | cd.p = irq_data_get_irq_chip_data(data); | 554 | cd.p = irq_data_get_irq_chip_data(data); |
552 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0); | 555 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); |
553 | 556 | ||
554 | octeon_irq_ciu_disable_all_v2(data); | 557 | octeon_irq_ciu_disable_all_v2(data); |
555 | } | 558 | } |
@@ -559,7 +562,7 @@ static void octeon_irq_ciu_disable_gpio(struct irq_data *data) | |||
559 | union octeon_ciu_chip_data cd; | 562 | union octeon_ciu_chip_data cd; |
560 | 563 | ||
561 | cd.p = irq_data_get_irq_chip_data(data); | 564 | cd.p = irq_data_get_irq_chip_data(data); |
562 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0); | 565 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); |
563 | 566 | ||
564 | octeon_irq_ciu_disable_all(data); | 567 | octeon_irq_ciu_disable_all(data); |
565 | } | 568 | } |
@@ -570,7 +573,7 @@ static void octeon_irq_ciu_gpio_ack(struct irq_data *data) | |||
570 | u64 mask; | 573 | u64 mask; |
571 | 574 | ||
572 | cd.p = irq_data_get_irq_chip_data(data); | 575 | cd.p = irq_data_get_irq_chip_data(data); |
573 | mask = 1ull << (cd.s.bit - 16); | 576 | mask = 1ull << (cd.s.gpio_line); |
574 | 577 | ||
575 | cvmx_write_csr(CVMX_GPIO_INT_CLR, mask); | 578 | cvmx_write_csr(CVMX_GPIO_INT_CLR, mask); |
576 | } | 579 | } |
@@ -616,7 +619,7 @@ static int octeon_irq_ciu_set_affinity(struct irq_data *data, | |||
616 | unsigned long flags; | 619 | unsigned long flags; |
617 | union octeon_ciu_chip_data cd; | 620 | union octeon_ciu_chip_data cd; |
618 | 621 | ||
619 | cd.p = data->chip_data; | 622 | cd.p = irq_data_get_irq_chip_data(data); |
620 | 623 | ||
621 | /* | 624 | /* |
622 | * For non-v2 CIU, we will allow only single CPU affinity. | 625 | * For non-v2 CIU, we will allow only single CPU affinity. |
@@ -679,7 +682,7 @@ static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, | |||
679 | if (!enable_one) | 682 | if (!enable_one) |
680 | return 0; | 683 | return 0; |
681 | 684 | ||
682 | cd.p = data->chip_data; | 685 | cd.p = irq_data_get_irq_chip_data(data); |
683 | mask = 1ull << cd.s.bit; | 686 | mask = 1ull << cd.s.bit; |
684 | 687 | ||
685 | if (cd.s.line == 0) { | 688 | if (cd.s.line == 0) { |
@@ -976,19 +979,20 @@ static int octeon_irq_ciu_map(struct irq_domain *d, | |||
976 | return -EINVAL; | 979 | return -EINVAL; |
977 | 980 | ||
978 | if (octeon_irq_ciu_is_edge(line, bit)) | 981 | if (octeon_irq_ciu_is_edge(line, bit)) |
979 | octeon_irq_set_ciu_mapping(virq, line, bit, | 982 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, |
980 | octeon_irq_ciu_chip, | 983 | octeon_irq_ciu_chip, |
981 | handle_edge_irq); | 984 | handle_edge_irq); |
982 | else | 985 | else |
983 | octeon_irq_set_ciu_mapping(virq, line, bit, | 986 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, |
984 | octeon_irq_ciu_chip, | 987 | octeon_irq_ciu_chip, |
985 | handle_level_irq); | 988 | handle_level_irq); |
986 | 989 | ||
987 | return 0; | 990 | return 0; |
988 | } | 991 | } |
989 | 992 | ||
990 | static int octeon_irq_gpio_map(struct irq_domain *d, | 993 | static int octeon_irq_gpio_map_common(struct irq_domain *d, |
991 | unsigned int virq, irq_hw_number_t hw) | 994 | unsigned int virq, irq_hw_number_t hw, |
995 | int line_limit, struct irq_chip *chip) | ||
992 | { | 996 | { |
993 | struct octeon_irq_gpio_domain_data *gpiod = d->host_data; | 997 | struct octeon_irq_gpio_domain_data *gpiod = d->host_data; |
994 | unsigned int line, bit; | 998 | unsigned int line, bit; |
@@ -999,15 +1003,20 @@ static int octeon_irq_gpio_map(struct irq_domain *d, | |||
999 | hw += gpiod->base_hwirq; | 1003 | hw += gpiod->base_hwirq; |
1000 | line = hw >> 6; | 1004 | line = hw >> 6; |
1001 | bit = hw & 63; | 1005 | bit = hw & 63; |
1002 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | 1006 | if (line > line_limit || octeon_irq_ciu_to_irq[line][bit] != 0) |
1003 | return -EINVAL; | 1007 | return -EINVAL; |
1004 | 1008 | ||
1005 | octeon_irq_set_ciu_mapping(virq, line, bit, | 1009 | octeon_irq_set_ciu_mapping(virq, line, bit, hw, |
1006 | octeon_irq_gpio_chip, | 1010 | chip, octeon_irq_handle_gpio); |
1007 | octeon_irq_handle_gpio); | ||
1008 | return 0; | 1011 | return 0; |
1009 | } | 1012 | } |
1010 | 1013 | ||
1014 | static int octeon_irq_gpio_map(struct irq_domain *d, | ||
1015 | unsigned int virq, irq_hw_number_t hw) | ||
1016 | { | ||
1017 | return octeon_irq_gpio_map_common(d, virq, hw, 1, octeon_irq_gpio_chip); | ||
1018 | } | ||
1019 | |||
1011 | static struct irq_domain_ops octeon_irq_domain_ciu_ops = { | 1020 | static struct irq_domain_ops octeon_irq_domain_ciu_ops = { |
1012 | .map = octeon_irq_ciu_map, | 1021 | .map = octeon_irq_ciu_map, |
1013 | .xlate = octeon_irq_ciu_xlat, | 1022 | .xlate = octeon_irq_ciu_xlat, |
@@ -1091,6 +1100,13 @@ static void octeon_irq_ip3_v2(void) | |||
1091 | } | 1100 | } |
1092 | } | 1101 | } |
1093 | 1102 | ||
1103 | static bool octeon_irq_use_ip4; | ||
1104 | |||
1105 | static void __cpuinit octeon_irq_local_enable_ip4(void *arg) | ||
1106 | { | ||
1107 | set_c0_status(STATUSF_IP4); | ||
1108 | } | ||
1109 | |||
1094 | static void octeon_irq_ip4_mask(void) | 1110 | static void octeon_irq_ip4_mask(void) |
1095 | { | 1111 | { |
1096 | clear_c0_status(STATUSF_IP4); | 1112 | clear_c0_status(STATUSF_IP4); |
@@ -1103,6 +1119,13 @@ static void (*octeon_irq_ip4)(void); | |||
1103 | 1119 | ||
1104 | void __cpuinitdata (*octeon_irq_setup_secondary)(void); | 1120 | void __cpuinitdata (*octeon_irq_setup_secondary)(void); |
1105 | 1121 | ||
1122 | void __cpuinit octeon_irq_set_ip4_handler(octeon_irq_ip4_handler_t h) | ||
1123 | { | ||
1124 | octeon_irq_ip4 = h; | ||
1125 | octeon_irq_use_ip4 = true; | ||
1126 | on_each_cpu(octeon_irq_local_enable_ip4, NULL, 1); | ||
1127 | } | ||
1128 | |||
1106 | static void __cpuinit octeon_irq_percpu_enable(void) | 1129 | static void __cpuinit octeon_irq_percpu_enable(void) |
1107 | { | 1130 | { |
1108 | irq_cpu_online(); | 1131 | irq_cpu_online(); |
@@ -1123,6 +1146,28 @@ static void __cpuinit octeon_irq_init_ciu_percpu(void) | |||
1123 | cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); | 1146 | cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); |
1124 | } | 1147 | } |
1125 | 1148 | ||
1149 | static void octeon_irq_init_ciu2_percpu(void) | ||
1150 | { | ||
1151 | u64 regx, ipx; | ||
1152 | int coreid = cvmx_get_core_num(); | ||
1153 | u64 base = CVMX_CIU2_EN_PPX_IP2_WRKQ(coreid); | ||
1154 | |||
1155 | /* | ||
1156 | * Disable All CIU2 Interrupts. The ones we need will be | ||
1157 | * enabled later. Read the SUM register so we know the write | ||
1158 | * completed. | ||
1159 | * | ||
1160 | * There are 9 registers and 3 IPX levels with strides 0x1000 | ||
1161 | * and 0x200 respectivly. Use loops to clear them. | ||
1162 | */ | ||
1163 | for (regx = 0; regx <= 0x8000; regx += 0x1000) { | ||
1164 | for (ipx = 0; ipx <= 0x400; ipx += 0x200) | ||
1165 | cvmx_write_csr(base + regx + ipx, 0); | ||
1166 | } | ||
1167 | |||
1168 | cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid)); | ||
1169 | } | ||
1170 | |||
1126 | static void __cpuinit octeon_irq_setup_secondary_ciu(void) | 1171 | static void __cpuinit octeon_irq_setup_secondary_ciu(void) |
1127 | { | 1172 | { |
1128 | 1173 | ||
@@ -1137,6 +1182,19 @@ static void __cpuinit octeon_irq_setup_secondary_ciu(void) | |||
1137 | clear_c0_status(STATUSF_IP4); | 1182 | clear_c0_status(STATUSF_IP4); |
1138 | } | 1183 | } |
1139 | 1184 | ||
1185 | static void octeon_irq_setup_secondary_ciu2(void) | ||
1186 | { | ||
1187 | octeon_irq_init_ciu2_percpu(); | ||
1188 | octeon_irq_percpu_enable(); | ||
1189 | |||
1190 | /* Enable the CIU lines */ | ||
1191 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | ||
1192 | if (octeon_irq_use_ip4) | ||
1193 | set_c0_status(STATUSF_IP4); | ||
1194 | else | ||
1195 | clear_c0_status(STATUSF_IP4); | ||
1196 | } | ||
1197 | |||
1140 | static void __init octeon_irq_init_ciu(void) | 1198 | static void __init octeon_irq_init_ciu(void) |
1141 | { | 1199 | { |
1142 | unsigned int i; | 1200 | unsigned int i; |
@@ -1200,8 +1258,8 @@ static void __init octeon_irq_init_ciu(void) | |||
1200 | for (i = 0; i < 16; i++) | 1258 | for (i = 0; i < 16; i++) |
1201 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0); | 1259 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0); |
1202 | 1260 | ||
1203 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); | 1261 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, 0, chip_mbox, handle_percpu_irq); |
1204 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); | 1262 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, 0, chip_mbox, handle_percpu_irq); |
1205 | 1263 | ||
1206 | for (i = 0; i < 4; i++) | 1264 | for (i = 0; i < 4; i++) |
1207 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36); | 1265 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36); |
@@ -1217,7 +1275,7 @@ static void __init octeon_irq_init_ciu(void) | |||
1217 | 1275 | ||
1218 | /* CIU_1 */ | 1276 | /* CIU_1 */ |
1219 | for (i = 0; i < 16; i++) | 1277 | for (i = 0; i < 16; i++) |
1220 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq); | 1278 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, 0, chip_wd, handle_level_irq); |
1221 | 1279 | ||
1222 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17); | 1280 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17); |
1223 | 1281 | ||
@@ -1226,6 +1284,465 @@ static void __init octeon_irq_init_ciu(void) | |||
1226 | clear_c0_status(STATUSF_IP4); | 1284 | clear_c0_status(STATUSF_IP4); |
1227 | } | 1285 | } |
1228 | 1286 | ||
1287 | /* | ||
1288 | * Watchdog interrupts are special. They are associated with a single | ||
1289 | * core, so we hardwire the affinity to that core. | ||
1290 | */ | ||
1291 | static void octeon_irq_ciu2_wd_enable(struct irq_data *data) | ||
1292 | { | ||
1293 | u64 mask; | ||
1294 | u64 en_addr; | ||
1295 | int coreid = data->irq - OCTEON_IRQ_WDOG0; | ||
1296 | union octeon_ciu_chip_data cd; | ||
1297 | |||
1298 | cd.p = irq_data_get_irq_chip_data(data); | ||
1299 | mask = 1ull << (cd.s.bit); | ||
1300 | |||
1301 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | ||
1302 | cvmx_write_csr(en_addr, mask); | ||
1303 | |||
1304 | } | ||
1305 | |||
1306 | static void octeon_irq_ciu2_enable(struct irq_data *data) | ||
1307 | { | ||
1308 | u64 mask; | ||
1309 | u64 en_addr; | ||
1310 | int cpu = next_cpu_for_irq(data); | ||
1311 | int coreid = octeon_coreid_for_cpu(cpu); | ||
1312 | union octeon_ciu_chip_data cd; | ||
1313 | |||
1314 | cd.p = irq_data_get_irq_chip_data(data); | ||
1315 | mask = 1ull << (cd.s.bit); | ||
1316 | |||
1317 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | ||
1318 | cvmx_write_csr(en_addr, mask); | ||
1319 | } | ||
1320 | |||
1321 | static void octeon_irq_ciu2_enable_local(struct irq_data *data) | ||
1322 | { | ||
1323 | u64 mask; | ||
1324 | u64 en_addr; | ||
1325 | int coreid = cvmx_get_core_num(); | ||
1326 | union octeon_ciu_chip_data cd; | ||
1327 | |||
1328 | cd.p = irq_data_get_irq_chip_data(data); | ||
1329 | mask = 1ull << (cd.s.bit); | ||
1330 | |||
1331 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | ||
1332 | cvmx_write_csr(en_addr, mask); | ||
1333 | |||
1334 | } | ||
1335 | |||
1336 | static void octeon_irq_ciu2_disable_local(struct irq_data *data) | ||
1337 | { | ||
1338 | u64 mask; | ||
1339 | u64 en_addr; | ||
1340 | int coreid = cvmx_get_core_num(); | ||
1341 | union octeon_ciu_chip_data cd; | ||
1342 | |||
1343 | cd.p = irq_data_get_irq_chip_data(data); | ||
1344 | mask = 1ull << (cd.s.bit); | ||
1345 | |||
1346 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(coreid) + (0x1000ull * cd.s.line); | ||
1347 | cvmx_write_csr(en_addr, mask); | ||
1348 | |||
1349 | } | ||
1350 | |||
1351 | static void octeon_irq_ciu2_ack(struct irq_data *data) | ||
1352 | { | ||
1353 | u64 mask; | ||
1354 | u64 en_addr; | ||
1355 | int coreid = cvmx_get_core_num(); | ||
1356 | union octeon_ciu_chip_data cd; | ||
1357 | |||
1358 | cd.p = irq_data_get_irq_chip_data(data); | ||
1359 | mask = 1ull << (cd.s.bit); | ||
1360 | |||
1361 | en_addr = CVMX_CIU2_RAW_PPX_IP2_WRKQ(coreid) + (0x1000ull * cd.s.line); | ||
1362 | cvmx_write_csr(en_addr, mask); | ||
1363 | |||
1364 | } | ||
1365 | |||
1366 | static void octeon_irq_ciu2_disable_all(struct irq_data *data) | ||
1367 | { | ||
1368 | int cpu; | ||
1369 | u64 mask; | ||
1370 | union octeon_ciu_chip_data cd; | ||
1371 | |||
1372 | cd.p = irq_data_get_irq_chip_data(data); | ||
1373 | mask = 1ull << (cd.s.bit); | ||
1374 | |||
1375 | for_each_online_cpu(cpu) { | ||
1376 | u64 en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | ||
1377 | cvmx_write_csr(en_addr, mask); | ||
1378 | } | ||
1379 | } | ||
1380 | |||
1381 | static void octeon_irq_ciu2_mbox_enable_all(struct irq_data *data) | ||
1382 | { | ||
1383 | int cpu; | ||
1384 | u64 mask; | ||
1385 | |||
1386 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | ||
1387 | |||
1388 | for_each_online_cpu(cpu) { | ||
1389 | u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(octeon_coreid_for_cpu(cpu)); | ||
1390 | cvmx_write_csr(en_addr, mask); | ||
1391 | } | ||
1392 | } | ||
1393 | |||
1394 | static void octeon_irq_ciu2_mbox_disable_all(struct irq_data *data) | ||
1395 | { | ||
1396 | int cpu; | ||
1397 | u64 mask; | ||
1398 | |||
1399 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | ||
1400 | |||
1401 | for_each_online_cpu(cpu) { | ||
1402 | u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(octeon_coreid_for_cpu(cpu)); | ||
1403 | cvmx_write_csr(en_addr, mask); | ||
1404 | } | ||
1405 | } | ||
1406 | |||
1407 | static void octeon_irq_ciu2_mbox_enable_local(struct irq_data *data) | ||
1408 | { | ||
1409 | u64 mask; | ||
1410 | u64 en_addr; | ||
1411 | int coreid = cvmx_get_core_num(); | ||
1412 | |||
1413 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | ||
1414 | en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(coreid); | ||
1415 | cvmx_write_csr(en_addr, mask); | ||
1416 | } | ||
1417 | |||
1418 | static void octeon_irq_ciu2_mbox_disable_local(struct irq_data *data) | ||
1419 | { | ||
1420 | u64 mask; | ||
1421 | u64 en_addr; | ||
1422 | int coreid = cvmx_get_core_num(); | ||
1423 | |||
1424 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | ||
1425 | en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(coreid); | ||
1426 | cvmx_write_csr(en_addr, mask); | ||
1427 | } | ||
1428 | |||
1429 | #ifdef CONFIG_SMP | ||
1430 | static int octeon_irq_ciu2_set_affinity(struct irq_data *data, | ||
1431 | const struct cpumask *dest, bool force) | ||
1432 | { | ||
1433 | int cpu; | ||
1434 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); | ||
1435 | u64 mask; | ||
1436 | union octeon_ciu_chip_data cd; | ||
1437 | |||
1438 | if (!enable_one) | ||
1439 | return 0; | ||
1440 | |||
1441 | cd.p = irq_data_get_irq_chip_data(data); | ||
1442 | mask = 1ull << cd.s.bit; | ||
1443 | |||
1444 | for_each_online_cpu(cpu) { | ||
1445 | u64 en_addr; | ||
1446 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | ||
1447 | enable_one = false; | ||
1448 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | ||
1449 | } else { | ||
1450 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | ||
1451 | } | ||
1452 | cvmx_write_csr(en_addr, mask); | ||
1453 | } | ||
1454 | |||
1455 | return 0; | ||
1456 | } | ||
1457 | #endif | ||
1458 | |||
1459 | static void octeon_irq_ciu2_enable_gpio(struct irq_data *data) | ||
1460 | { | ||
1461 | octeon_irq_gpio_setup(data); | ||
1462 | octeon_irq_ciu2_enable(data); | ||
1463 | } | ||
1464 | |||
1465 | static void octeon_irq_ciu2_disable_gpio(struct irq_data *data) | ||
1466 | { | ||
1467 | union octeon_ciu_chip_data cd; | ||
1468 | cd.p = irq_data_get_irq_chip_data(data); | ||
1469 | |||
1470 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); | ||
1471 | |||
1472 | octeon_irq_ciu2_disable_all(data); | ||
1473 | } | ||
1474 | |||
1475 | static struct irq_chip octeon_irq_chip_ciu2 = { | ||
1476 | .name = "CIU2-E", | ||
1477 | .irq_enable = octeon_irq_ciu2_enable, | ||
1478 | .irq_disable = octeon_irq_ciu2_disable_all, | ||
1479 | .irq_ack = octeon_irq_ciu2_ack, | ||
1480 | .irq_mask = octeon_irq_ciu2_disable_local, | ||
1481 | .irq_unmask = octeon_irq_ciu2_enable, | ||
1482 | #ifdef CONFIG_SMP | ||
1483 | .irq_set_affinity = octeon_irq_ciu2_set_affinity, | ||
1484 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | ||
1485 | #endif | ||
1486 | }; | ||
1487 | |||
1488 | static struct irq_chip octeon_irq_chip_ciu2_mbox = { | ||
1489 | .name = "CIU2-M", | ||
1490 | .irq_enable = octeon_irq_ciu2_mbox_enable_all, | ||
1491 | .irq_disable = octeon_irq_ciu2_mbox_disable_all, | ||
1492 | .irq_ack = octeon_irq_ciu2_mbox_disable_local, | ||
1493 | .irq_eoi = octeon_irq_ciu2_mbox_enable_local, | ||
1494 | |||
1495 | .irq_cpu_online = octeon_irq_ciu2_mbox_enable_local, | ||
1496 | .irq_cpu_offline = octeon_irq_ciu2_mbox_disable_local, | ||
1497 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | ||
1498 | }; | ||
1499 | |||
1500 | static struct irq_chip octeon_irq_chip_ciu2_wd = { | ||
1501 | .name = "CIU2-W", | ||
1502 | .irq_enable = octeon_irq_ciu2_wd_enable, | ||
1503 | .irq_disable = octeon_irq_ciu2_disable_all, | ||
1504 | .irq_mask = octeon_irq_ciu2_disable_local, | ||
1505 | .irq_unmask = octeon_irq_ciu2_enable_local, | ||
1506 | }; | ||
1507 | |||
1508 | static struct irq_chip octeon_irq_chip_ciu2_gpio = { | ||
1509 | .name = "CIU-GPIO", | ||
1510 | .irq_enable = octeon_irq_ciu2_enable_gpio, | ||
1511 | .irq_disable = octeon_irq_ciu2_disable_gpio, | ||
1512 | .irq_ack = octeon_irq_ciu_gpio_ack, | ||
1513 | .irq_mask = octeon_irq_ciu2_disable_local, | ||
1514 | .irq_unmask = octeon_irq_ciu2_enable, | ||
1515 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | ||
1516 | #ifdef CONFIG_SMP | ||
1517 | .irq_set_affinity = octeon_irq_ciu2_set_affinity, | ||
1518 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | ||
1519 | #endif | ||
1520 | .flags = IRQCHIP_SET_TYPE_MASKED, | ||
1521 | }; | ||
1522 | |||
1523 | static int octeon_irq_ciu2_xlat(struct irq_domain *d, | ||
1524 | struct device_node *node, | ||
1525 | const u32 *intspec, | ||
1526 | unsigned int intsize, | ||
1527 | unsigned long *out_hwirq, | ||
1528 | unsigned int *out_type) | ||
1529 | { | ||
1530 | unsigned int ciu, bit; | ||
1531 | |||
1532 | ciu = intspec[0]; | ||
1533 | bit = intspec[1]; | ||
1534 | |||
1535 | /* Line 7 are the GPIO lines */ | ||
1536 | if (ciu > 6 || bit > 63) | ||
1537 | return -EINVAL; | ||
1538 | |||
1539 | *out_hwirq = (ciu << 6) | bit; | ||
1540 | *out_type = 0; | ||
1541 | |||
1542 | return 0; | ||
1543 | } | ||
1544 | |||
1545 | static bool octeon_irq_ciu2_is_edge(unsigned int line, unsigned int bit) | ||
1546 | { | ||
1547 | bool edge = false; | ||
1548 | |||
1549 | if (line == 3) /* MIO */ | ||
1550 | switch (bit) { | ||
1551 | case 2: /* IPD_DRP */ | ||
1552 | case 8 ... 11: /* Timers */ | ||
1553 | case 48: /* PTP */ | ||
1554 | edge = true; | ||
1555 | break; | ||
1556 | default: | ||
1557 | break; | ||
1558 | } | ||
1559 | else if (line == 6) /* PKT */ | ||
1560 | switch (bit) { | ||
1561 | case 52 ... 53: /* ILK_DRP */ | ||
1562 | case 8 ... 12: /* GMX_DRP */ | ||
1563 | edge = true; | ||
1564 | break; | ||
1565 | default: | ||
1566 | break; | ||
1567 | } | ||
1568 | return edge; | ||
1569 | } | ||
1570 | |||
1571 | static int octeon_irq_ciu2_map(struct irq_domain *d, | ||
1572 | unsigned int virq, irq_hw_number_t hw) | ||
1573 | { | ||
1574 | unsigned int line = hw >> 6; | ||
1575 | unsigned int bit = hw & 63; | ||
1576 | |||
1577 | if (!octeon_irq_virq_in_range(virq)) | ||
1578 | return -EINVAL; | ||
1579 | |||
1580 | /* Line 7 are the GPIO lines */ | ||
1581 | if (line > 6 || octeon_irq_ciu_to_irq[line][bit] != 0) | ||
1582 | return -EINVAL; | ||
1583 | |||
1584 | if (octeon_irq_ciu2_is_edge(line, bit)) | ||
1585 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, | ||
1586 | &octeon_irq_chip_ciu2, | ||
1587 | handle_edge_irq); | ||
1588 | else | ||
1589 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, | ||
1590 | &octeon_irq_chip_ciu2, | ||
1591 | handle_level_irq); | ||
1592 | |||
1593 | return 0; | ||
1594 | } | ||
1595 | static int octeon_irq_ciu2_gpio_map(struct irq_domain *d, | ||
1596 | unsigned int virq, irq_hw_number_t hw) | ||
1597 | { | ||
1598 | return octeon_irq_gpio_map_common(d, virq, hw, 7, &octeon_irq_chip_ciu2_gpio); | ||
1599 | } | ||
1600 | |||
1601 | static struct irq_domain_ops octeon_irq_domain_ciu2_ops = { | ||
1602 | .map = octeon_irq_ciu2_map, | ||
1603 | .xlate = octeon_irq_ciu2_xlat, | ||
1604 | }; | ||
1605 | |||
1606 | static struct irq_domain_ops octeon_irq_domain_ciu2_gpio_ops = { | ||
1607 | .map = octeon_irq_ciu2_gpio_map, | ||
1608 | .xlate = octeon_irq_gpio_xlat, | ||
1609 | }; | ||
1610 | |||
1611 | static void octeon_irq_ciu2(void) | ||
1612 | { | ||
1613 | int line; | ||
1614 | int bit; | ||
1615 | int irq; | ||
1616 | u64 src_reg, src, sum; | ||
1617 | const unsigned long core_id = cvmx_get_core_num(); | ||
1618 | |||
1619 | sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core_id)) & 0xfful; | ||
1620 | |||
1621 | if (unlikely(!sum)) | ||
1622 | goto spurious; | ||
1623 | |||
1624 | line = fls64(sum) - 1; | ||
1625 | src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core_id) + (0x1000 * line); | ||
1626 | src = cvmx_read_csr(src_reg); | ||
1627 | |||
1628 | if (unlikely(!src)) | ||
1629 | goto spurious; | ||
1630 | |||
1631 | bit = fls64(src) - 1; | ||
1632 | irq = octeon_irq_ciu_to_irq[line][bit]; | ||
1633 | if (unlikely(!irq)) | ||
1634 | goto spurious; | ||
1635 | |||
1636 | do_IRQ(irq); | ||
1637 | goto out; | ||
1638 | |||
1639 | spurious: | ||
1640 | spurious_interrupt(); | ||
1641 | out: | ||
1642 | /* CN68XX pass 1.x has an errata that accessing the ACK registers | ||
1643 | can stop interrupts from propagating */ | ||
1644 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | ||
1645 | cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY); | ||
1646 | else | ||
1647 | cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core_id)); | ||
1648 | return; | ||
1649 | } | ||
1650 | |||
1651 | static void octeon_irq_ciu2_mbox(void) | ||
1652 | { | ||
1653 | int line; | ||
1654 | |||
1655 | const unsigned long core_id = cvmx_get_core_num(); | ||
1656 | u64 sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP3(core_id)) >> 60; | ||
1657 | |||
1658 | if (unlikely(!sum)) | ||
1659 | goto spurious; | ||
1660 | |||
1661 | line = fls64(sum) - 1; | ||
1662 | |||
1663 | do_IRQ(OCTEON_IRQ_MBOX0 + line); | ||
1664 | goto out; | ||
1665 | |||
1666 | spurious: | ||
1667 | spurious_interrupt(); | ||
1668 | out: | ||
1669 | /* CN68XX pass 1.x has an errata that accessing the ACK registers | ||
1670 | can stop interrupts from propagating */ | ||
1671 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | ||
1672 | cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY); | ||
1673 | else | ||
1674 | cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP3(core_id)); | ||
1675 | return; | ||
1676 | } | ||
1677 | |||
1678 | static void __init octeon_irq_init_ciu2(void) | ||
1679 | { | ||
1680 | unsigned int i; | ||
1681 | struct device_node *gpio_node; | ||
1682 | struct device_node *ciu_node; | ||
1683 | struct irq_domain *ciu_domain = NULL; | ||
1684 | |||
1685 | octeon_irq_init_ciu2_percpu(); | ||
1686 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu2; | ||
1687 | |||
1688 | octeon_irq_ip2 = octeon_irq_ciu2; | ||
1689 | octeon_irq_ip3 = octeon_irq_ciu2_mbox; | ||
1690 | octeon_irq_ip4 = octeon_irq_ip4_mask; | ||
1691 | |||
1692 | /* Mips internal */ | ||
1693 | octeon_irq_init_core(); | ||
1694 | |||
1695 | gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio"); | ||
1696 | if (gpio_node) { | ||
1697 | struct octeon_irq_gpio_domain_data *gpiod; | ||
1698 | |||
1699 | gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL); | ||
1700 | if (gpiod) { | ||
1701 | /* gpio domain host_data is the base hwirq number. */ | ||
1702 | gpiod->base_hwirq = 7 << 6; | ||
1703 | irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_ciu2_gpio_ops, gpiod); | ||
1704 | of_node_put(gpio_node); | ||
1705 | } else | ||
1706 | pr_warn("Cannot allocate memory for GPIO irq_domain.\n"); | ||
1707 | } else | ||
1708 | pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n"); | ||
1709 | |||
1710 | ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-6880-ciu2"); | ||
1711 | if (ciu_node) { | ||
1712 | ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu2_ops, NULL); | ||
1713 | of_node_put(ciu_node); | ||
1714 | } else | ||
1715 | panic("Cannot find device node for cavium,octeon-6880-ciu2."); | ||
1716 | |||
1717 | /* CUI2 */ | ||
1718 | for (i = 0; i < 64; i++) | ||
1719 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i); | ||
1720 | |||
1721 | for (i = 0; i < 32; i++) | ||
1722 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i, 0, | ||
1723 | &octeon_irq_chip_ciu2_wd, handle_level_irq); | ||
1724 | |||
1725 | for (i = 0; i < 4; i++) | ||
1726 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 3, i + 8); | ||
1727 | |||
1728 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 3, 44); | ||
1729 | |||
1730 | for (i = 0; i < 4; i++) | ||
1731 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 4, i); | ||
1732 | |||
1733 | for (i = 0; i < 4; i++) | ||
1734 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 4, i + 8); | ||
1735 | |||
1736 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX0, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | ||
1737 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX1, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | ||
1738 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX2, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | ||
1739 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX3, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | ||
1740 | |||
1741 | /* Enable the CIU lines */ | ||
1742 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | ||
1743 | clear_c0_status(STATUSF_IP4); | ||
1744 | } | ||
1745 | |||
1229 | void __init arch_init_irq(void) | 1746 | void __init arch_init_irq(void) |
1230 | { | 1747 | { |
1231 | #ifdef CONFIG_SMP | 1748 | #ifdef CONFIG_SMP |
@@ -1233,7 +1750,10 @@ void __init arch_init_irq(void) | |||
1233 | cpumask_clear(irq_default_affinity); | 1750 | cpumask_clear(irq_default_affinity); |
1234 | cpumask_set_cpu(smp_processor_id(), irq_default_affinity); | 1751 | cpumask_set_cpu(smp_processor_id(), irq_default_affinity); |
1235 | #endif | 1752 | #endif |
1236 | octeon_irq_init_ciu(); | 1753 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) |
1754 | octeon_irq_init_ciu2(); | ||
1755 | else | ||
1756 | octeon_irq_init_ciu(); | ||
1237 | } | 1757 | } |
1238 | 1758 | ||
1239 | asmlinkage void plat_irq_dispatch(void) | 1759 | asmlinkage void plat_irq_dispatch(void) |