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