^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) * IDT Interprise 79RC32434 watchdog driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * based on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * SoftDog 0.05: A Software Watchdog Device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * All Rights Reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/module.h> /* For module specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/moduleparam.h> /* For new moduleparam's */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/types.h> /* For standard types (like size_t) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/errno.h> /* For the -ENODEV/... values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/kernel.h> /* For printk/panic/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <linux/fs.h> /* For file operations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <linux/miscdevice.h> /* For struct miscdevice */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <linux/watchdog.h> /* For the watchdog specific items */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <linux/init.h> /* For __init/__exit/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/platform_device.h> /* For platform_driver framework */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/io.h> /* For devm_ioremap */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <asm/mach-rc32434/integ.h> /* For the Watchdog registers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define VERSION "1.0"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) unsigned long inuse;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) spinlock_t io_lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) } rc32434_wdt_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static struct integ __iomem *wdt_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static int expect_close;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /* Board internal clock speed in Hz,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * the watchdog timer ticks at. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) extern unsigned int idt_cpu_freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) /* translate wtcompare value to seconds and vice versa */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #define WTCOMP2SEC(x) (x / idt_cpu_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #define SEC2WTCOMP(x) (x * idt_cpu_freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) /* Use a default timeout of 20s. This should be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * safe for CPU clock speeds up to 400MHz, as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #define WATCHDOG_TIMEOUT 20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) static int timeout = WATCHDOG_TIMEOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) module_param(timeout, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) MODULE_PARM_DESC(timeout, "Watchdog timeout value, in seconds (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static bool nowayout = WATCHDOG_NOWAYOUT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) module_param(nowayout, bool, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) /* apply or and nand masks to data read from addr and write back */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) #define SET_BITS(addr, or, nand) \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) writel((readl(&addr) | or) & ~nand, &addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) static int rc32434_wdt_set(int new_timeout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) int max_to = WTCOMP2SEC((u32)-1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if (new_timeout < 0 || new_timeout > max_to) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) pr_err("timeout value must be between 0 and %d\n", max_to);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) timeout = new_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) spin_lock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) spin_unlock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) static void rc32434_wdt_start(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) u32 or, nand;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) spin_lock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) /* zero the counter before enabling */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) writel(0, &wdt_reg->wtcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) /* don't generate a non-maskable interrupt,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) * do a warm reset instead */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) nand = 1 << RC32434_ERR_WNE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) or = 1 << RC32434_ERR_WRE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /* reset the ERRCS timeout bit in case it's set */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) nand |= 1 << RC32434_ERR_WTO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) SET_BITS(wdt_reg->errcs, or, nand);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) /* set the timeout (either default or based on module param) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) rc32434_wdt_set(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /* reset WTC timeout bit and enable WDT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) nand = 1 << RC32434_WTC_TO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) or = 1 << RC32434_WTC_EN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) SET_BITS(wdt_reg->wtc, or, nand);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) spin_unlock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) pr_info("Started watchdog timer\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) static void rc32434_wdt_stop(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) spin_lock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) /* Disable WDT */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) spin_unlock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) pr_info("Stopped watchdog timer\n");
^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 void rc32434_wdt_ping(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) spin_lock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) writel(0, &wdt_reg->wtcount);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) spin_unlock(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) static int rc32434_wdt_open(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if (nowayout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) __module_get(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) rc32434_wdt_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) rc32434_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) return stream_open(inode, file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) static int rc32434_wdt_release(struct inode *inode, struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) if (expect_close == 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) rc32434_wdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) module_put(THIS_MODULE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) pr_crit("device closed unexpectedly. WDT will not stop!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) rc32434_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) clear_bit(0, &rc32434_wdt_device.inuse);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) static ssize_t rc32434_wdt_write(struct file *file, const char *data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) size_t len, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) if (len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) if (!nowayout) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) size_t i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) /* In case it was set long ago */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) expect_close = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) for (i = 0; i != len; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) char c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (get_user(c, data + i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if (c == 'V')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) expect_close = 42;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) rc32434_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) return len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) unsigned long arg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) void __user *argp = (void __user *)arg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) int new_timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) unsigned int value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) static const struct watchdog_info ident = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .options = WDIOF_SETTIMEOUT |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) WDIOF_KEEPALIVEPING |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) WDIOF_MAGICCLOSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) .identity = "RC32434_WDT Watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) switch (cmd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) case WDIOC_GETSUPPORT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) if (copy_to_user(argp, &ident, sizeof(ident)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) case WDIOC_GETSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) case WDIOC_GETBOOTSTATUS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) value = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) if (copy_to_user(argp, &value, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) case WDIOC_SETOPTIONS:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (copy_from_user(&value, argp, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) switch (value) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) case WDIOS_ENABLECARD:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) rc32434_wdt_start();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) case WDIOS_DISABLECARD:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) rc32434_wdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) case WDIOC_KEEPALIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) rc32434_wdt_ping();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) case WDIOC_SETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) if (copy_from_user(&new_timeout, argp, sizeof(int)))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) if (rc32434_wdt_set(new_timeout))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) fallthrough;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) case WDIOC_GETTIMEOUT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) return copy_to_user(argp, &timeout, sizeof(int)) ? -EFAULT : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) return -ENOTTY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) static const struct file_operations rc32434_wdt_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) .llseek = no_llseek,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) .write = rc32434_wdt_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) .unlocked_ioctl = rc32434_wdt_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) .compat_ioctl = compat_ptr_ioctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) .open = rc32434_wdt_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) .release = rc32434_wdt_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) static struct miscdevice rc32434_wdt_miscdev = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) .minor = WATCHDOG_MINOR,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) .name = "watchdog",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) .fops = &rc32434_wdt_fops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static int rc32434_wdt_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) struct resource *r;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) if (!r) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) pr_err("failed to retrieve resources\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) wdt_reg = devm_ioremap(&pdev->dev, r->start, resource_size(r));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (!wdt_reg) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) pr_err("failed to remap I/O resources\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) spin_lock_init(&rc32434_wdt_device.io_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) /* Make sure the watchdog is not running */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) rc32434_wdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) /* Check that the heartbeat value is within it's range;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) * if not reset to the default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) if (rc32434_wdt_set(timeout)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) rc32434_wdt_set(WATCHDOG_TIMEOUT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) pr_info("timeout value must be between 0 and %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) WTCOMP2SEC((u32)-1));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) ret = misc_register(&rc32434_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) pr_err("failed to register watchdog device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) pr_info("Watchdog Timer version " VERSION ", timer margin: %d sec\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) static int rc32434_wdt_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) misc_deregister(&rc32434_wdt_miscdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) static void rc32434_wdt_shutdown(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) rc32434_wdt_stop();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) static struct platform_driver rc32434_wdt_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) .probe = rc32434_wdt_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) .remove = rc32434_wdt_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) .shutdown = rc32434_wdt_shutdown,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) .name = "rc32434_wdt",
^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)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) module_platform_driver(rc32434_wdt_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) "Florian Fainelli <florian@openwrt.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) MODULE_LICENSE("GPL");