Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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, &reg);
^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");