1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Mediatek Watchdog Driver 4 * 5 * Copyright (C) 2014 Matthias Brugger 6 * 7 * Matthias Brugger <matthias.bgg@gmail.com> 8 * 9 * Based on sunxi_wdt.c 10 */ 11 12 #include <linux/err.h> 13 #include <linux/init.h> 14 #include <linux/io.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/moduleparam.h> 18 #include <linux/of.h> 19 #include <linux/platform_device.h> 20 #include <linux/types.h> 21 #include <linux/watchdog.h> 22 #include <linux/delay.h> 23 24 #define WDT_MAX_TIMEOUT 31 25 #define WDT_MIN_TIMEOUT 1 26 #define WDT_LENGTH_TIMEOUT(n) ((n) << 5) 27 28 #define WDT_LENGTH 0x04 29 #define WDT_LENGTH_KEY 0x8 30 31 #define WDT_RST 0x08 32 #define WDT_RST_RELOAD 0x1971 33 34 #define WDT_MODE 0x00 35 #define WDT_MODE_EN (1 << 0) 36 #define WDT_MODE_EXT_POL_LOW (0 << 1) 37 #define WDT_MODE_EXT_POL_HIGH (1 << 1) 38 #define WDT_MODE_EXRST_EN (1 << 2) 39 #define WDT_MODE_IRQ_EN (1 << 3) 40 #define WDT_MODE_AUTO_START (1 << 4) 41 #define WDT_MODE_DUAL_EN (1 << 6) 42 #define WDT_MODE_KEY 0x22000000 43 44 #define WDT_SWRST 0x14 45 #define WDT_SWRST_KEY 0x1209 46 47 #define DRV_NAME "mtk-wdt" 48 #define DRV_VERSION "1.0" 49 50 static bool nowayout = WATCHDOG_NOWAYOUT; 51 static unsigned int timeout; 52 53 struct mtk_wdt_dev { 54 struct watchdog_device wdt_dev; 55 void __iomem *wdt_base; 56 }; 57 58 static int mtk_wdt_restart(struct watchdog_device *wdt_dev, 59 unsigned long action, void *data) 60 { 61 struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 62 void __iomem *wdt_base; 63 64 wdt_base = mtk_wdt->wdt_base; 65 66 while (1) { 67 writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST); 68 mdelay(5); 69 } 70 71 return 0; 72 } 73 74 static int mtk_wdt_ping(struct watchdog_device *wdt_dev) 75 { 76 struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 77 void __iomem *wdt_base = mtk_wdt->wdt_base; 78 79 iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST); 80 81 return 0; 82 } 83 84 static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, 85 unsigned int timeout) 86 { 87 struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 88 void __iomem *wdt_base = mtk_wdt->wdt_base; 89 u32 reg; 90 91 wdt_dev->timeout = timeout; 92 93 /* 94 * One bit is the value of 512 ticks 95 * The clock has 32 KHz 96 */ 97 reg = WDT_LENGTH_TIMEOUT(timeout << 6) | WDT_LENGTH_KEY; 98 iowrite32(reg, wdt_base + WDT_LENGTH); 99 100 mtk_wdt_ping(wdt_dev); 101 102 return 0; 103 } 104 105 static int mtk_wdt_stop(struct watchdog_device *wdt_dev) 106 { 107 struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 108 void __iomem *wdt_base = mtk_wdt->wdt_base; 109 u32 reg; 110 111 reg = readl(wdt_base + WDT_MODE); 112 reg &= ~WDT_MODE_EN; 113 reg |= WDT_MODE_KEY; 114 iowrite32(reg, wdt_base + WDT_MODE); 115 116 return 0; 117 } 118 119 static int mtk_wdt_start(struct watchdog_device *wdt_dev) 120 { 121 u32 reg; 122 struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 123 void __iomem *wdt_base = mtk_wdt->wdt_base; 124 int ret; 125 126 ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 127 if (ret < 0) 128 return ret; 129 130 reg = ioread32(wdt_base + WDT_MODE); 131 reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); 132 reg |= (WDT_MODE_EN | WDT_MODE_KEY); 133 iowrite32(reg, wdt_base + WDT_MODE); 134 135 return 0; 136 } 137 138 static const struct watchdog_info mtk_wdt_info = { 139 .identity = DRV_NAME, 140 .options = WDIOF_SETTIMEOUT | 141 WDIOF_KEEPALIVEPING | 142 WDIOF_MAGICCLOSE, 143 }; 144 145 static const struct watchdog_ops mtk_wdt_ops = { 146 .owner = THIS_MODULE, 147 .start = mtk_wdt_start, 148 .stop = mtk_wdt_stop, 149 .ping = mtk_wdt_ping, 150 .set_timeout = mtk_wdt_set_timeout, 151 .restart = mtk_wdt_restart, 152 }; 153 154 static int mtk_wdt_probe(struct platform_device *pdev) 155 { 156 struct mtk_wdt_dev *mtk_wdt; 157 struct resource *res; 158 int err; 159 160 mtk_wdt = devm_kzalloc(&pdev->dev, sizeof(*mtk_wdt), GFP_KERNEL); 161 if (!mtk_wdt) 162 return -ENOMEM; 163 164 platform_set_drvdata(pdev, mtk_wdt); 165 166 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 167 mtk_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); 168 if (IS_ERR(mtk_wdt->wdt_base)) 169 return PTR_ERR(mtk_wdt->wdt_base); 170 171 mtk_wdt->wdt_dev.info = &mtk_wdt_info; 172 mtk_wdt->wdt_dev.ops = &mtk_wdt_ops; 173 mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; 174 mtk_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; 175 mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; 176 mtk_wdt->wdt_dev.parent = &pdev->dev; 177 178 watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, &pdev->dev); 179 watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout); 180 watchdog_set_restart_priority(&mtk_wdt->wdt_dev, 128); 181 182 watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); 183 184 mtk_wdt_stop(&mtk_wdt->wdt_dev); 185 186 err = watchdog_register_device(&mtk_wdt->wdt_dev); 187 if (unlikely(err)) 188 return err; 189 190 dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", 191 mtk_wdt->wdt_dev.timeout, nowayout); 192 193 return 0; 194 } 195 196 static void mtk_wdt_shutdown(struct platform_device *pdev) 197 { 198 struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); 199 200 if (watchdog_active(&mtk_wdt->wdt_dev)) 201 mtk_wdt_stop(&mtk_wdt->wdt_dev); 202 } 203 204 static int mtk_wdt_remove(struct platform_device *pdev) 205 { 206 struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); 207 208 watchdog_unregister_device(&mtk_wdt->wdt_dev); 209 210 return 0; 211 } 212 213 #ifdef CONFIG_PM_SLEEP 214 static int mtk_wdt_suspend(struct device *dev) 215 { 216 struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); 217 218 if (watchdog_active(&mtk_wdt->wdt_dev)) 219 mtk_wdt_stop(&mtk_wdt->wdt_dev); 220 221 return 0; 222 } 223 224 static int mtk_wdt_resume(struct device *dev) 225 { 226 struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); 227 228 if (watchdog_active(&mtk_wdt->wdt_dev)) { 229 mtk_wdt_start(&mtk_wdt->wdt_dev); 230 mtk_wdt_ping(&mtk_wdt->wdt_dev); 231 } 232 233 return 0; 234 } 235 #endif 236 237 static const struct of_device_id mtk_wdt_dt_ids[] = { 238 { .compatible = "mediatek,mt6589-wdt" }, 239 { /* sentinel */ } 240 }; 241 MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); 242 243 static const struct dev_pm_ops mtk_wdt_pm_ops = { 244 SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend, 245 mtk_wdt_resume) 246 }; 247 248 static struct platform_driver mtk_wdt_driver = { 249 .probe = mtk_wdt_probe, 250 .remove = mtk_wdt_remove, 251 .shutdown = mtk_wdt_shutdown, 252 .driver = { 253 .name = DRV_NAME, 254 .pm = &mtk_wdt_pm_ops, 255 .of_match_table = mtk_wdt_dt_ids, 256 }, 257 }; 258 259 module_platform_driver(mtk_wdt_driver); 260 261 module_param(timeout, uint, 0); 262 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); 263 264 module_param(nowayout, bool, 0); 265 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 266 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 267 268 MODULE_LICENSE("GPL"); 269 MODULE_AUTHOR("Matthias Brugger <matthias.bgg@gmail.com>"); 270 MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver"); 271 MODULE_VERSION(DRV_VERSION); 272