Blame view
drivers/watchdog/octeon-wdt-main.c
16.1 KB
d01732789 watchdog: convert... |
1 |
// SPDX-License-Identifier: GPL-2.0+ |
4c076fb41 WATCHDOG: Add wat... |
2 3 4 |
/* * Octeon Watchdog driver * |
381cec022 watchdog: octeon-... |
5 |
* Copyright (C) 2007-2017 Cavium, Inc. |
4c076fb41 WATCHDOG: Add wat... |
6 |
* |
3d588c93c watchdog: octeon:... |
7 8 |
* Converted to use WATCHDOG_CORE by Aaro Koskinen <aaro.koskinen@iki.fi>. * |
4c076fb41 WATCHDOG: Add wat... |
9 10 11 12 13 |
* Some parts derived from wdt.c * * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, * All Rights Reserved. * |
4c076fb41 WATCHDOG: Add wat... |
14 15 16 17 18 19 |
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> * |
4c076fb41 WATCHDOG: Add wat... |
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
* The OCTEON watchdog has a maximum timeout of 2^32 * io_clock. * For most systems this is less than 10 seconds, so to allow for * software to request longer watchdog heartbeats, we maintain software * counters to count multiples of the base rate. If the system locks * up in such a manner that we can not run the software counters, the * only result is a watchdog reset sooner than was requested. But * that is OK, because in this case userspace would likely not be able * to do anything anyhow. * * The hardware watchdog interval we call the period. The OCTEON * watchdog goes through several stages, after the first period an * irq is asserted, then if it is not reset, after the next period NMI * is asserted, then after an additional period a chip wide soft reset. * So for the software counters, we reset watchdog after each period * and decrement the counter. But for the last two periods we need to * let the watchdog progress to the NMI stage so we disable the irq * and let it proceed. Once in the NMI, we print the register state * to the serial port and then wait for the reset. * * A watchdog is maintained for each CPU in the system, that way if * one CPU suffers a lockup, we also get a register dump and reset. * The userspace ping resets the watchdog on all CPUs. * * Before userspace opens the watchdog device, we still run the * watchdogs to catch any lockups that may be kernel related. * */ |
27c766aaa watchdog: Use pr_... |
47 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
4c076fb41 WATCHDOG: Add wat... |
48 49 50 |
#include <linux/interrupt.h> #include <linux/watchdog.h> #include <linux/cpumask.h> |
4c076fb41 WATCHDOG: Add wat... |
51 |
#include <linux/module.h> |
4c076fb41 WATCHDOG: Add wat... |
52 53 |
#include <linux/delay.h> #include <linux/cpu.h> |
ca4d3e674 MIPS: Add missing... |
54 |
#include <linux/irq.h> |
4c076fb41 WATCHDOG: Add wat... |
55 56 57 58 59 |
#include <asm/mipsregs.h> #include <asm/uasm.h> #include <asm/octeon/octeon.h> |
49d148b4e watchdog: octeon-... |
60 |
#include <asm/octeon/cvmx-boot-vector.h> |
0cd4e7a91 watchdog: octeon-... |
61 |
#include <asm/octeon/cvmx-ciu2-defs.h> |
1d1821b20 watchdog: octeon-... |
62 63 64 65 |
#include <asm/octeon/cvmx-rst-defs.h> /* Watchdog interrupt major block number (8 MSBs of intsn) */ #define WD_BLOCK_NUMBER 0x01 |
0cd4e7a91 watchdog: octeon-... |
66 67 |
static int divisor; |
4c076fb41 WATCHDOG: Add wat... |
68 69 70 71 72 73 74 75 76 77 78 |
/* The count needed to achieve timeout_sec. */ static unsigned int timeout_cnt; /* The maximum period supported. */ static unsigned int max_timeout_sec; /* The current period. */ static unsigned int timeout_sec; /* Set to non-zero when userspace countdown mode active */ |
381cec022 watchdog: octeon-... |
79 |
static bool do_countdown; |
4c076fb41 WATCHDOG: Add wat... |
80 81 82 83 84 85 |
static unsigned int countdown_reset; static unsigned int per_cpu_countdown[NR_CPUS]; static cpumask_t irq_enabled_cpus; #define WD_TIMO 60 /* Default heartbeat = 60 seconds */ |
1d1821b20 watchdog: octeon-... |
86 |
#define CVMX_GSERX_SCRATCH(offset) (CVMX_ADD_IO_SEG(0x0001180090000020ull) + ((offset) & 15) * 0x1000000ull) |
4c076fb41 WATCHDOG: Add wat... |
87 |
static int heartbeat = WD_TIMO; |
381cec022 watchdog: octeon-... |
88 |
module_param(heartbeat, int, 0444); |
4c076fb41 WATCHDOG: Add wat... |
89 90 91 |
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0 < heartbeat, default=" __MODULE_STRING(WD_TIMO) ")"); |
86a1e1896 watchdog: nowayou... |
92 |
static bool nowayout = WATCHDOG_NOWAYOUT; |
381cec022 watchdog: octeon-... |
93 |
module_param(nowayout, bool, 0444); |
4c076fb41 WATCHDOG: Add wat... |
94 95 96 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
381cec022 watchdog: octeon-... |
97 98 99 100 |
static int disable; module_param(disable, int, 0444); MODULE_PARM_DESC(disable, "Disable the watchdog entirely (default=0)"); |
49d148b4e watchdog: octeon-... |
101 |
static struct cvmx_boot_vector_element *octeon_wdt_bootvector; |
4c076fb41 WATCHDOG: Add wat... |
102 103 |
void octeon_wdt_nmi_stage2(void); |
4c076fb41 WATCHDOG: Add wat... |
104 105 106 |
static int cpu2core(int cpu) { #ifdef CONFIG_SMP |
1d1821b20 watchdog: octeon-... |
107 |
return cpu_logical_map(cpu) & 0x3f; |
4c076fb41 WATCHDOG: Add wat... |
108 109 110 111 |
#else return cvmx_get_core_num(); #endif } |
4c076fb41 WATCHDOG: Add wat... |
112 113 114 115 116 117 118 119 120 121 |
/** * Poke the watchdog when an interrupt is received * * @cpl: * @dev_id: * * Returns */ static irqreturn_t octeon_wdt_poke_irq(int cpl, void *dev_id) { |
1d1821b20 watchdog: octeon-... |
122 123 124 |
int cpu = raw_smp_processor_id(); unsigned int core = cpu2core(cpu); int node = cpu_to_node(cpu); |
4c076fb41 WATCHDOG: Add wat... |
125 |
|
381cec022 watchdog: octeon-... |
126 |
if (do_countdown) { |
4c076fb41 WATCHDOG: Add wat... |
127 128 |
if (per_cpu_countdown[cpu] > 0) { /* We're alive, poke the watchdog */ |
1d1821b20 watchdog: octeon-... |
129 |
cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1); |
4c076fb41 WATCHDOG: Add wat... |
130 131 132 133 134 135 136 137 |
per_cpu_countdown[cpu]--; } else { /* Bad news, you are about to reboot. */ disable_irq_nosync(cpl); cpumask_clear_cpu(cpu, &irq_enabled_cpus); } } else { /* Not open, just ping away... */ |
1d1821b20 watchdog: octeon-... |
138 |
cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1); |
4c076fb41 WATCHDOG: Add wat... |
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
} return IRQ_HANDLED; } /* From setup.c */ extern int prom_putchar(char c); /** * Write a string to the uart * * @str: String to write */ static void octeon_wdt_write_string(const char *str) { /* Just loop writing one byte at a time */ while (*str) prom_putchar(*str++); } /** * Write a hex number out of the uart * * @value: Number to display * @digits: Number of digits to print (1 to 16) */ static void octeon_wdt_write_hex(u64 value, int digits) { int d; int v; |
8692cf0ad watchdog: octeon:... |
168 |
|
4c076fb41 WATCHDOG: Add wat... |
169 170 171 172 173 174 175 176 |
for (d = 0; d < digits; d++) { v = (value >> ((digits - d - 1) * 4)) & 0xf; if (v >= 10) prom_putchar('a' + v - 10); else prom_putchar('0' + v); } } |
3a30c07e7 watchdog: octeon:... |
177 |
static const char reg_name[][3] = { |
4c076fb41 WATCHDOG: Add wat... |
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
"$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra" }; /** * NMI stage 3 handler. NMIs are handled in the following manner: * 1) The first NMI handler enables CVMSEG and transfers from * the bootbus region into normal memory. It is careful to not * destroy any registers. * 2) The second stage handler uses CVMSEG to save the registers * and create a stack for C code. It then calls the third level * handler with one argument, a pointer to the register values. * 3) The third, and final, level handler is the following C * function that prints out some useful infomration. * * @reg: Pointer to register state before the NMI */ void octeon_wdt_nmi_stage3(u64 reg[32]) { u64 i; unsigned int coreid = cvmx_get_core_num(); /* * Save status and cause early to get them before any changes * might happen. */ u64 cp0_cause = read_c0_cause(); u64 cp0_status = read_c0_status(); u64 cp0_error_epc = read_c0_errorepc(); u64 cp0_epc = read_c0_epc(); /* Delay so output from all cores output is not jumbled together. */ |
0cd4e7a91 watchdog: octeon-... |
212 |
udelay(85000 * coreid); |
4c076fb41 WATCHDOG: Add wat... |
213 214 215 |
octeon_wdt_write_string("\r *** NMI Watchdog interrupt on Core 0x"); |
0cd4e7a91 watchdog: octeon-... |
216 |
octeon_wdt_write_hex(coreid, 2); |
4c076fb41 WATCHDOG: Add wat... |
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
octeon_wdt_write_string(" ***\r "); for (i = 0; i < 32; i++) { octeon_wdt_write_string("\t"); octeon_wdt_write_string(reg_name[i]); octeon_wdt_write_string("\t0x"); octeon_wdt_write_hex(reg[i], 16); if (i & 1) octeon_wdt_write_string("\r "); } octeon_wdt_write_string("\terr_epc\t0x"); octeon_wdt_write_hex(cp0_error_epc, 16); octeon_wdt_write_string("\tepc\t0x"); octeon_wdt_write_hex(cp0_epc, 16); octeon_wdt_write_string("\r "); octeon_wdt_write_string("\tstatus\t0x"); octeon_wdt_write_hex(cp0_status, 16); octeon_wdt_write_string("\tcause\t0x"); octeon_wdt_write_hex(cp0_cause, 16); octeon_wdt_write_string("\r "); |
0cd4e7a91 watchdog: octeon-... |
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
/* The CIU register is different for each Octeon model. */ if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { octeon_wdt_write_string("\tsrc_wd\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SRC_PPX_IP2_WDOG(coreid)), 16); octeon_wdt_write_string("\ten_wd\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_EN_PPX_IP2_WDOG(coreid)), 16); octeon_wdt_write_string("\r "); octeon_wdt_write_string("\tsrc_rml\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SRC_PPX_IP2_RML(coreid)), 16); octeon_wdt_write_string("\ten_rml\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_EN_PPX_IP2_RML(coreid)), 16); octeon_wdt_write_string("\r "); octeon_wdt_write_string("\tsum\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid)), 16); octeon_wdt_write_string("\r "); } else if (!octeon_has_feature(OCTEON_FEATURE_CIU3)) { octeon_wdt_write_string("\tsum0\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_SUM0(coreid * 2)), 16); octeon_wdt_write_string("\ten0\t0x"); octeon_wdt_write_hex(cvmx_read_csr(CVMX_CIU_INTX_EN0(coreid * 2)), 16); octeon_wdt_write_string("\r "); } |
4c076fb41 WATCHDOG: Add wat... |
268 269 270 |
octeon_wdt_write_string("*** Chip soft reset soon ***\r "); |
1d1821b20 watchdog: octeon-... |
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
/* * G-30204: We must trigger a soft reset before watchdog * does an incomplete job of doing it. */ if (OCTEON_IS_OCTEON3() && !OCTEON_IS_MODEL(OCTEON_CN70XX)) { u64 scr; unsigned int node = cvmx_get_node_num(); unsigned int lcore = cvmx_get_local_core_num(); union cvmx_ciu_wdogx ciu_wdog; /* * Wait for other cores to print out information, but * not too long. Do the soft reset before watchdog * can trigger it. */ do { ciu_wdog.u64 = cvmx_read_csr_node(node, CVMX_CIU_WDOGX(lcore)); } while (ciu_wdog.s.cnt > 0x10000); scr = cvmx_read_csr_node(0, CVMX_GSERX_SCRATCH(0)); scr |= 1 << 11; /* Indicate watchdog in bit 11 */ cvmx_write_csr_node(0, CVMX_GSERX_SCRATCH(0), scr); cvmx_write_csr_node(0, CVMX_RST_SOFT_RST, 1); } } static int octeon_wdt_cpu_to_irq(int cpu) { unsigned int coreid; int node; int irq; coreid = cpu2core(cpu); node = cpu_to_node(cpu); if (octeon_has_feature(OCTEON_FEATURE_CIU3)) { struct irq_domain *domain; int hwirq; domain = octeon_irq_get_block_domain(node, WD_BLOCK_NUMBER); hwirq = WD_BLOCK_NUMBER << 12 | 0x200 | coreid; irq = irq_find_mapping(domain, hwirq); } else { irq = OCTEON_IRQ_WDOG0 + coreid; } return irq; |
4c076fb41 WATCHDOG: Add wat... |
319 |
} |
948b9c60c watchdog/octeon: ... |
320 |
static int octeon_wdt_cpu_pre_down(unsigned int cpu) |
4c076fb41 WATCHDOG: Add wat... |
321 322 |
{ unsigned int core; |
1d1821b20 watchdog: octeon-... |
323 |
int node; |
4c076fb41 WATCHDOG: Add wat... |
324 325 326 |
union cvmx_ciu_wdogx ciu_wdog; core = cpu2core(cpu); |
1d1821b20 watchdog: octeon-... |
327 |
node = cpu_to_node(cpu); |
4c076fb41 WATCHDOG: Add wat... |
328 329 |
/* Poke the watchdog to clear out its state */ |
1d1821b20 watchdog: octeon-... |
330 |
cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1); |
4c076fb41 WATCHDOG: Add wat... |
331 332 333 |
/* Disable the hardware. */ ciu_wdog.u64 = 0; |
1d1821b20 watchdog: octeon-... |
334 |
cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64); |
4c076fb41 WATCHDOG: Add wat... |
335 |
|
1d1821b20 watchdog: octeon-... |
336 |
free_irq(octeon_wdt_cpu_to_irq(cpu), octeon_wdt_poke_irq); |
948b9c60c watchdog/octeon: ... |
337 |
return 0; |
4c076fb41 WATCHDOG: Add wat... |
338 |
} |
948b9c60c watchdog/octeon: ... |
339 |
static int octeon_wdt_cpu_online(unsigned int cpu) |
4c076fb41 WATCHDOG: Add wat... |
340 341 342 343 |
{ unsigned int core; unsigned int irq; union cvmx_ciu_wdogx ciu_wdog; |
1d1821b20 watchdog: octeon-... |
344 345 346 |
int node; struct irq_domain *domain; int hwirq; |
4c076fb41 WATCHDOG: Add wat... |
347 348 |
core = cpu2core(cpu); |
1d1821b20 watchdog: octeon-... |
349 |
node = cpu_to_node(cpu); |
4c076fb41 WATCHDOG: Add wat... |
350 |
|
49d148b4e watchdog: octeon-... |
351 |
octeon_wdt_bootvector[core].target_ptr = (u64)octeon_wdt_nmi_stage2; |
4c076fb41 WATCHDOG: Add wat... |
352 353 |
/* Disable it before doing anything with the interrupts. */ ciu_wdog.u64 = 0; |
1d1821b20 watchdog: octeon-... |
354 |
cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64); |
4c076fb41 WATCHDOG: Add wat... |
355 356 |
per_cpu_countdown[cpu] = countdown_reset; |
1d1821b20 watchdog: octeon-... |
357 358 359 360 361 362 363 364 365 366 367 |
if (octeon_has_feature(OCTEON_FEATURE_CIU3)) { /* Must get the domain for the watchdog block */ domain = octeon_irq_get_block_domain(node, WD_BLOCK_NUMBER); /* Get a irq for the wd intsn (hardware interrupt) */ hwirq = WD_BLOCK_NUMBER << 12 | 0x200 | core; irq = irq_create_mapping(domain, hwirq); irqd_set_trigger_type(irq_get_irq_data(irq), IRQ_TYPE_EDGE_RISING); } else irq = OCTEON_IRQ_WDOG0 + core; |
4c076fb41 WATCHDOG: Add wat... |
368 369 |
if (request_irq(irq, octeon_wdt_poke_irq, |
47bfd0581 watchdog: Octeon:... |
370 |
IRQF_NO_THREAD, "octeon_wdt", octeon_wdt_poke_irq)) |
4c076fb41 WATCHDOG: Add wat... |
371 |
panic("octeon_wdt: Couldn't obtain irq %d", irq); |
1d1821b20 watchdog: octeon-... |
372 373 374 375 376 377 378 379 |
/* Must set the irq affinity here */ if (octeon_has_feature(OCTEON_FEATURE_CIU3)) { cpumask_t mask; cpumask_clear(&mask); cpumask_set_cpu(cpu, &mask); irq_set_affinity(irq, &mask); } |
4c076fb41 WATCHDOG: Add wat... |
380 381 382 |
cpumask_set_cpu(cpu, &irq_enabled_cpus); /* Poke the watchdog to clear out its state */ |
1d1821b20 watchdog: octeon-... |
383 |
cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(core), 1); |
4c076fb41 WATCHDOG: Add wat... |
384 385 386 387 388 |
/* Finally enable the watchdog now that all handlers are installed */ ciu_wdog.u64 = 0; ciu_wdog.s.len = timeout_cnt; ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ |
1d1821b20 watchdog: octeon-... |
389 |
cvmx_write_csr_node(node, CVMX_CIU_WDOGX(core), ciu_wdog.u64); |
4c076fb41 WATCHDOG: Add wat... |
390 |
|
948b9c60c watchdog/octeon: ... |
391 |
return 0; |
4c076fb41 WATCHDOG: Add wat... |
392 |
} |
3d588c93c watchdog: octeon:... |
393 |
static int octeon_wdt_ping(struct watchdog_device __always_unused *wdog) |
4c076fb41 WATCHDOG: Add wat... |
394 395 396 |
{ int cpu; int coreid; |
1d1821b20 watchdog: octeon-... |
397 |
int node; |
4c076fb41 WATCHDOG: Add wat... |
398 |
|
381cec022 watchdog: octeon-... |
399 400 |
if (disable) return 0; |
4c076fb41 WATCHDOG: Add wat... |
401 402 |
for_each_online_cpu(cpu) { coreid = cpu2core(cpu); |
1d1821b20 watchdog: octeon-... |
403 404 |
node = cpu_to_node(cpu); cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1); |
4c076fb41 WATCHDOG: Add wat... |
405 |
per_cpu_countdown[cpu] = countdown_reset; |
381cec022 watchdog: octeon-... |
406 |
if ((countdown_reset || !do_countdown) && |
4c076fb41 WATCHDOG: Add wat... |
407 408 |
!cpumask_test_cpu(cpu, &irq_enabled_cpus)) { /* We have to enable the irq */ |
1d1821b20 watchdog: octeon-... |
409 |
enable_irq(octeon_wdt_cpu_to_irq(cpu)); |
4c076fb41 WATCHDOG: Add wat... |
410 411 412 |
cpumask_set_cpu(cpu, &irq_enabled_cpus); } } |
3d588c93c watchdog: octeon:... |
413 |
return 0; |
4c076fb41 WATCHDOG: Add wat... |
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
} static void octeon_wdt_calc_parameters(int t) { unsigned int periods; timeout_sec = max_timeout_sec; /* * Find the largest interrupt period, that can evenly divide * the requested heartbeat time. */ while ((t % timeout_sec) != 0) timeout_sec--; periods = t / timeout_sec; /* * The last two periods are after the irq is disabled, and * then to the nmi, so we subtract them off. */ countdown_reset = periods > 2 ? periods - 2 : 0; heartbeat = t; |
0cd4e7a91 watchdog: octeon-... |
439 |
timeout_cnt = ((octeon_get_io_clock_rate() / divisor) * timeout_sec) >> 8; |
4c076fb41 WATCHDOG: Add wat... |
440 |
} |
3d588c93c watchdog: octeon:... |
441 442 |
static int octeon_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) |
4c076fb41 WATCHDOG: Add wat... |
443 444 445 446 |
{ int cpu; int coreid; union cvmx_ciu_wdogx ciu_wdog; |
1d1821b20 watchdog: octeon-... |
447 |
int node; |
4c076fb41 WATCHDOG: Add wat... |
448 449 450 451 452 |
if (t <= 0) return -1; octeon_wdt_calc_parameters(t); |
381cec022 watchdog: octeon-... |
453 454 |
if (disable) return 0; |
4c076fb41 WATCHDOG: Add wat... |
455 456 |
for_each_online_cpu(cpu) { coreid = cpu2core(cpu); |
1d1821b20 watchdog: octeon-... |
457 458 |
node = cpu_to_node(cpu); cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1); |
4c076fb41 WATCHDOG: Add wat... |
459 460 461 |
ciu_wdog.u64 = 0; ciu_wdog.s.len = timeout_cnt; ciu_wdog.s.mode = 3; /* 3 = Interrupt + NMI + Soft-Reset */ |
1d1821b20 watchdog: octeon-... |
462 463 |
cvmx_write_csr_node(node, CVMX_CIU_WDOGX(coreid), ciu_wdog.u64); cvmx_write_csr_node(node, CVMX_CIU_PP_POKEX(coreid), 1); |
4c076fb41 WATCHDOG: Add wat... |
464 |
} |
3d588c93c watchdog: octeon:... |
465 |
octeon_wdt_ping(wdog); /* Get the irqs back on. */ |
4c076fb41 WATCHDOG: Add wat... |
466 467 |
return 0; } |
3d588c93c watchdog: octeon:... |
468 |
static int octeon_wdt_start(struct watchdog_device *wdog) |
4c076fb41 WATCHDOG: Add wat... |
469 |
{ |
3d588c93c watchdog: octeon:... |
470 |
octeon_wdt_ping(wdog); |
381cec022 watchdog: octeon-... |
471 |
do_countdown = 1; |
3d588c93c watchdog: octeon:... |
472 |
return 0; |
4c076fb41 WATCHDOG: Add wat... |
473 |
} |
3d588c93c watchdog: octeon:... |
474 |
static int octeon_wdt_stop(struct watchdog_device *wdog) |
4c076fb41 WATCHDOG: Add wat... |
475 |
{ |
381cec022 watchdog: octeon-... |
476 |
do_countdown = 0; |
3d588c93c watchdog: octeon:... |
477 |
octeon_wdt_ping(wdog); |
4c076fb41 WATCHDOG: Add wat... |
478 479 |
return 0; } |
3d588c93c watchdog: octeon:... |
480 481 482 |
static const struct watchdog_info octeon_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "OCTEON", |
4c076fb41 WATCHDOG: Add wat... |
483 |
}; |
3d588c93c watchdog: octeon:... |
484 485 486 487 488 489 |
static const struct watchdog_ops octeon_wdt_ops = { .owner = THIS_MODULE, .start = octeon_wdt_start, .stop = octeon_wdt_stop, .ping = octeon_wdt_ping, .set_timeout = octeon_wdt_set_timeout, |
4c076fb41 WATCHDOG: Add wat... |
490 |
}; |
3d588c93c watchdog: octeon:... |
491 492 493 494 |
static struct watchdog_device octeon_wdt = { .info = &octeon_wdt_info, .ops = &octeon_wdt_ops, }; |
4c076fb41 WATCHDOG: Add wat... |
495 |
|
948b9c60c watchdog/octeon: ... |
496 |
static enum cpuhp_state octeon_wdt_online; |
4c076fb41 WATCHDOG: Add wat... |
497 498 499 500 501 502 503 |
/** * Module/ driver initialization. * * Returns Zero on success */ static int __init octeon_wdt_init(void) { |
4c076fb41 WATCHDOG: Add wat... |
504 |
int ret; |
4c076fb41 WATCHDOG: Add wat... |
505 |
|
49d148b4e watchdog: octeon-... |
506 507 508 509 510 511 |
octeon_wdt_bootvector = cvmx_boot_vector_get(); if (!octeon_wdt_bootvector) { pr_err("Error: Cannot allocate boot vector. "); return -ENOMEM; } |
0cd4e7a91 watchdog: octeon-... |
512 513 |
if (OCTEON_IS_MODEL(OCTEON_CN68XX)) divisor = 0x200; |
1d1821b20 watchdog: octeon-... |
514 515 |
else if (OCTEON_IS_MODEL(OCTEON_CN78XX)) divisor = 0x400; |
0cd4e7a91 watchdog: octeon-... |
516 517 |
else divisor = 0x100; |
4c076fb41 WATCHDOG: Add wat... |
518 519 520 |
/* * Watchdog time expiration length = The 16 bits of LEN * represent the most significant bits of a 24 bit decrementer |
0cd4e7a91 watchdog: octeon-... |
521 |
* that decrements every divisor cycle. |
4c076fb41 WATCHDOG: Add wat... |
522 523 524 525 526 527 528 |
* * Try for a timeout of 5 sec, if that fails a smaller number * of even seconds, */ max_timeout_sec = 6; do { max_timeout_sec--; |
0cd4e7a91 watchdog: octeon-... |
529 |
timeout_cnt = ((octeon_get_io_clock_rate() / divisor) * max_timeout_sec) >> 8; |
4c076fb41 WATCHDOG: Add wat... |
530 531 532 533 534 |
} while (timeout_cnt > 65535); BUG_ON(timeout_cnt == 0); octeon_wdt_calc_parameters(heartbeat); |
27c766aaa watchdog: Use pr_... |
535 536 |
pr_info("Initial granularity %d Sec ", timeout_sec); |
4c076fb41 WATCHDOG: Add wat... |
537 |
|
3d588c93c watchdog: octeon:... |
538 539 540 541 542 543 |
octeon_wdt.timeout = timeout_sec; octeon_wdt.max_timeout = UINT_MAX; watchdog_set_nowayout(&octeon_wdt, nowayout); ret = watchdog_register_device(&octeon_wdt); |
4c076fb41 WATCHDOG: Add wat... |
544 |
if (ret) { |
3d588c93c watchdog: octeon:... |
545 546 547 |
pr_err("watchdog_register_device() failed: %d ", ret); return ret; |
4c076fb41 WATCHDOG: Add wat... |
548 |
} |
381cec022 watchdog: octeon-... |
549 550 551 552 553 |
if (disable) { pr_notice("disabled "); return 0; } |
4c076fb41 WATCHDOG: Add wat... |
554 |
cpumask_clear(&irq_enabled_cpus); |
948b9c60c watchdog/octeon: ... |
555 556 557 558 559 |
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "watchdog/octeon:online", octeon_wdt_cpu_online, octeon_wdt_cpu_pre_down); if (ret < 0) goto err; octeon_wdt_online = ret; |
3d588c93c watchdog: octeon:... |
560 |
return 0; |
948b9c60c watchdog/octeon: ... |
561 562 563 564 |
err: cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0); watchdog_unregister_device(&octeon_wdt); return ret; |
4c076fb41 WATCHDOG: Add wat... |
565 566 567 568 569 570 571 |
} /** * Module / driver shutdown */ static void __exit octeon_wdt_cleanup(void) { |
3d588c93c watchdog: octeon:... |
572 |
watchdog_unregister_device(&octeon_wdt); |
381cec022 watchdog: octeon-... |
573 574 575 |
if (disable) return; |
948b9c60c watchdog/octeon: ... |
576 |
cpuhp_remove_state(octeon_wdt_online); |
99c3bf361 octeon, watchdog:... |
577 |
|
4c076fb41 WATCHDOG: Add wat... |
578 579 580 581 582 583 584 585 |
/* * Disable the boot-bus memory, the code it points to is soon * to go missing. */ cvmx_write_csr(CVMX_MIO_BOOT_LOC_CFGX(0), 0); } MODULE_LICENSE("GPL"); |
381cec022 watchdog: octeon-... |
586 587 |
MODULE_AUTHOR("Cavium Inc. <support@cavium.com>"); MODULE_DESCRIPTION("Cavium Inc. OCTEON Watchdog driver."); |
4c076fb41 WATCHDOG: Add wat... |
588 589 |
module_init(octeon_wdt_init); module_exit(octeon_wdt_cleanup); |