12e62c498SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0 2c36a483dSWilliam Breathitt Gray /* 3c36a483dSWilliam Breathitt Gray * Watchdog timer driver for the WinSystems EBC-C384 4c36a483dSWilliam Breathitt Gray * Copyright (C) 2016 William Breathitt Gray 5c36a483dSWilliam Breathitt Gray */ 6c36a483dSWilliam Breathitt Gray #include <linux/device.h> 7c36a483dSWilliam Breathitt Gray #include <linux/dmi.h> 8c36a483dSWilliam Breathitt Gray #include <linux/errno.h> 9c36a483dSWilliam Breathitt Gray #include <linux/io.h> 10c36a483dSWilliam Breathitt Gray #include <linux/ioport.h> 114ef1bec4SWilliam Breathitt Gray #include <linux/isa.h> 12c36a483dSWilliam Breathitt Gray #include <linux/kernel.h> 13c36a483dSWilliam Breathitt Gray #include <linux/module.h> 14c36a483dSWilliam Breathitt Gray #include <linux/moduleparam.h> 15c36a483dSWilliam Breathitt Gray #include <linux/types.h> 16c36a483dSWilliam Breathitt Gray #include <linux/watchdog.h> 17c36a483dSWilliam Breathitt Gray 18c36a483dSWilliam Breathitt Gray #define MODULE_NAME "ebc-c384_wdt" 19c36a483dSWilliam Breathitt Gray #define WATCHDOG_TIMEOUT 60 20c36a483dSWilliam Breathitt Gray /* 21c36a483dSWilliam Breathitt Gray * The timeout value in minutes must fit in a single byte when sent to the 22c36a483dSWilliam Breathitt Gray * watchdog timer; the maximum timeout possible is 15300 (255 * 60) seconds. 23c36a483dSWilliam Breathitt Gray */ 24c36a483dSWilliam Breathitt Gray #define WATCHDOG_MAX_TIMEOUT 15300 25c36a483dSWilliam Breathitt Gray #define BASE_ADDR 0x564 26c36a483dSWilliam Breathitt Gray #define ADDR_EXTENT 5 27c36a483dSWilliam Breathitt Gray #define CFG_ADDR (BASE_ADDR + 1) 28c36a483dSWilliam Breathitt Gray #define PET_ADDR (BASE_ADDR + 2) 29c36a483dSWilliam Breathitt Gray 30c36a483dSWilliam Breathitt Gray static bool nowayout = WATCHDOG_NOWAYOUT; 31c36a483dSWilliam Breathitt Gray module_param(nowayout, bool, 0); 32c36a483dSWilliam Breathitt Gray MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 33c36a483dSWilliam Breathitt Gray __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 34c36a483dSWilliam Breathitt Gray 35c36a483dSWilliam Breathitt Gray static unsigned timeout; 36c36a483dSWilliam Breathitt Gray module_param(timeout, uint, 0); 37c36a483dSWilliam Breathitt Gray MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" 38c36a483dSWilliam Breathitt Gray __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 39c36a483dSWilliam Breathitt Gray 40c36a483dSWilliam Breathitt Gray static int ebc_c384_wdt_start(struct watchdog_device *wdev) 41c36a483dSWilliam Breathitt Gray { 42c36a483dSWilliam Breathitt Gray unsigned t = wdev->timeout; 43c36a483dSWilliam Breathitt Gray 44c36a483dSWilliam Breathitt Gray /* resolution is in minutes for timeouts greater than 255 seconds */ 45c36a483dSWilliam Breathitt Gray if (t > 255) 46c36a483dSWilliam Breathitt Gray t = DIV_ROUND_UP(t, 60); 47c36a483dSWilliam Breathitt Gray 48c36a483dSWilliam Breathitt Gray outb(t, PET_ADDR); 49c36a483dSWilliam Breathitt Gray 50c36a483dSWilliam Breathitt Gray return 0; 51c36a483dSWilliam Breathitt Gray } 52c36a483dSWilliam Breathitt Gray 53c36a483dSWilliam Breathitt Gray static int ebc_c384_wdt_stop(struct watchdog_device *wdev) 54c36a483dSWilliam Breathitt Gray { 55c36a483dSWilliam Breathitt Gray outb(0x00, PET_ADDR); 56c36a483dSWilliam Breathitt Gray 57c36a483dSWilliam Breathitt Gray return 0; 58c36a483dSWilliam Breathitt Gray } 59c36a483dSWilliam Breathitt Gray 60c36a483dSWilliam Breathitt Gray static int ebc_c384_wdt_set_timeout(struct watchdog_device *wdev, unsigned t) 61c36a483dSWilliam Breathitt Gray { 62c36a483dSWilliam Breathitt Gray /* resolution is in minutes for timeouts greater than 255 seconds */ 63c36a483dSWilliam Breathitt Gray if (t > 255) { 64c36a483dSWilliam Breathitt Gray /* round second resolution up to minute granularity */ 65c36a483dSWilliam Breathitt Gray wdev->timeout = roundup(t, 60); 66c36a483dSWilliam Breathitt Gray 67c36a483dSWilliam Breathitt Gray /* set watchdog timer for minutes */ 68c36a483dSWilliam Breathitt Gray outb(0x00, CFG_ADDR); 69c36a483dSWilliam Breathitt Gray } else { 70c36a483dSWilliam Breathitt Gray wdev->timeout = t; 71c36a483dSWilliam Breathitt Gray 72c36a483dSWilliam Breathitt Gray /* set watchdog timer for seconds */ 73c36a483dSWilliam Breathitt Gray outb(0x80, CFG_ADDR); 74c36a483dSWilliam Breathitt Gray } 75c36a483dSWilliam Breathitt Gray 76c36a483dSWilliam Breathitt Gray return 0; 77c36a483dSWilliam Breathitt Gray } 78c36a483dSWilliam Breathitt Gray 79c36a483dSWilliam Breathitt Gray static const struct watchdog_ops ebc_c384_wdt_ops = { 80c36a483dSWilliam Breathitt Gray .start = ebc_c384_wdt_start, 81c36a483dSWilliam Breathitt Gray .stop = ebc_c384_wdt_stop, 82c36a483dSWilliam Breathitt Gray .set_timeout = ebc_c384_wdt_set_timeout 83c36a483dSWilliam Breathitt Gray }; 84c36a483dSWilliam Breathitt Gray 85c36a483dSWilliam Breathitt Gray static const struct watchdog_info ebc_c384_wdt_info = { 86c36a483dSWilliam Breathitt Gray .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT, 87c36a483dSWilliam Breathitt Gray .identity = MODULE_NAME 88c36a483dSWilliam Breathitt Gray }; 89c36a483dSWilliam Breathitt Gray 904ef1bec4SWilliam Breathitt Gray static int ebc_c384_wdt_probe(struct device *dev, unsigned int id) 91c36a483dSWilliam Breathitt Gray { 92c36a483dSWilliam Breathitt Gray struct watchdog_device *wdd; 93c36a483dSWilliam Breathitt Gray 94c36a483dSWilliam Breathitt Gray if (!devm_request_region(dev, BASE_ADDR, ADDR_EXTENT, dev_name(dev))) { 95c36a483dSWilliam Breathitt Gray dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 96c36a483dSWilliam Breathitt Gray BASE_ADDR, BASE_ADDR + ADDR_EXTENT); 97c36a483dSWilliam Breathitt Gray return -EBUSY; 98c36a483dSWilliam Breathitt Gray } 99c36a483dSWilliam Breathitt Gray 100c36a483dSWilliam Breathitt Gray wdd = devm_kzalloc(dev, sizeof(*wdd), GFP_KERNEL); 101c36a483dSWilliam Breathitt Gray if (!wdd) 102c36a483dSWilliam Breathitt Gray return -ENOMEM; 103c36a483dSWilliam Breathitt Gray 104c36a483dSWilliam Breathitt Gray wdd->info = &ebc_c384_wdt_info; 105c36a483dSWilliam Breathitt Gray wdd->ops = &ebc_c384_wdt_ops; 106c36a483dSWilliam Breathitt Gray wdd->timeout = WATCHDOG_TIMEOUT; 107c36a483dSWilliam Breathitt Gray wdd->min_timeout = 1; 108c36a483dSWilliam Breathitt Gray wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; 109c36a483dSWilliam Breathitt Gray 110c36a483dSWilliam Breathitt Gray watchdog_set_nowayout(wdd, nowayout); 111*cccbf8baSWolfram Sang watchdog_init_timeout(wdd, timeout, dev); 112c36a483dSWilliam Breathitt Gray 113540aea3dSWilliam Breathitt Gray return devm_watchdog_register_device(dev, wdd); 114c36a483dSWilliam Breathitt Gray } 115c36a483dSWilliam Breathitt Gray 1164ef1bec4SWilliam Breathitt Gray static struct isa_driver ebc_c384_wdt_driver = { 1174ef1bec4SWilliam Breathitt Gray .probe = ebc_c384_wdt_probe, 118c36a483dSWilliam Breathitt Gray .driver = { 119c36a483dSWilliam Breathitt Gray .name = MODULE_NAME 120c36a483dSWilliam Breathitt Gray }, 121c36a483dSWilliam Breathitt Gray }; 122c36a483dSWilliam Breathitt Gray 123c36a483dSWilliam Breathitt Gray static int __init ebc_c384_wdt_init(void) 124c36a483dSWilliam Breathitt Gray { 125c36a483dSWilliam Breathitt Gray if (!dmi_match(DMI_BOARD_NAME, "EBC-C384 SBC")) 126c36a483dSWilliam Breathitt Gray return -ENODEV; 127c36a483dSWilliam Breathitt Gray 1284ef1bec4SWilliam Breathitt Gray return isa_register_driver(&ebc_c384_wdt_driver, 1); 129c36a483dSWilliam Breathitt Gray } 130c36a483dSWilliam Breathitt Gray 131c36a483dSWilliam Breathitt Gray static void __exit ebc_c384_wdt_exit(void) 132c36a483dSWilliam Breathitt Gray { 1334ef1bec4SWilliam Breathitt Gray isa_unregister_driver(&ebc_c384_wdt_driver); 134c36a483dSWilliam Breathitt Gray } 135c36a483dSWilliam Breathitt Gray 136c36a483dSWilliam Breathitt Gray module_init(ebc_c384_wdt_init); 137c36a483dSWilliam Breathitt Gray module_exit(ebc_c384_wdt_exit); 138c36a483dSWilliam Breathitt Gray 139c36a483dSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 140c36a483dSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems EBC-C384 watchdog timer driver"); 141c36a483dSWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 1424ef1bec4SWilliam Breathitt Gray MODULE_ALIAS("isa:" MODULE_NAME); 143