linux/drivers/watchdog/via_wdt.c
<<
>>
Prefs
   1/*
   2 * VIA Chipset Watchdog Driver
   3 *
   4 * Copyright (C) 2011 Sigfox
   5 * License terms: GNU General Public License (GPL) version 2
   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 watchog 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
  56static int timeout = WDT_TIMEOUT;
  57module_param(timeout, int, 0);
  58MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, between 1 and 1023 "
  59        "(default = " __MODULE_STRING(WDT_TIMEOUT) ")");
  60
  61static bool nowayout = WATCHDOG_NOWAYOUT;
  62module_param(nowayout, bool, 0);
  63MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  64        "(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  65
  66static struct watchdog_device wdt_dev;
  67static struct resource wdt_res;
  68static void __iomem *wdt_mem;
  69static unsigned int mmio;
  70static void wdt_timer_tick(unsigned long data);
  71static DEFINE_TIMER(timer, wdt_timer_tick, 0, 0);
  72                                        /* The timer that pings the watchdog */
  73static unsigned long next_heartbeat;    /* the next_heartbeat for the timer */
  74
  75static 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 watchog 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 */
  91static void wdt_timer_tick(unsigned long data)
  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
 101static 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
 108static 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
 119static 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
 127static 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
 135static 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
 143static 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
 151static 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
 158static 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        watchdog_set_nowayout(&wdt_dev, nowayout);
 210        if (readl(wdt_mem) & VIA_WDT_FIRED)
 211                wdt_dev.bootstatus |= WDIOF_CARDRESET;
 212
 213        ret = watchdog_register_device(&wdt_dev);
 214        if (ret)
 215                goto err_out_iounmap;
 216
 217        /* start triggering, in case of watchdog already enabled by BIOS */
 218        mod_timer(&timer, jiffies + WDT_HEARTBEAT);
 219        return 0;
 220
 221err_out_iounmap:
 222        iounmap(wdt_mem);
 223err_out_release:
 224        release_mem_region(mmio, VIA_WDT_MMIO_LEN);
 225err_out_resource:
 226        release_resource(&wdt_res);
 227err_out_disable_device:
 228        pci_disable_device(pdev);
 229        return ret;
 230}
 231
 232static void wdt_remove(struct pci_dev *pdev)
 233{
 234        watchdog_unregister_device(&wdt_dev);
 235        del_timer(&timer);
 236        iounmap(wdt_mem);
 237        release_mem_region(mmio, VIA_WDT_MMIO_LEN);
 238        release_resource(&wdt_res);
 239        pci_disable_device(pdev);
 240}
 241
 242static DEFINE_PCI_DEVICE_TABLE(wdt_pci_table) = {
 243        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_CX700) },
 244        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX800) },
 245        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
 246        { 0 }
 247};
 248
 249static struct pci_driver wdt_driver = {
 250        .name           = "via_wdt",
 251        .id_table       = wdt_pci_table,
 252        .probe          = wdt_probe,
 253        .remove         = wdt_remove,
 254};
 255
 256module_pci_driver(wdt_driver);
 257
 258MODULE_AUTHOR("Marc Vertes");
 259MODULE_DESCRIPTION("Driver for watchdog timer on VIA chipset");
 260MODULE_LICENSE("GPL");
 261
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.