0001
0002
0003
0004
0005
0006
0007
0008
0009
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
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
0063
0064
0065
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
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
0171
0172
0173
0174
0175
0176
0177
0178 ret = wdt_start(wdt_dev);
0179 if (ret)
0180 return ret;
0181
0182
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");