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-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Kontron PLD watchdog driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * Copyright (c) 2010-2013 Kontron Europe GmbH
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  * Author: Michael Brunner <michael.brunner@kontron.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  * Note: From the PLD watchdog point of view timeout and pretimeout are
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  *       defined differently than in the kernel.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  *       First the pretimeout stage runs out before the timeout stage gets
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  *       active.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  * Kernel/API:                     P-----| pretimeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  *               |-----------------------T timeout
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  * Watchdog:     |-----------------P       pretimeout_stage
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  *                                 |-----T timeout_stage
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) #include <linux/moduleparam.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) #include <linux/mfd/kempld.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) #define KEMPLD_WDT_STAGE_TIMEOUT(x)	(0x1b + (x) * 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) #define KEMPLD_WDT_STAGE_CFG(x)		(0x18 + (x))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) #define STAGE_CFG_GET_PRESCALER(x)	(((x) & 0x30) >> 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) #define STAGE_CFG_SET_PRESCALER(x)	(((x) & 0x3) << 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #define STAGE_CFG_PRESCALER_MASK	0x30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) #define STAGE_CFG_ACTION_MASK		0x7
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) #define STAGE_CFG_ASSERT		(1 << 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #define KEMPLD_WDT_MAX_STAGES		2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #define KEMPLD_WDT_KICK			0x16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #define KEMPLD_WDT_CFG			0x17
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #define KEMPLD_WDT_CFG_ENABLE		0x10
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #define KEMPLD_WDT_CFG_ENABLE_LOCK	0x8
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) #define KEMPLD_WDT_CFG_GLOBAL_LOCK	0x80
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 	ACTION_NONE = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 	ACTION_RESET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 	ACTION_NMI,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 	ACTION_SMI,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 	ACTION_SCI,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 	ACTION_DELAY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	STAGE_TIMEOUT = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 	STAGE_PRETIMEOUT,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	PRESCALER_21 = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	PRESCALER_17,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 	PRESCALER_12,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) static const u32 kempld_prescaler[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 	[PRESCALER_21] = (1 << 21) - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 	[PRESCALER_17] = (1 << 17) - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 	[PRESCALER_12] = (1 << 12) - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 	0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) struct kempld_wdt_stage {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	unsigned int	id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	u32		mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) struct kempld_wdt_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	struct kempld_device_data	*pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	struct watchdog_device		wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	unsigned int			pretimeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	struct kempld_wdt_stage		stage[KEMPLD_WDT_MAX_STAGES];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) #ifdef CONFIG_PM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	u8				pm_status_store;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) #define DEFAULT_TIMEOUT		30 /* seconds */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) #define DEFAULT_PRETIMEOUT	0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) static unsigned int timeout = DEFAULT_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) module_param(timeout, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) MODULE_PARM_DESC(timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 	"Watchdog timeout in seconds. (>=0, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 	__MODULE_STRING(DEFAULT_TIMEOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) static unsigned int pretimeout = DEFAULT_PRETIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) module_param(pretimeout, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) MODULE_PARM_DESC(pretimeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	"Watchdog pretimeout in seconds. (>=0, default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	__MODULE_STRING(DEFAULT_PRETIMEOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) MODULE_PARM_DESC(nowayout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 	"Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 					struct kempld_wdt_stage *stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 					u8 action)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	u8 stage_cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	if (!stage || !stage->mask)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	stage_cfg &= ~STAGE_CFG_ACTION_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	stage_cfg |= (action & STAGE_CFG_ACTION_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 	if (action == ACTION_RESET)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 		stage_cfg |= STAGE_CFG_ASSERT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 		stage_cfg &= ~STAGE_CFG_ASSERT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 					struct kempld_wdt_stage *stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 					unsigned int timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	u32 prescaler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	u64 stage_timeout64;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	u32 stage_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	u32 remainder;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	u8 stage_cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	prescaler = kempld_prescaler[PRESCALER_21];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	if (!stage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	stage_timeout64 = (u64)timeout * pld->pld_clock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	remainder = do_div(stage_timeout64, prescaler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 	if (remainder)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 		stage_timeout64++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 	if (stage_timeout64 > stage->mask)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	stage_timeout = stage_timeout64 & stage->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 			stage_timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	return 0;
^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) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)  * kempld_get_mutex must be called prior to calling this function.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 						struct kempld_wdt_stage *stage)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	unsigned int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 	u64 stage_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	u32 prescaler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	u32 remainder;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 	u8 stage_cfg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 	if (!stage->mask)
^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) 	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 	stage_timeout = (stage_timeout & stage->mask) * prescaler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 	remainder = do_div(stage_timeout, pld->pld_clock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 	if (remainder)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 		stage_timeout++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	timeout = stage_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	WARN_ON_ONCE(timeout != stage_timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	return timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 					unsigned int timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	struct kempld_wdt_stage *pretimeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 	struct kempld_wdt_stage *timeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 	timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 	pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 	if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 		timeout = wdt_data->pretimeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 						ACTION_RESET);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 						timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 	wdd->timeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 					unsigned int pretimeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 	struct kempld_wdt_stage *pretimeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 	u8 action = ACTION_NONE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	if (!pretimeout_stage->mask)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 		return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	if (pretimeout > wdd->timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 	if (pretimeout > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 		action = ACTION_NMI;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 	ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 						action);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 	ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 						wdd->timeout - pretimeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	wdt_data->pretimeout = pretimeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 	struct kempld_wdt_stage *pretimeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 	struct kempld_wdt_stage *timeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 	unsigned int pretimeout, timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 	pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 	timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 	pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 	timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 	if (pretimeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 		wdt_data->pretimeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 		wdt_data->pretimeout = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 	wdt_data->wdd.timeout = pretimeout + timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) static int kempld_wdt_start(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 	u8 status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 	ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 	status = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 	status |= KEMPLD_WDT_CFG_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 	kempld_write8(pld, KEMPLD_WDT_CFG, status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 	status = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 	/* Check if the watchdog was enabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 	if (!(status & KEMPLD_WDT_CFG_ENABLE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 		return -EACCES;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) static int kempld_wdt_stop(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 	u8 status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) 	status = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) 	status &= ~KEMPLD_WDT_CFG_ENABLE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) 	kempld_write8(pld, KEMPLD_WDT_CFG, status);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) 	status = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 	/* Check if the watchdog was disabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 	if (status & KEMPLD_WDT_CFG_ENABLE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 		return -EACCES;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) static int kempld_wdt_keepalive(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 	kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) 				unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) 	void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 	int ret = -ENOIOCTLCMD;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 	int __user *p = argp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 	int new_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 	switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 	case WDIOC_SETPRETIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 		if (get_user(new_value, p))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 			return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) 		ret = kempld_wdt_set_pretimeout(wdd, new_value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 		if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 			return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 		ret = kempld_wdt_keepalive(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 		break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 	case WDIOC_GETPRETIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 		ret = put_user(wdt_data->pretimeout, (int __user *)arg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 		break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 	struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 	struct kempld_wdt_stage *pretimeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 	struct kempld_wdt_stage *timeout_stage;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 	u8 index, data, data_orig;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) 	u32 mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 	int i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 	pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 	timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 	pretimeout_stage->mask = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) 	timeout_stage->mask = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 	for (i = 0; i < 3; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 		index = KEMPLD_WDT_STAGE_TIMEOUT(i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 		mask = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 		kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 		/* Probe each byte individually. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 		for (j = 0; j < 4; j++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 			data_orig = kempld_read8(pld, index + j);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 			kempld_write8(pld, index + j, 0x00);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 			data = kempld_read8(pld, index + j);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 			/* A failed write means this byte is reserved */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) 			if (data != 0x00)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 				break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 			kempld_write8(pld, index + j, data_orig);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 			mask |= 0xff << (j * 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 		kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 		/* Assign available stages to timeout and pretimeout */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) 		if (!timeout_stage->mask) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) 			timeout_stage->mask = mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 			timeout_stage->id = i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) 		} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 			if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 				pretimeout_stage->mask = timeout_stage->mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 				timeout_stage->mask = mask;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 				pretimeout_stage->id = timeout_stage->id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) 				timeout_stage->id = i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 			}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 	if (!timeout_stage->mask)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) static const struct watchdog_info kempld_wdt_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) 	.identity	= "KEMPLD Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) 	.options	= WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 			WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) 			WDIOF_MAGICCLOSE |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 			WDIOF_PRETIMEOUT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) static const struct watchdog_ops kempld_wdt_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) 	.owner		= THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 	.start		= kempld_wdt_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) 	.stop		= kempld_wdt_stop,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 	.ping		= kempld_wdt_keepalive,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) 	.set_timeout	= kempld_wdt_set_timeout,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) 	.ioctl		= kempld_wdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) static int kempld_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) 	struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 	struct kempld_wdt_data *wdt_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 	struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) 	struct watchdog_device *wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 	u8 status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) 	int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 	wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 	if (!wdt_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 		return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) 	wdt_data->pld = pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) 	wdd = &wdt_data->wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) 	wdd->parent = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) 	status = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) 	/* Enable nowayout if watchdog is already locked */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) 	if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) 			KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) 		if (!nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) 			dev_warn(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) 				 "Forcing nowayout - watchdog lock enabled!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) 		nowayout = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) 	wdd->info = &kempld_wdt_info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) 	wdd->ops = &kempld_wdt_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) 	watchdog_set_drvdata(wdd, wdt_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) 	watchdog_set_nowayout(wdd, nowayout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) 	ret = kempld_wdt_probe_stages(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) 	kempld_wdt_set_timeout(wdd, timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) 	kempld_wdt_set_pretimeout(wdd, pretimeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) 	/* Check if watchdog is already enabled */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) 	if (status & KEMPLD_WDT_CFG_ENABLE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) 		/* Get current watchdog settings */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) 		kempld_wdt_update_timeouts(wdt_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) 		dev_info(dev, "Watchdog was already enabled\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) 	platform_set_drvdata(pdev, wdt_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) 	watchdog_stop_on_reboot(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) 	watchdog_stop_on_unregister(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) 	ret = devm_watchdog_register_device(dev, wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) 	if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) 		return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) 	dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) #ifdef CONFIG_PM
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) /* Disable watchdog if it is active during suspend */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) static int kempld_wdt_suspend(struct platform_device *pdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) 				pm_message_t message)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) 	struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) 	struct kempld_device_data *pld = wdt_data->pld;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) 	struct watchdog_device *wdd = &wdt_data->wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) 	kempld_get_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) 	wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) 	kempld_release_mutex(pld);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) 	kempld_wdt_update_timeouts(wdt_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) 	if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) 		return kempld_wdt_stop(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) /* Enable watchdog and configure it if necessary */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) static int kempld_wdt_resume(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) 	struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) 	struct watchdog_device *wdd = &wdt_data->wdd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) 	 * If watchdog was stopped before suspend be sure it gets disabled
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) 	 * again, for the case BIOS has enabled it during resume
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) 	if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) 		return kempld_wdt_start(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) 		return kempld_wdt_stop(wdd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) #define kempld_wdt_suspend	NULL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) #define kempld_wdt_resume	NULL
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) static struct platform_driver kempld_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) 	.driver		= {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) 		.name	= "kempld-wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) 	.probe		= kempld_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) 	.suspend	= kempld_wdt_suspend,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) 	.resume		= kempld_wdt_resume,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) module_platform_driver(kempld_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) MODULE_LICENSE("GPL");