0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0049
0050 #include <linux/interrupt.h>
0051 #include <linux/watchdog.h>
0052 #include <linux/cpumask.h>
0053 #include <linux/module.h>
0054 #include <linux/delay.h>
0055 #include <linux/cpu.h>
0056 #include <linux/irq.h>
0057 #include <linux/irqdomain.h>
0058
0059 #include <asm/mipsregs.h>
0060 #include <asm/uasm.h>
0061
0062 #include <asm/octeon/octeon.h>
0063 #include <asm/octeon/cvmx-boot-vector.h>
0064 #include <asm/octeon/cvmx-ciu2-defs.h>
0065 #include <asm/octeon/cvmx-rst-defs.h>
0066
0067
0068 #define WD_BLOCK_NUMBER 0x01
0069
0070 static int divisor;
0071
0072
0073 static unsigned int timeout_cnt;
0074
0075
0076 static unsigned int max_timeout_sec;
0077
0078
0079 static unsigned int timeout_sec;
0080
0081
0082 static bool do_countdown;
0083 static unsigned int countdown_reset;
0084 static unsigned int per_cpu_countdown[NR_CPUS];
0085
0086 static cpumask_t irq_enabled_cpus;
0087
0088 #define WD_TIMO 60
0089
0090 #define CVMX_GSERX_SCRATCH(offset) (CVMX_ADD_IO_SEG(0x0001180090000020ull) + ((offset) & 15) * 0x1000000ull)
0091
0092 static int heartbeat = WD_TIMO;
0093 module_param(heartbeat, int, 0444);
0094 MODULE_PARM_DESC(heartbeat,
0095 "Watchdog heartbeat in seconds. (0 < heartbeat, default="
0096 __MODULE_STRING(WD_TIMO) ")");
0097
0098 static bool nowayout = WATCHDOG_NOWAYOUT;
0099 module_param(nowayout, bool, 0444);
0100 MODULE_PARM_DESC(nowayout,
0101 "Watchdog cannot be stopped once started (default="
0102 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0103
0104 static int disable;
0105 module_param(disable, int, 0444);
0106 MODULE_PARM_DESC(disable,
0107 "Disable the watchdog entirely (default=0)");
0108
0109 static struct cvmx_boot_vector_element *octeon_wdt_bootvector;
0110
0111 void octeon_wdt_nmi_stage2(void);
0112
0113 static int cpu2core(int cpu)
0114 {
0115 #ifdef CONFIG_SMP
0116 return cpu_logical_map(cpu) & 0x3f;
0117 #else
0118 return cvmx_get_core_num();
0119 #endif
0120 }
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130 static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id)
0131 {
0132 int cpu = raw_smp_processor_id();
0133 unsigned int core = cpu2core(cpu);
0134 int node = cpu_to_node(cpu);
0135
0136 if (do_countdown) {
0137 if (per_cpu_countdown[cpu] > 0) {
0138
0139 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1);
0140 per_cpu_countdown[cpu]--;
0141 } else {
0142
0143 disable_irq_nosync(cpl);
0144 cpumask_clear_cpu(cpu, &irq_enabled_cpus);
0145 }
0146 } else {
0147
0148 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1);
0149 }
0150 return IRQ_HANDLED;
0151 }
0152
0153
0154 extern int prom_putchar(char c);
0155
0156
0157
0158
0159
0160
0161 static void octeon_wdt_write_string(const char *str)
0162 {
0163
0164 while (*str)
0165 prom_putchar(*str++);
0166 }
0167
0168
0169
0170
0171
0172
0173
0174 static void octeon_wdt_write_hex(u64 value, int digits)
0175 {
0176 int d;
0177 int v;
0178
0179 for (d = 0; d < digits; d++) {
0180 v = (value >> ((digits - d - 1) * 4)) & 0xf;
0181 if (v >= 10)
0182 prom_putchar('a' + v - 10);
0183 else
0184 prom_putchar('0' + v);
0185 }
0186 }
0187
0188 static const char reg_name[][3] = {
0189 "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3",
0190 "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3",
0191 "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
0192 "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
0193 };
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 void octeon_wdt_nmi_stage3(u64 reg[32])
0211 {
0212 u64 i;
0213
0214 unsigned int coreid = cvmx_get_core_num();
0215
0216
0217
0218
0219 u64 cp0_cause = read_c0_cause();
0220 u64 cp0_status = read_c0_status();
0221 u64 cp0_error_epc = read_c0_errorepc();
0222 u64 cp0_epc = read_c0_epc();
0223
0224
0225 udelay(85000 * coreid);
0226
0227 octeon_wdt_write_string("\r\n*** NMI Watchdog interrupt on Core 0x");
0228 octeon_wdt_write_hex(coreid, 2);
0229 octeon_wdt_write_string(" ***\r\n");
0230 for (i = 0; i < 32; i++) {
0231 octeon_wdt_write_string("\t");
0232 octeon_wdt_write_string(reg_name[i]);
0233 octeon_wdt_write_string("\t0x");
0234 octeon_wdt_write_hex(reg[i], 16);
0235 if (i & 1)
0236 octeon_wdt_write_string("\r\n");
0237 }
0238 octeon_wdt_write_string("\terr_epc\t0x");
0239 octeon_wdt_write_hex(cp0_error_epc, 16);
0240
0241 octeon_wdt_write_string("\tepc\t0x");
0242 octeon_wdt_write_hex(cp0_epc, 16);
0243 octeon_wdt_write_string("\r\n");
0244
0245 octeon_wdt_write_string("\tstatus\t0x");
0246 octeon_wdt_write_hex(cp0_status, 16);
0247 octeon_wdt_write_string("\tcause\t0x");
0248 octeon_wdt_write_hex(cp0_cause, 16);
0249 octeon_wdt_write_string("\r\n");
0250
0251
0252 if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
0253 octeon_wdt_write_string("\tsrc_wd\t0x");
0254 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SRC_PPX_IP2_WDOG(coreid)), 16);
0255 octeon_wdt_write_string("\ten_wd\t0x");
0256 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_EN_PPX_IP2_WDOG(coreid)), 16);
0257 octeon_wdt_write_string("\r\n");
0258 octeon_wdt_write_string("\tsrc_rml\t0x");
0259 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SRC_PPX_IP2_RML(coreid)), 16);
0260 octeon_wdt_write_string("\ten_rml\t0x");
0261 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_EN_PPX_IP2_RML(coreid)), 16);
0262 octeon_wdt_write_string("\r\n");
0263 octeon_wdt_write_string("\tsum\t0x");
0264 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid)), 16);
0265 octeon_wdt_write_string("\r\n");
0266 } else if (!octeon_has_feature(OCTEON_FEATURE_CIU3)) {
0267 octeon_wdt_write_string("\tsum0\t0x");
0268 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_SUM0(coreid * 2)), 16);
0269 octeon_wdt_write_string("\ten0\t0x");
0270 octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)), 16);
0271 octeon_wdt_write_string("\r\n");
0272 }
0273
0274 octeon_wdt_write_string("*** Chip soft reset soon ***\r\n");
0275
0276
0277
0278
0279
0280 if (OCTEON_IS_OCTEON3() && !OCTEON_IS_MODEL(OCTEON_CN70XX)) {
0281 u64 scr;
0282 unsigned int node = cvmx_get_node_num();
0283 unsigned int lcore = cvmx_get_local_core_num();
0284 union cvmx_ciu_wdogx ciu_wdog;
0285
0286
0287
0288
0289
0290
0291 do {
0292 ciu_wdog.u64 = cvmx_read_csr_node(node, CVMX_CIU_WDOGX(lcore));
0293 } while (ciu_wdog.s.cnt > 0x10000);
0294
0295 scr = cvmx_read_csr_node(0, CVMX_GSERX_SCRATCH(0));
0296 scr |= 1 << 11;
0297 cvmx_write_csr_node(0, CVMX_GSERX_SCRATCH(0), scr);
0298 cvmx_write_csr_node(0, CVMX_RST_SOFT_RST, 1);
0299 }
0300 }
0301
0302 static int octeon_wdt_cpu_to_irq(int cpu)
0303 {
0304 unsigned int coreid;
0305 int node;
0306 int irq;
0307
0308 coreid = cpu2core(cpu);
0309 node = cpu_to_node(cpu);
0310
0311 if (octeon_has_feature(OCTEON_FEATURE_CIU3)) {
0312 struct irq_domain *domain;
0313 int hwirq;
0314
0315 domain = octeon_irq_get_block_domain(node,
0316 WD_BLOCK_NUMBER);
0317 hwirq = WD_BLOCK_NUMBER << 12 | 0x200 | coreid;
0318 irq = irq_find_mapping(domain, hwirq);
0319 } else {
0320 irq = OCTEON_IRQ_WDOG0 + coreid;
0321 }
0322 return irq;
0323 }
0324
0325 static int octeon_wdt_cpu_pre_down(unsigned int cpu)
0326 {
0327 unsigned int core;
0328 int node;
0329 union cvmx_ciu_wdogx ciu_wdog;
0330
0331 core = cpu2core(cpu);
0332
0333 node = cpu_to_node(cpu);
0334
0335
0336 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1);
0337
0338
0339 ciu_wdog.u64 = 0;
0340 cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64);
0341
0342 free_irq(octeon_wdt_cpu_to_irq(cpu), octeon_wdt_poke_irq);
0343 return 0;
0344 }
0345
0346 static int octeon_wdt_cpu_online(unsigned int cpu)
0347 {
0348 unsigned int core;
0349 unsigned int irq;
0350 union cvmx_ciu_wdogx ciu_wdog;
0351 int node;
0352 struct irq_domain *domain;
0353 int hwirq;
0354
0355 core = cpu2core(cpu);
0356 node = cpu_to_node(cpu);
0357
0358 octeon_wdt_bootvector[core].target_ptr = (u64)octeon_wdt_nmi_stage2;
0359
0360
0361 ciu_wdog.u64 = 0;
0362 cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64);
0363
0364 per_cpu_countdown[cpu] = countdown_reset;
0365
0366 if (octeon_has_feature(OCTEON_FEATURE_CIU3)) {
0367
0368 domain = octeon_irq_get_block_domain(node, WD_BLOCK_NUMBER);
0369
0370
0371 hwirq = WD_BLOCK_NUMBER << 12 | 0x200 | core;
0372 irq = irq_create_mapping(domain, hwirq);
0373 irqd_set_trigger_type(irq_get_irq_data(irq),
0374 IRQ_TYPE_EDGE_RISING);
0375 } else
0376 irq = OCTEON_IRQ_WDOG0 + core;
0377
0378 if (request_irq(irq, octeon_wdt_poke_irq,
0379 IRQF_NO_THREAD, "octeon_wdt", octeon_wdt_poke_irq))
0380 panic("octeon_wdt: Couldn't obtain irq %d", irq);
0381
0382
0383 if (octeon_has_feature(OCTEON_FEATURE_CIU3)) {
0384 cpumask_t mask;
0385
0386 cpumask_clear(&mask);
0387 cpumask_set_cpu(cpu, &mask);
0388 irq_set_affinity(irq, &mask);
0389 }
0390
0391 cpumask_set_cpu(cpu, &irq_enabled_cpus);
0392
0393
0394 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1);
0395
0396
0397 ciu_wdog.u64 = 0;
0398 ciu_wdog.s.len = timeout_cnt;
0399 ciu_wdog.s.mode = 3;
0400 cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64);
0401
0402 return 0;
0403 }
0404
0405 static int octeon_wdt_ping(struct watchdog_device __always_unused *wdog)
0406 {
0407 int cpu;
0408 int coreid;
0409 int node;
0410
0411 if (disable)
0412 return 0;
0413
0414 for_each_online_cpu(cpu) {
0415 coreid = cpu2core(cpu);
0416 node = cpu_to_node(cpu);
0417 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1);
0418 per_cpu_countdown[cpu] = countdown_reset;
0419 if ((countdown_reset || !do_countdown) &&
0420 !cpumask_test_cpu(cpu, &irq_enabled_cpus)) {
0421
0422 enable_irq(octeon_wdt_cpu_to_irq(cpu));
0423 cpumask_set_cpu(cpu, &irq_enabled_cpus);
0424 }
0425 }
0426 return 0;
0427 }
0428
0429 static void octeon_wdt_calc_parameters(int t)
0430 {
0431 unsigned int periods;
0432
0433 timeout_sec = max_timeout_sec;
0434
0435
0436
0437
0438
0439
0440 while ((t % timeout_sec) != 0)
0441 timeout_sec--;
0442
0443 periods = t / timeout_sec;
0444
0445
0446
0447
0448
0449
0450 countdown_reset = periods > 2 ? periods - 2 : 0;
0451 heartbeat = t;
0452 timeout_cnt = ((octeon_get_io_clock_rate() / divisor) * timeout_sec) >> 8;
0453 }
0454
0455 static int octeon_wdt_set_timeout(struct watchdog_device *wdog,
0456 unsigned int t)
0457 {
0458 int cpu;
0459 int coreid;
0460 union cvmx_ciu_wdogx ciu_wdog;
0461 int node;
0462
0463 if (t <= 0)
0464 return -1;
0465
0466 octeon_wdt_calc_parameters(t);
0467
0468 if (disable)
0469 return 0;
0470
0471 for_each_online_cpu(cpu) {
0472 coreid = cpu2core(cpu);
0473 node = cpu_to_node(cpu);
0474 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1);
0475 ciu_wdog.u64 = 0;
0476 ciu_wdog.s.len = timeout_cnt;
0477 ciu_wdog.s.mode = 3;
0478 cvmx_write_csr_node(node, CVMX_CIU_WDOGX(coreid), ciu_wdog.u64);
0479 cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1);
0480 }
0481 octeon_wdt_ping(wdog);
0482 return 0;
0483 }
0484
0485 static int octeon_wdt_start(struct watchdog_device *wdog)
0486 {
0487 octeon_wdt_ping(wdog);
0488 do_countdown = 1;
0489 return 0;
0490 }
0491
0492 static int octeon_wdt_stop(struct watchdog_device *wdog)
0493 {
0494 do_countdown = 0;
0495 octeon_wdt_ping(wdog);
0496 return 0;
0497 }
0498
0499 static const struct watchdog_info octeon_wdt_info = {
0500 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
0501 .identity = "OCTEON",
0502 };
0503
0504 static const struct watchdog_ops octeon_wdt_ops = {
0505 .owner = THIS_MODULE,
0506 .start = octeon_wdt_start,
0507 .stop = octeon_wdt_stop,
0508 .ping = octeon_wdt_ping,
0509 .set_timeout = octeon_wdt_set_timeout,
0510 };
0511
0512 static struct watchdog_device octeon_wdt = {
0513 .info = &octeon_wdt_info,
0514 .ops = &octeon_wdt_ops,
0515 };
0516
0517 static enum cpuhp_state octeon_wdt_online;
0518
0519
0520
0521
0522
0523 static int __init octeon_wdt_init(void)
0524 {
0525 int ret;
0526
0527 octeon_wdt_bootvector = cvmx_boot_vector_get();
0528 if (!octeon_wdt_bootvector) {
0529 pr_err("Error: Cannot allocate boot vector.\n");
0530 return -ENOMEM;
0531 }
0532
0533 if (OCTEON_IS_MODEL(OCTEON_CN68XX))
0534 divisor = 0x200;
0535 else if (OCTEON_IS_MODEL(OCTEON_CN78XX))
0536 divisor = 0x400;
0537 else
0538 divisor = 0x100;
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548 max_timeout_sec = 6;
0549 do {
0550 max_timeout_sec--;
0551 timeout_cnt = ((octeon_get_io_clock_rate() / divisor) * max_timeout_sec) >> 8;
0552 } while (timeout_cnt > 65535);
0553
0554 BUG_ON(timeout_cnt == 0);
0555
0556 octeon_wdt_calc_parameters(heartbeat);
0557
0558 pr_info("Initial granularity %d Sec\n", timeout_sec);
0559
0560 octeon_wdt.timeout = timeout_sec;
0561 octeon_wdt.max_timeout = UINT_MAX;
0562
0563 watchdog_set_nowayout(&octeon_wdt, nowayout);
0564
0565 ret = watchdog_register_device(&octeon_wdt);
0566 if (ret) {
0567 pr_err("watchdog_register_device() failed: %d\n", ret);
0568 return ret;
0569 }
0570
0571 if (disable) {
0572 pr_notice("disabled\n");
0573 return 0;
0574 }
0575
0576 cpumask_clear(&irq_enabled_cpus);
0577
0578 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "watchdog/octeon:online",
0579 octeon_wdt_cpu_online, octeon_wdt_cpu_pre_down);
0580 if (ret < 0)
0581 goto err;
0582 octeon_wdt_online = ret;
0583 return 0;
0584 err:
0585 cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0);
0586 watchdog_unregister_device(&octeon_wdt);
0587 return ret;
0588 }
0589
0590
0591
0592
0593 static void __exit octeon_wdt_cleanup(void)
0594 {
0595 watchdog_unregister_device(&octeon_wdt);
0596
0597 if (disable)
0598 return;
0599
0600 cpuhp_remove_state(octeon_wdt_online);
0601
0602
0603
0604
0605
0606 cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0);
0607 }
0608
0609 MODULE_LICENSE("GPL");
0610 MODULE_AUTHOR("Cavium Inc. <support@cavium.com>");
0611 MODULE_DESCRIPTION("Cavium Inc. OCTEON Watchdog driver.");
0612 module_init(octeon_wdt_init);
0613 module_exit(octeon_wdt_cleanup);