123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Watchdog Timer Driver
- * for ITE IT87xx Environment Control - Low Pin Count Input / Output
- *
- * (c) Copyright 2007 Oliver Schuster <[email protected]>
- *
- * Based on softdog.c by Alan Cox,
- * 83977f_wdt.c by Jose Goncalves,
- * it87.c by Chris Gauthron, Jean Delvare
- *
- * Data-sheets: Publicly available at the ITE website
- * http://www.ite.com.tw/
- *
- * Support of the watchdog timers, which are available on
- * IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
- * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
- * IT8772, IT8783 and IT8784.
- */
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/types.h>
- #include <linux/watchdog.h>
- #define WATCHDOG_NAME "IT87 WDT"
- /* Defaults for Module Parameter */
- #define DEFAULT_TIMEOUT 60
- #define DEFAULT_TESTMODE 0
- #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
- /* IO Ports */
- #define REG 0x2e
- #define VAL 0x2f
- /* Logical device Numbers LDN */
- #define GPIO 0x07
- /* Configuration Registers and Functions */
- #define LDNREG 0x07
- #define CHIPID 0x20
- #define CHIPREV 0x22
- /* Chip Id numbers */
- #define NO_DEV_ID 0xffff
- #define IT8607_ID 0x8607
- #define IT8620_ID 0x8620
- #define IT8622_ID 0x8622
- #define IT8625_ID 0x8625
- #define IT8628_ID 0x8628
- #define IT8655_ID 0x8655
- #define IT8665_ID 0x8665
- #define IT8686_ID 0x8686
- #define IT8702_ID 0x8702
- #define IT8705_ID 0x8705
- #define IT8712_ID 0x8712
- #define IT8716_ID 0x8716
- #define IT8718_ID 0x8718
- #define IT8720_ID 0x8720
- #define IT8721_ID 0x8721
- #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
- #define IT8728_ID 0x8728
- #define IT8772_ID 0x8772
- #define IT8783_ID 0x8783
- #define IT8784_ID 0x8784
- #define IT8786_ID 0x8786
- /* GPIO Configuration Registers LDN=0x07 */
- #define WDTCTRL 0x71
- #define WDTCFG 0x72
- #define WDTVALLSB 0x73
- #define WDTVALMSB 0x74
- /* GPIO Bits WDTCFG */
- #define WDT_TOV1 0x80
- #define WDT_KRST 0x40
- #define WDT_TOVE 0x20
- #define WDT_PWROK 0x10 /* not in it8721 */
- #define WDT_INT_MASK 0x0f
- static unsigned int max_units, chip_type;
- static unsigned int timeout = DEFAULT_TIMEOUT;
- static int testmode = DEFAULT_TESTMODE;
- static bool nowayout = DEFAULT_NOWAYOUT;
- module_param(timeout, int, 0);
- MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
- __MODULE_STRING(DEFAULT_TIMEOUT));
- module_param(testmode, int, 0);
- MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
- __MODULE_STRING(DEFAULT_TESTMODE));
- module_param(nowayout, bool, 0);
- MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
- __MODULE_STRING(WATCHDOG_NOWAYOUT));
- /* Superio Chip */
- static inline int superio_enter(void)
- {
- /*
- * Try to reserve REG and REG + 1 for exclusive access.
- */
- if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
- return -EBUSY;
- outb(0x87, REG);
- outb(0x01, REG);
- outb(0x55, REG);
- outb(0x55, REG);
- return 0;
- }
- static inline void superio_exit(void)
- {
- outb(0x02, REG);
- outb(0x02, VAL);
- release_region(REG, 2);
- }
- static inline void superio_select(int ldn)
- {
- outb(LDNREG, REG);
- outb(ldn, VAL);
- }
- static inline int superio_inb(int reg)
- {
- outb(reg, REG);
- return inb(VAL);
- }
- static inline void superio_outb(int val, int reg)
- {
- outb(reg, REG);
- outb(val, VAL);
- }
- static inline int superio_inw(int reg)
- {
- int val;
- outb(reg++, REG);
- val = inb(VAL) << 8;
- outb(reg, REG);
- val |= inb(VAL);
- return val;
- }
- /* Internal function, should be called after superio_select(GPIO) */
- static void _wdt_update_timeout(unsigned int t)
- {
- unsigned char cfg = WDT_KRST;
- if (testmode)
- cfg = 0;
- if (t <= max_units)
- cfg |= WDT_TOV1;
- else
- t /= 60;
- if (chip_type != IT8721_ID)
- cfg |= WDT_PWROK;
- superio_outb(cfg, WDTCFG);
- superio_outb(t, WDTVALLSB);
- if (max_units > 255)
- superio_outb(t >> 8, WDTVALMSB);
- }
- static int wdt_update_timeout(unsigned int t)
- {
- int ret;
- ret = superio_enter();
- if (ret)
- return ret;
- superio_select(GPIO);
- _wdt_update_timeout(t);
- superio_exit();
- return 0;
- }
- static int wdt_round_time(int t)
- {
- t += 59;
- t -= t % 60;
- return t;
- }
- /* watchdog timer handling */
- static int wdt_start(struct watchdog_device *wdd)
- {
- return wdt_update_timeout(wdd->timeout);
- }
- static int wdt_stop(struct watchdog_device *wdd)
- {
- return wdt_update_timeout(0);
- }
- /**
- * wdt_set_timeout - set a new timeout value with watchdog ioctl
- * @t: timeout value in seconds
- *
- * The hardware device has a 8 or 16 bit watchdog timer (depends on
- * chip version) that can be configured to count seconds or minutes.
- *
- * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
- */
- static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
- {
- int ret = 0;
- if (t > max_units)
- t = wdt_round_time(t);
- wdd->timeout = t;
- if (watchdog_hw_running(wdd))
- ret = wdt_update_timeout(t);
- return ret;
- }
- static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
- .firmware_version = 1,
- .identity = WATCHDOG_NAME,
- };
- static const struct watchdog_ops wdt_ops = {
- .owner = THIS_MODULE,
- .start = wdt_start,
- .stop = wdt_stop,
- .set_timeout = wdt_set_timeout,
- };
- static struct watchdog_device wdt_dev = {
- .info = &ident,
- .ops = &wdt_ops,
- .min_timeout = 1,
- };
- static int __init it87_wdt_init(void)
- {
- u8 chip_rev;
- int rc;
- rc = superio_enter();
- if (rc)
- return rc;
- chip_type = superio_inw(CHIPID);
- chip_rev = superio_inb(CHIPREV) & 0x0f;
- superio_exit();
- switch (chip_type) {
- case IT8702_ID:
- max_units = 255;
- break;
- case IT8712_ID:
- max_units = (chip_rev < 8) ? 255 : 65535;
- break;
- case IT8716_ID:
- case IT8726_ID:
- max_units = 65535;
- break;
- case IT8607_ID:
- case IT8620_ID:
- case IT8622_ID:
- case IT8625_ID:
- case IT8628_ID:
- case IT8655_ID:
- case IT8665_ID:
- case IT8686_ID:
- case IT8718_ID:
- case IT8720_ID:
- case IT8721_ID:
- case IT8728_ID:
- case IT8772_ID:
- case IT8783_ID:
- case IT8784_ID:
- case IT8786_ID:
- max_units = 65535;
- break;
- case IT8705_ID:
- pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
- chip_type, chip_rev);
- return -ENODEV;
- case NO_DEV_ID:
- pr_err("no device\n");
- return -ENODEV;
- default:
- pr_err("Unknown Chip found, Chip %04x Revision %04x\n",
- chip_type, chip_rev);
- return -ENODEV;
- }
- rc = superio_enter();
- if (rc)
- return rc;
- superio_select(GPIO);
- superio_outb(WDT_TOV1, WDTCFG);
- superio_outb(0x00, WDTCTRL);
- superio_exit();
- if (timeout < 1 || timeout > max_units * 60) {
- timeout = DEFAULT_TIMEOUT;
- pr_warn("Timeout value out of range, use default %d sec\n",
- DEFAULT_TIMEOUT);
- }
- if (timeout > max_units)
- timeout = wdt_round_time(timeout);
- wdt_dev.timeout = timeout;
- wdt_dev.max_timeout = max_units * 60;
- watchdog_stop_on_reboot(&wdt_dev);
- rc = watchdog_register_device(&wdt_dev);
- if (rc) {
- pr_err("Cannot register watchdog device (err=%d)\n", rc);
- return rc;
- }
- pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
- chip_type, chip_rev, timeout, nowayout, testmode);
- return 0;
- }
- static void __exit it87_wdt_exit(void)
- {
- watchdog_unregister_device(&wdt_dev);
- }
- module_init(it87_wdt_init);
- module_exit(it87_wdt_exit);
- MODULE_AUTHOR("Oliver Schuster");
- MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O");
- MODULE_LICENSE("GPL");
|