^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) * Watchdog driver for Marvell Armada 37xx SoCs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Marek Behun <marek.behun@nic.cz>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/clk.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/mfd/syscon.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/of_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/regmap.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * There are four counters that can be used for watchdog on Armada 37xx.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * The addresses for counter control registers are register base plus ID*0x10,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * where ID is 0, 1, 2 or 3.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * In this driver we use IDs 0 and 1. Counter ID 1 is used as watchdog counter,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * while counter ID 0 is used to implement pinging the watchdog: counter ID 1 is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * set to restart counting from initial value on counter ID 0 end count event.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * Pinging is done by forcing immediate end count event on counter ID 0.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * If only one counter was used, pinging would have to be implemented by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * disabling and enabling the counter, leaving the system in a vulnerable state
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * for a (really) short period of time.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * Counters ID 2 and 3 are enabled by default even before U-Boot loads,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * therefore this driver does not provide a way to use them, eg. by setting a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * property in device tree.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #define CNTR_ID_RETRIGGER 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) #define CNTR_ID_WDOG 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /* relative to cpu_misc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #define WDT_TIMER_SELECT 0x64
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define WDT_TIMER_SELECT_MASK 0xf
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define WDT_TIMER_SELECT_VAL BIT(CNTR_ID_WDOG)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) /* relative to reg */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define CNTR_CTRL(id) ((id) * 0x10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define CNTR_CTRL_ENABLE 0x0001
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define CNTR_CTRL_ACTIVE 0x0002
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define CNTR_CTRL_MODE_MASK 0x000c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #define CNTR_CTRL_MODE_ONESHOT 0x0000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define CNTR_CTRL_MODE_HWSIG 0x000c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #define CNTR_CTRL_TRIG_SRC_MASK 0x00f0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #define CNTR_CTRL_TRIG_SRC_PREV_CNTR 0x0050
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #define CNTR_CTRL_PRESCALE_MASK 0xff00
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define CNTR_CTRL_PRESCALE_MIN 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) #define CNTR_CTRL_PRESCALE_SHIFT 8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) #define CNTR_COUNT_LOW(id) (CNTR_CTRL(id) + 0x4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) #define CNTR_COUNT_HIGH(id) (CNTR_CTRL(id) + 0x8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) #define WATCHDOG_TIMEOUT 120
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static unsigned int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) module_param(timeout, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) struct armada_37xx_watchdog {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) struct watchdog_device wdt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) struct regmap *cpu_misc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) void __iomem *reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) u64 timeout; /* in clock ticks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) unsigned long clk_rate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct clk *clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) static u64 get_counter_value(struct armada_37xx_watchdog *dev, int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) u64 val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * when low is read, high is latched into flip-flops so that it can be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * read consistently without using software debouncing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) val = readl(dev->reg + CNTR_COUNT_LOW(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) val |= ((u64)readl(dev->reg + CNTR_COUNT_HIGH(id))) << 32;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) return val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) static void set_counter_value(struct armada_37xx_watchdog *dev, int id, u64 val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) writel(val & 0xffffffff, dev->reg + CNTR_COUNT_LOW(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) writel(val >> 32, dev->reg + CNTR_COUNT_HIGH(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static void counter_enable(struct armada_37xx_watchdog *dev, int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) reg = readl(dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) reg |= CNTR_CTRL_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) writel(reg, dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) static void counter_disable(struct armada_37xx_watchdog *dev, int id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) reg = readl(dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) reg &= ~CNTR_CTRL_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) writel(reg, dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) static void init_counter(struct armada_37xx_watchdog *dev, int id, u32 mode,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) u32 trig_src)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) reg = readl(dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) reg &= ~(CNTR_CTRL_MODE_MASK | CNTR_CTRL_PRESCALE_MASK |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) CNTR_CTRL_TRIG_SRC_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /* set mode */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) reg |= mode & CNTR_CTRL_MODE_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) /* set prescaler to the min value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) reg |= CNTR_CTRL_PRESCALE_MIN << CNTR_CTRL_PRESCALE_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) /* set trigger source */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) reg |= trig_src & CNTR_CTRL_TRIG_SRC_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) writel(reg, dev->reg + CNTR_CTRL(id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static int armada_37xx_wdt_ping(struct watchdog_device *wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) /* counter 1 is retriggered by forcing end count on counter 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) counter_disable(dev, CNTR_ID_RETRIGGER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) counter_enable(dev, CNTR_ID_RETRIGGER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) return 0;
^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) static unsigned int armada_37xx_wdt_get_timeleft(struct watchdog_device *wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) u64 res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) res = get_counter_value(dev, CNTR_ID_WDOG) * CNTR_CTRL_PRESCALE_MIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) do_div(res, dev->clk_rate);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return res;
^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) static int armada_37xx_wdt_set_timeout(struct watchdog_device *wdt,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) unsigned int timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) wdt->timeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) * Compute the timeout in clock rate. We use smallest possible
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) * prescaler, which divides the clock rate by 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) * (CNTR_CTRL_PRESCALE_MIN).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) dev->timeout = (u64)dev->clk_rate * timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) do_div(dev->timeout, CNTR_CTRL_PRESCALE_MIN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) static bool armada_37xx_wdt_is_running(struct armada_37xx_watchdog *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) u32 reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) regmap_read(dev->cpu_misc, WDT_TIMER_SELECT, ®);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) if ((reg & WDT_TIMER_SELECT_MASK) != WDT_TIMER_SELECT_VAL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) reg = readl(dev->reg + CNTR_CTRL(CNTR_ID_WDOG));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) return !!(reg & CNTR_CTRL_ACTIVE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) static int armada_37xx_wdt_start(struct watchdog_device *wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) /* select counter 1 as watchdog counter */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, WDT_TIMER_SELECT_VAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) /* init counter 0 as retrigger counter for counter 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) init_counter(dev, CNTR_ID_RETRIGGER, CNTR_CTRL_MODE_ONESHOT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) set_counter_value(dev, CNTR_ID_RETRIGGER, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) /* init counter 1 to be retriggerable by counter 0 end count */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) init_counter(dev, CNTR_ID_WDOG, CNTR_CTRL_MODE_HWSIG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) CNTR_CTRL_TRIG_SRC_PREV_CNTR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) set_counter_value(dev, CNTR_ID_WDOG, dev->timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) /* enable counter 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) counter_enable(dev, CNTR_ID_WDOG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) /* start counter 1 by forcing immediate end count on counter 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) counter_enable(dev, CNTR_ID_RETRIGGER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) static int armada_37xx_wdt_stop(struct watchdog_device *wdt)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) struct armada_37xx_watchdog *dev = watchdog_get_drvdata(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) counter_disable(dev, CNTR_ID_WDOG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) counter_disable(dev, CNTR_ID_RETRIGGER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) regmap_write(dev->cpu_misc, WDT_TIMER_SELECT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) static const struct watchdog_info armada_37xx_wdt_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) .identity = "Armada 37xx Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) static const struct watchdog_ops armada_37xx_wdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) .start = armada_37xx_wdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) .stop = armada_37xx_wdt_stop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) .ping = armada_37xx_wdt_ping,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) .set_timeout = armada_37xx_wdt_set_timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) .get_timeleft = armada_37xx_wdt_get_timeleft,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) static void armada_clk_disable_unprepare(void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) clk_disable_unprepare(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) static int armada_37xx_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) struct armada_37xx_watchdog *dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) struct resource *res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) struct regmap *regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) dev = devm_kzalloc(&pdev->dev, sizeof(struct armada_37xx_watchdog),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) if (!dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) dev->wdt.info = &armada_37xx_wdt_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) dev->wdt.ops = &armada_37xx_wdt_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) "marvell,system-controller");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) if (IS_ERR(regmap))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) return PTR_ERR(regmap);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) dev->cpu_misc = regmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) if (!res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) dev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) /* init clock */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) dev->clk = devm_clk_get(&pdev->dev, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (IS_ERR(dev->clk))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) return PTR_ERR(dev->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) ret = clk_prepare_enable(dev->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) ret = devm_add_action_or_reset(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) armada_clk_disable_unprepare, dev->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) dev->clk_rate = clk_get_rate(dev->clk);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) if (!dev->clk_rate)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) * Since the timeout in seconds is given as 32 bit unsigned int, and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) * the counters hold 64 bit values, even after multiplication by clock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) * rate the counter can hold timeout of UINT_MAX seconds.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) dev->wdt.min_timeout = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) dev->wdt.max_timeout = UINT_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) dev->wdt.parent = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) /* default value, possibly override by module parameter or dtb */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) dev->wdt.timeout = WATCHDOG_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) watchdog_init_timeout(&dev->wdt, timeout, &pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) platform_set_drvdata(pdev, &dev->wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) watchdog_set_drvdata(&dev->wdt, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) armada_37xx_wdt_set_timeout(&dev->wdt, dev->wdt.timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (armada_37xx_wdt_is_running(dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) set_bit(WDOG_HW_RUNNING, &dev->wdt.status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) watchdog_set_nowayout(&dev->wdt, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) watchdog_stop_on_reboot(&dev->wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) ret = devm_watchdog_register_device(&pdev->dev, &dev->wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) dev_info(&pdev->dev, "Initial timeout %d sec%s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) dev->wdt.timeout, nowayout ? ", nowayout" : "");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) static int __maybe_unused armada_37xx_wdt_suspend(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) struct watchdog_device *wdt = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) return armada_37xx_wdt_stop(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) static int __maybe_unused armada_37xx_wdt_resume(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) struct watchdog_device *wdt = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) if (watchdog_active(wdt))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) return armada_37xx_wdt_start(wdt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) static const struct dev_pm_ops armada_37xx_wdt_dev_pm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) SET_SYSTEM_SLEEP_PM_OPS(armada_37xx_wdt_suspend,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) armada_37xx_wdt_resume)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) #ifdef CONFIG_OF
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) static const struct of_device_id armada_37xx_wdt_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) { .compatible = "marvell,armada-3700-wdt", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) MODULE_DEVICE_TABLE(of, armada_37xx_wdt_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) static struct platform_driver armada_37xx_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) .probe = armada_37xx_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) .name = "armada_37xx_wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) .of_match_table = of_match_ptr(armada_37xx_wdt_match),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) .pm = &armada_37xx_wdt_dev_pm_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) module_platform_driver(armada_37xx_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) MODULE_DESCRIPTION("Armada 37xx CPU Watchdog");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) MODULE_ALIAS("platform:armada_37xx_wdt");