^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * GE watchdog userspace interface
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Martyn Welch <martyn.welch@ge.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Based on: mv64x60_wdt.c (MV64X60 watchdog userspace interface)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Author: James Chapman <jchapman@katalix.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) /* TODO:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * This driver does not provide support for the hardwares capability of sending
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * an interrupt at a programmable threshold.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * This driver currently can only support 1 watchdog - there are 2 in the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * hardware that this driver supports. Thus one could be configured as a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * process-based watchdog (via /dev/watchdog), the second (using the interrupt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * capabilities) a kernel-based watchdog.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/compiler.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/miscdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/watchdog.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <linux/fs.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #include <sysdev/fsl_soc.h>
^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) * The watchdog configuration register contains a pair of 2-bit fields,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * 1. a reload field, bits 27-26, which triggers a reload of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * the countdown register, and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * 2. an enable field, bits 25-24, which toggles between
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * enabling and disabling the watchdog timer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * Bit 31 is a read-only field which indicates whether the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) * watchdog timer is currently enabled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * The low 24 bits contain the timer reload value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #define GEF_WDC_ENABLE_SHIFT 24
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #define GEF_WDC_SERVICE_SHIFT 26
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #define GEF_WDC_ENABLED_SHIFT 31
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define GEF_WDC_ENABLED_TRUE 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #define GEF_WDC_ENABLED_FALSE 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) /* Flags bits */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) #define GEF_WDOG_FLAG_OPENED 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) static unsigned long wdt_flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static int wdt_status;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) static void __iomem *gef_wdt_regs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static int gef_wdt_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) static int gef_wdt_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static unsigned int bus_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static char expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static DEFINE_SPINLOCK(gef_wdt_spinlock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int gef_wdt_toggle_wdc(int enabled_predicate, int field_shift)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) u32 data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) u32 enabled;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) spin_lock(&gef_wdt_spinlock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) data = ioread32be(gef_wdt_regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) enabled = (data >> GEF_WDC_ENABLED_SHIFT) & 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) /* only toggle the requested field if enabled state matches predicate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) if ((enabled ^ enabled_predicate) == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) /* We write a 1, then a 2 -- to the appropriate field */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) data = (1 << field_shift) | gef_wdt_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) iowrite32be(data, gef_wdt_regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) data = (2 << field_shift) | gef_wdt_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) iowrite32be(data, gef_wdt_regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) ret = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) spin_unlock(&gef_wdt_spinlock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) static void gef_wdt_service(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) GEF_WDC_SERVICE_SHIFT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) static void gef_wdt_handler_enable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_FALSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) GEF_WDC_ENABLE_SHIFT)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) gef_wdt_service();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) pr_notice("watchdog activated\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static void gef_wdt_handler_disable(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if (gef_wdt_toggle_wdc(GEF_WDC_ENABLED_TRUE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) GEF_WDC_ENABLE_SHIFT))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) pr_notice("watchdog deactivated\n");
^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 gef_wdt_set_timeout(unsigned int timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) /* maximum bus cycle count is 0xFFFFFFFF */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (timeout > 0xFFFFFFFF / bus_clk)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) timeout = 0xFFFFFFFF / bus_clk;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) /* Register only holds upper 24 bits, bit shifted into lower 24 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) gef_wdt_count = (timeout * bus_clk) >> 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) gef_wdt_timeout = timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) static ssize_t gef_wdt_write(struct file *file, const char __user *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) for (i = 0; i != len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) gef_wdt_service();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) static long gef_wdt_ioctl(struct file *file, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) int options;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static const struct watchdog_info info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) WDIOF_KEEPALIVEPING,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) .firmware_version = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) .identity = "GE watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) if (copy_to_user(argp, &info, sizeof(info)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) if (put_user(wdt_status, (int __user *)argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) wdt_status &= ~WDIOF_KEEPALIVEPING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) case WDIOC_SETOPTIONS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if (get_user(options, (int __user *)argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) if (options & WDIOS_DISABLECARD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) gef_wdt_handler_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) if (options & WDIOS_ENABLECARD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) gef_wdt_handler_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) gef_wdt_service();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) wdt_status |= WDIOF_KEEPALIVEPING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) if (get_user(timeout, (int __user *)argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) gef_wdt_set_timeout(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) if (put_user(gef_wdt_timeout, (int __user *)argp))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) static int gef_wdt_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) if (test_and_set_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if (nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) __module_get(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) gef_wdt_handler_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) static int gef_wdt_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (expect_close == 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) gef_wdt_handler_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) pr_crit("unexpected close, not stopping timer!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) gef_wdt_service();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) clear_bit(GEF_WDOG_FLAG_OPENED, &wdt_flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) static const struct file_operations gef_wdt_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) .write = gef_wdt_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) .unlocked_ioctl = gef_wdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) .compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) .open = gef_wdt_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) .release = gef_wdt_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) static struct miscdevice gef_wdt_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) .minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) .name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) .fops = &gef_wdt_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) static int gef_wdt_probe(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) int timeout = 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) u32 freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) bus_clk = 133; /* in MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) freq = fsl_get_sys_freq();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (freq != -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) bus_clk = freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) /* Map devices registers into memory */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) gef_wdt_regs = of_iomap(dev->dev.of_node, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) if (gef_wdt_regs == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) gef_wdt_set_timeout(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) gef_wdt_handler_disable(); /* in case timer was already running */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) return misc_register(&gef_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) static int gef_wdt_remove(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) misc_deregister(&gef_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) gef_wdt_handler_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) iounmap(gef_wdt_regs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) static const struct of_device_id gef_wdt_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) .compatible = "gef,fpga-wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) MODULE_DEVICE_TABLE(of, gef_wdt_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) static struct platform_driver gef_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) .name = "gef_wdt",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) .of_match_table = gef_wdt_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) .probe = gef_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) .remove = gef_wdt_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) static int __init gef_wdt_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) pr_info("GE watchdog driver\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) return platform_driver_register(&gef_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) static void __exit gef_wdt_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) platform_driver_unregister(&gef_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) module_init(gef_wdt_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) module_exit(gef_wdt_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) MODULE_DESCRIPTION("GE watchdog driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) MODULE_ALIAS("platform:gef_wdt");