^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");