Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *      intel-mid_wdt: generic Intel MID SCU watchdog driver
0004  *
0005  *      Platforms supported so far:
0006  *      - Merrifield only
0007  *
0008  *      Copyright (C) 2014 Intel Corporation. All rights reserved.
0009  *      Contact: David Cohen <david.a.cohen@linux.intel.com>
0010  */
0011 
0012 #include <linux/interrupt.h>
0013 #include <linux/module.h>
0014 #include <linux/nmi.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/watchdog.h>
0017 #include <linux/platform_data/intel-mid_wdt.h>
0018 
0019 #include <asm/intel_scu_ipc.h>
0020 #include <asm/intel-mid.h>
0021 
0022 #define IPC_WATCHDOG 0xf8
0023 
0024 #define MID_WDT_PRETIMEOUT      15
0025 #define MID_WDT_TIMEOUT_MIN     (1 + MID_WDT_PRETIMEOUT)
0026 #define MID_WDT_TIMEOUT_MAX     170
0027 #define MID_WDT_DEFAULT_TIMEOUT     90
0028 
0029 /* SCU watchdog messages */
0030 enum {
0031     SCU_WATCHDOG_START = 0,
0032     SCU_WATCHDOG_STOP,
0033     SCU_WATCHDOG_KEEPALIVE,
0034 };
0035 
0036 struct mid_wdt {
0037     struct watchdog_device wd;
0038     struct device *dev;
0039     struct intel_scu_ipc_dev *scu;
0040 };
0041 
0042 static inline int
0043 wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
0044 {
0045     struct intel_scu_ipc_dev *scu = mid->scu;
0046 
0047     return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
0048                            inlen, size, NULL, 0);
0049 }
0050 
0051 static int wdt_start(struct watchdog_device *wd)
0052 {
0053     struct mid_wdt *mid = watchdog_get_drvdata(wd);
0054     int ret, in_size;
0055     int timeout = wd->timeout;
0056     struct ipc_wd_start {
0057         u32 pretimeout;
0058         u32 timeout;
0059     } ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
0060 
0061     /*
0062      * SCU expects the input size for watchdog IPC to be 2 which is the
0063      * size of the structure in dwords. SCU IPC normally takes bytes
0064      * but this is a special case where we specify size to be different
0065      * than inlen.
0066      */
0067     in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
0068 
0069     ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
0070               sizeof(ipc_wd_start), in_size);
0071     if (ret)
0072         dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
0073 
0074     return ret;
0075 }
0076 
0077 static int wdt_ping(struct watchdog_device *wd)
0078 {
0079     struct mid_wdt *mid = watchdog_get_drvdata(wd);
0080     int ret;
0081 
0082     ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
0083     if (ret)
0084         dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
0085 
0086     return ret;
0087 }
0088 
0089 static int wdt_stop(struct watchdog_device *wd)
0090 {
0091     struct mid_wdt *mid = watchdog_get_drvdata(wd);
0092     int ret;
0093 
0094     ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
0095     if (ret)
0096         dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
0097 
0098     return ret;
0099 }
0100 
0101 static irqreturn_t mid_wdt_irq(int irq, void *dev_id)
0102 {
0103     panic("Kernel Watchdog");
0104 
0105     /* This code should not be reached */
0106     return IRQ_HANDLED;
0107 }
0108 
0109 static const struct watchdog_info mid_wdt_info = {
0110     .identity = "Intel MID SCU watchdog",
0111     .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
0112 };
0113 
0114 static const struct watchdog_ops mid_wdt_ops = {
0115     .owner = THIS_MODULE,
0116     .start = wdt_start,
0117     .stop = wdt_stop,
0118     .ping = wdt_ping,
0119 };
0120 
0121 static int mid_wdt_probe(struct platform_device *pdev)
0122 {
0123     struct device *dev = &pdev->dev;
0124     struct watchdog_device *wdt_dev;
0125     struct intel_mid_wdt_pdata *pdata = dev->platform_data;
0126     struct mid_wdt *mid;
0127     int ret;
0128 
0129     if (!pdata) {
0130         dev_err(dev, "missing platform data\n");
0131         return -EINVAL;
0132     }
0133 
0134     if (pdata->probe) {
0135         ret = pdata->probe(pdev);
0136         if (ret)
0137             return ret;
0138     }
0139 
0140     mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
0141     if (!mid)
0142         return -ENOMEM;
0143 
0144     mid->dev = dev;
0145     wdt_dev = &mid->wd;
0146 
0147     wdt_dev->info = &mid_wdt_info;
0148     wdt_dev->ops = &mid_wdt_ops;
0149     wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
0150     wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
0151     wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
0152     wdt_dev->parent = dev;
0153 
0154     watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
0155     watchdog_set_drvdata(wdt_dev, mid);
0156 
0157     mid->scu = devm_intel_scu_ipc_dev_get(dev);
0158     if (!mid->scu)
0159         return -EPROBE_DEFER;
0160 
0161     ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
0162                    IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
0163                    wdt_dev);
0164     if (ret) {
0165         dev_err(dev, "error requesting warning irq %d\n", pdata->irq);
0166         return ret;
0167     }
0168 
0169     /*
0170      * The firmware followed by U-Boot leaves the watchdog running
0171      * with the default threshold which may vary. When we get here
0172      * we should make a decision to prevent any side effects before
0173      * user space daemon will take care of it. The best option,
0174      * taking into consideration that there is no way to read values
0175      * back from hardware, is to enforce watchdog being run with
0176      * deterministic values.
0177      */
0178     ret = wdt_start(wdt_dev);
0179     if (ret)
0180         return ret;
0181 
0182     /* Make sure the watchdog is serviced */
0183     set_bit(WDOG_HW_RUNNING, &wdt_dev->status);
0184 
0185     ret = devm_watchdog_register_device(dev, wdt_dev);
0186     if (ret)
0187         return ret;
0188 
0189     dev_info(dev, "Intel MID watchdog device probed\n");
0190 
0191     return 0;
0192 }
0193 
0194 static struct platform_driver mid_wdt_driver = {
0195     .probe      = mid_wdt_probe,
0196     .driver     = {
0197         .name   = "intel_mid_wdt",
0198     },
0199 };
0200 
0201 module_platform_driver(mid_wdt_driver);
0202 
0203 MODULE_AUTHOR("David Cohen <david.a.cohen@linux.intel.com>");
0204 MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform");
0205 MODULE_LICENSE("GPL");