linux/drivers/watchdog/intel-mid_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *      intel-mid_wdt: generic Intel MID SCU watchdog driver
   4 *
   5 *      Platforms supported so far:
   6 *      - Merrifield only
   7 *
   8 *      Copyright (C) 2014 Intel Corporation. All rights reserved.
   9 *      Contact: David Cohen <david.a.cohen@linux.intel.com>
  10 */
  11
  12#include <linux/interrupt.h>
  13#include <linux/module.h>
  14#include <linux/nmi.h>
  15#include <linux/platform_device.h>
  16#include <linux/watchdog.h>
  17#include <linux/platform_data/intel-mid_wdt.h>
  18
  19#include <asm/intel_scu_ipc.h>
  20#include <asm/intel-mid.h>
  21
  22#define IPC_WATCHDOG 0xf8
  23
  24#define MID_WDT_PRETIMEOUT              15
  25#define MID_WDT_TIMEOUT_MIN             (1 + MID_WDT_PRETIMEOUT)
  26#define MID_WDT_TIMEOUT_MAX             170
  27#define MID_WDT_DEFAULT_TIMEOUT         90
  28
  29/* SCU watchdog messages */
  30enum {
  31        SCU_WATCHDOG_START = 0,
  32        SCU_WATCHDOG_STOP,
  33        SCU_WATCHDOG_KEEPALIVE,
  34};
  35
  36struct mid_wdt {
  37        struct watchdog_device wd;
  38        struct device *dev;
  39        struct intel_scu_ipc_dev *scu;
  40};
  41
  42static inline int
  43wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
  44{
  45        struct intel_scu_ipc_dev *scu = mid->scu;
  46
  47        return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
  48                                                   inlen, size, NULL, 0);
  49}
  50
  51static int wdt_start(struct watchdog_device *wd)
  52{
  53        struct mid_wdt *mid = watchdog_get_drvdata(wd);
  54        int ret, in_size;
  55        int timeout = wd->timeout;
  56        struct ipc_wd_start {
  57                u32 pretimeout;
  58                u32 timeout;
  59        } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
  60
  61        /*
  62         * SCU expects the input size for watchdog IPC to be 2 which is the
  63         * size of the structure in dwords. SCU IPC normally takes bytes
  64         * but this is a special case where we specify size to be different
  65         * than inlen.
  66         */
  67        in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
  68
  69        ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
  70                          sizeof(ipc_wd_start), in_size);
  71        if (ret)
  72                dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
  73
  74        return ret;
  75}
  76
  77static int wdt_ping(struct watchdog_device *wd)
  78{
  79        struct mid_wdt *mid = watchdog_get_drvdata(wd);
  80        int ret;
  81
  82        ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
  83        if (ret)
  84                dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
  85
  86        return ret;
  87}
  88
  89static int wdt_stop(struct watchdog_device *wd)
  90{
  91        struct mid_wdt *mid = watchdog_get_drvdata(wd);
  92        int ret;
  93
  94        ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
  95        if (ret)
  96                dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
  97
  98        return ret;
  99}
 100
 101static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
 102{
 103        panic("Kernel Watchdog");
 104
 105        /* This code should not be reached */
 106        return IRQ_HANDLED;
 107}
 108
 109static const struct watchdog_info mid_wdt_info = {
 110        .identity = "Intel MID SCU watchdog",
 111        .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 112};
 113
 114static const struct watchdog_ops mid_wdt_ops = {
 115        .owner = THIS_MODULE,
 116        .start = wdt_start,
 117        .stop = wdt_stop,
 118        .ping = wdt_ping,
 119};
 120
 121static int mid_wdt_probe(struct platform_device *pdev)
 122{
 123        struct device *dev = &pdev->dev;
 124        struct watchdog_device *wdt_dev;
 125        struct intel_mid_wdt_pdata *pdata = dev->platform_data;
 126        struct mid_wdt *mid;
 127        int ret;
 128
 129        if (!pdata) {
 130                dev_err(dev, "missing platform data\n");
 131                return -EINVAL;
 132        }
 133
 134        if (pdata->probe) {
 135                ret = pdata->probe(pdev);
 136                if (ret)
 137                        return ret;
 138        }
 139
 140        mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
 141        if (!mid)
 142                return -ENOMEM;
 143
 144        mid->dev = dev;
 145        wdt_dev = &mid->wd;
 146
 147        wdt_dev->info = &mid_wdt_info;
 148        wdt_dev->ops = &mid_wdt_ops;
 149        wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
 150        wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
 151        wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
 152        wdt_dev->parent = dev;
 153
 154        watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
 155        watchdog_set_drvdata(wdt_dev, mid);
 156
 157        mid->scu = devm_intel_scu_ipc_dev_get(dev);
 158        if (!mid->scu)
 159                return -EPROBE_DEFER;
 160
 161        ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
 162                               IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
 163                               wdt_dev);
 164        if (ret) {
 165                dev_err(dev, "error requesting warning irq %d\n", pdata->irq);
 166                return ret;
 167        }
 168
 169        /*
 170         * The firmware followed by U-Boot leaves the watchdog running
 171         * with the default threshold which may vary. When we get here
 172         * we should make a decision to prevent any side effects before
 173         * user space daemon will take care of it. The best option,
 174         * taking into consideration that there is no way to read values
 175         * back from hardware, is to enforce watchdog being run with
 176         * deterministic values.
 177         */
 178        ret = wdt_start(wdt_dev);
 179        if (ret)
 180                return ret;
 181
 182        /* Make sure the watchdog is serviced */
 183        set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
 184
 185        ret = devm_watchdog_register_device(dev, wdt_dev);
 186        if (ret)
 187                return ret;
 188
 189        dev_info(dev, "Intel MID watchdog device probed\n");
 190
 191        return 0;
 192}
 193
 194static struct platform_driver mid_wdt_driver = {
 195        .probe          = mid_wdt_probe,
 196        .driver         = {
 197                .name   = "intel_mid_wdt",
 198        },
 199};
 200
 201module_platform_driver(mid_wdt_driver);
 202
 203MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
 204MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
 205MODULE_LICENSE("GPL");
 206