root/drivers/watchdog/via_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wdt_reset
  2. wdt_timer_tick
  3. wdt_ping
  4. wdt_start
  5. wdt_stop
  6. wdt_set_timeout
  7. wdt_probe
  8. wdt_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * VIA Chipset Watchdog Driver
   4  *
   5  * Copyright (C) 2011 Sigfox
   6  * Author: Marc Vertes <marc.vertes@sigfox.com>
   7  * Based on a preliminary version from Harald Welte <HaraldWelte@viatech.com>
   8  * Timer code by Wim Van Sebroeck <wim@iguana.be>
   9  *
  10  * Caveat: PnP must be enabled in BIOS to allow full access to watchdog
  11  * control registers. If not, the watchdog must be configured in BIOS manually.
  12  */
  13 
  14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15 
  16 #include <linux/device.h>
  17 #include <linux/io.h>
  18 #include <linux/jiffies.h>
  19 #include <linux/module.h>
  20 #include <linux/pci.h>
  21 #include <linux/timer.h>
  22 #include <linux/watchdog.h>
  23 
  24 /* Configuration registers relative to the pci device */
  25 #define VIA_WDT_MMIO_BASE       0xe8    /* MMIO region base address */
  26 #define VIA_WDT_CONF            0xec    /* watchdog enable state */
  27 
  28 /* Relevant bits for the VIA_WDT_CONF register */
  29 #define VIA_WDT_CONF_ENABLE     0x01    /* 1: enable watchdog */
  30 #define VIA_WDT_CONF_MMIO       0x02    /* 1: enable watchdog MMIO */
  31 
  32 /*
  33  * The MMIO region contains the watchdog control register and the
  34  * hardware timer counter.
  35  */
  36 #define VIA_WDT_MMIO_LEN        8       /* MMIO region length in bytes */
  37 #define VIA_WDT_CTL             0       /* MMIO addr+0: state/control reg. */
  38 #define VIA_WDT_COUNT           4       /* MMIO addr+4: timer counter reg. */
  39 
  40 /* Bits for the VIA_WDT_CTL register */
  41 #define VIA_WDT_RUNNING         0x01    /* 0: stop, 1: running */
  42 #define VIA_WDT_FIRED           0x02    /* 1: restarted by expired watchdog */
  43 #define VIA_WDT_PWROFF          0x04    /* 0: reset, 1: poweroff */
  44 #define VIA_WDT_DISABLED        0x08    /* 1: timer is disabled */
  45 #define VIA_WDT_TRIGGER         0x80    /* 1: start a new countdown */
  46 
  47 /* Hardware heartbeat in seconds */
  48 #define WDT_HW_HEARTBEAT 1
  49 
  50 /* Timer heartbeat (500ms) */
  51 #define WDT_HEARTBEAT   (HZ/2)  /* should be <= ((WDT_HW_HEARTBEAT*HZ)/2) */
  52 
  53 /* User space timeout in seconds */
  54 #define WDT_TIMEOUT_MAX 1023    /* approx. 17 min. */
  55 #define WDT_TIMEOUT     60
  56 static int timeout = WDT_TIMEOUT;
  57 module_param(timeout, int, 0);
  58 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
  59         "(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
  60 
  61 static bool nowayout = WATCHDOG_NOWAYOUT;
  62 module_param(nowayout, bool, 0);
  63 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  64         "(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  65 
  66 static struct watchdog_device wdt_dev;
  67 static struct resource wdt_res;
  68 static void __iomem *wdt_mem;
  69 static unsigned int mmio;
  70 static void wdt_timer_tick(struct timer_list *unused);
  71 static DEFINE_TIMER(timer, wdt_timer_tick);
  72                                         /* The timer that pings the watchdog */
  73 static unsigned long next_heartbeat;    /* the next_heartbeat for the timer */
  74 
  75 static inline void wdt_reset(void)
  76 {
  77         unsigned int ctl = readl(wdt_mem);
  78 
  79         writel(ctl | VIA_WDT_TRIGGER, wdt_mem);
  80 }
  81 
  82 /*
  83  * Timer tick: the timer will make sure that the watchdog timer hardware
  84  * is being reset in time. The conditions to do this are:
  85  *  1) the watchdog timer has been started and /dev/watchdog is open
  86  *     and there is still time left before userspace should send the
  87  *     next heartbeat/ping. (note: the internal heartbeat is much smaller
  88  *     then the external/userspace heartbeat).
  89  *  2) the watchdog timer has been stopped by userspace.
  90  */
  91 static void wdt_timer_tick(struct timer_list *unused)
  92 {
  93         if (time_before(jiffies, next_heartbeat) ||
  94            (!watchdog_active(&wdt_dev))) {
  95                 wdt_reset();
  96                 mod_timer(&timer, jiffies + WDT_HEARTBEAT);
  97         } else
  98                 pr_crit("I will reboot your machine !\n");
  99 }
 100 
 101 static int wdt_ping(struct watchdog_device *wdd)
 102 {
 103         /* calculate when the next userspace timeout will be */
 104         next_heartbeat = jiffies + wdd->timeout * HZ;
 105         return 0;
 106 }
 107 
 108 static int wdt_start(struct watchdog_device *wdd)
 109 {
 110         unsigned int ctl = readl(wdt_mem);
 111 
 112         writel(wdd->timeout, wdt_mem + VIA_WDT_COUNT);
 113         writel(ctl | VIA_WDT_RUNNING | VIA_WDT_TRIGGER, wdt_mem);
 114         wdt_ping(wdd);
 115         mod_timer(&timer, jiffies + WDT_HEARTBEAT);
 116         return 0;
 117 }
 118 
 119 static int wdt_stop(struct watchdog_device *wdd)
 120 {
 121         unsigned int ctl = readl(wdt_mem);
 122 
 123         writel(ctl & ~VIA_WDT_RUNNING, wdt_mem);
 124         return 0;
 125 }
 126 
 127 static int wdt_set_timeout(struct watchdog_device *wdd,
 128                            unsigned int new_timeout)
 129 {
 130         writel(new_timeout, wdt_mem + VIA_WDT_COUNT);
 131         wdd->timeout = new_timeout;
 132         return 0;
 133 }
 134 
 135 static const struct watchdog_info wdt_info = {
 136         .identity =     "VIA watchdog",
 137         .options =      WDIOF_CARDRESET |
 138                         WDIOF_SETTIMEOUT |
 139                         WDIOF_MAGICCLOSE |
 140                         WDIOF_KEEPALIVEPING,
 141 };
 142 
 143 static const struct watchdog_ops wdt_ops = {
 144         .owner =        THIS_MODULE,
 145         .start =        wdt_start,
 146         .stop =         wdt_stop,
 147         .ping =         wdt_ping,
 148         .set_timeout =  wdt_set_timeout,
 149 };
 150 
 151 static struct watchdog_device wdt_dev = {
 152         .info =         &wdt_info,
 153         .ops =          &wdt_ops,
 154         .min_timeout =  1,
 155         .max_timeout =  WDT_TIMEOUT_MAX,
 156 };
 157 
 158 static int wdt_probe(struct pci_dev *pdev,
 159                                const struct pci_device_id *ent)
 160 {
 161         unsigned char conf;
 162         int ret = -ENODEV;
 163 
 164         if (pci_enable_device(pdev)) {
 165                 dev_err(&pdev->dev, "cannot enable PCI device\n");
 166                 return -ENODEV;
 167         }
 168 
 169         /*
 170          * Allocate a MMIO region which contains watchdog control register
 171          * and counter, then configure the watchdog to use this region.
 172          * This is possible only if PnP is properly enabled in BIOS.
 173          * If not, the watchdog must be configured in BIOS manually.
 174          */
 175         if (allocate_resource(&iomem_resource, &wdt_res, VIA_WDT_MMIO_LEN,
 176                               0xf0000000, 0xffffff00, 0xff, NULL, NULL)) {
 177                 dev_err(&pdev->dev, "MMIO allocation failed\n");
 178                 goto err_out_disable_device;
 179         }
 180 
 181         pci_write_config_dword(pdev, VIA_WDT_MMIO_BASE, wdt_res.start);
 182         pci_read_config_byte(pdev, VIA_WDT_CONF, &conf);
 183         conf |= VIA_WDT_CONF_ENABLE | VIA_WDT_CONF_MMIO;
 184         pci_write_config_byte(pdev, VIA_WDT_CONF, conf);
 185 
 186         pci_read_config_dword(pdev, VIA_WDT_MMIO_BASE, &mmio);
 187         if (mmio) {
 188                 dev_info(&pdev->dev, "VIA Chipset watchdog MMIO: %x\n", mmio);
 189         } else {
 190                 dev_err(&pdev->dev, "MMIO setting failed. Check BIOS.\n");
 191                 goto err_out_resource;
 192         }
 193 
 194         if (!request_mem_region(mmio, VIA_WDT_MMIO_LEN, "via_wdt")) {
 195                 dev_err(&pdev->dev, "MMIO region busy\n");
 196                 goto err_out_resource;
 197         }
 198 
 199         wdt_mem = ioremap(mmio, VIA_WDT_MMIO_LEN);
 200         if (wdt_mem == NULL) {
 201                 dev_err(&pdev->dev, "cannot remap VIA wdt MMIO registers\n");
 202                 goto err_out_release;
 203         }
 204 
 205         if (timeout < 1 || timeout > WDT_TIMEOUT_MAX)
 206                 timeout = WDT_TIMEOUT;
 207 
 208         wdt_dev.timeout = timeout;
 209         wdt_dev.parent = &pdev->dev;
 210         watchdog_set_nowayout(&wdt_dev, nowayout);
 211         if (readl(wdt_mem) & VIA_WDT_FIRED)
 212                 wdt_dev.bootstatus |= WDIOF_CARDRESET;
 213 
 214         ret = watchdog_register_device(&wdt_dev);
 215         if (ret)
 216                 goto err_out_iounmap;
 217 
 218         /* start triggering, in case of watchdog already enabled by BIOS */
 219         mod_timer(&timer, jiffies + WDT_HEARTBEAT);
 220         return 0;
 221 
 222 err_out_iounmap:
 223         iounmap(wdt_mem);
 224 err_out_release:
 225         release_mem_region(mmio, VIA_WDT_MMIO_LEN);
 226 err_out_resource:
 227         release_resource(&wdt_res);
 228 err_out_disable_device:
 229         pci_disable_device(pdev);
 230         return ret;
 231 }
 232 
 233 static void wdt_remove(struct pci_dev *pdev)
 234 {
 235         watchdog_unregister_device(&wdt_dev);
 236         del_timer_sync(&timer);
 237         iounmap(wdt_mem);
 238         release_mem_region(mmio, VIA_WDT_MMIO_LEN);
 239         release_resource(&wdt_res);
 240         pci_disable_device(pdev);
 241 }
 242 
 243 static const struct pci_device_id wdt_pci_table[] = {
 244         { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
 245         { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
 246         { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
 247         { 0 }
 248 };
 249 
 250 static struct pci_driver wdt_driver = {
 251         .name           = "via_wdt",
 252         .id_table       = wdt_pci_table,
 253         .probe          = wdt_probe,
 254         .remove         = wdt_remove,
 255 };
 256 
 257 module_pci_driver(wdt_driver);
 258 
 259 MODULE_AUTHOR("Marc Vertes");
 260 MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
 261 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */