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