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