12e62c498SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0+ 2a44a4553SMatthias Brugger /* 3a44a4553SMatthias Brugger * Mediatek Watchdog Driver 4a44a4553SMatthias Brugger * 5a44a4553SMatthias Brugger * Copyright (C) 2014 Matthias Brugger 6a44a4553SMatthias Brugger * 7a44a4553SMatthias Brugger * Matthias Brugger <matthias.bgg@gmail.com> 8a44a4553SMatthias Brugger * 9a44a4553SMatthias Brugger * Based on sunxi_wdt.c 10a44a4553SMatthias Brugger */ 11a44a4553SMatthias Brugger 12f07c776fSEnric Balletbo i Serra #include <dt-bindings/reset/mt2712-resets.h> 13f07c776fSEnric Balletbo i Serra #include <dt-bindings/reset/mt8183-resets.h> 14f07c776fSEnric Balletbo i Serra #include <dt-bindings/reset/mt8192-resets.h> 158c6b5ea6SChristine Zhu #include <dt-bindings/reset/mt8195-resets.h> 16c254e103Syong.liang #include <linux/delay.h> 17a44a4553SMatthias Brugger #include <linux/err.h> 18a44a4553SMatthias Brugger #include <linux/init.h> 19a44a4553SMatthias Brugger #include <linux/io.h> 20a44a4553SMatthias Brugger #include <linux/kernel.h> 21a44a4553SMatthias Brugger #include <linux/module.h> 22a44a4553SMatthias Brugger #include <linux/moduleparam.h> 23a44a4553SMatthias Brugger #include <linux/of.h> 24c254e103Syong.liang #include <linux/of_device.h> 25a44a4553SMatthias Brugger #include <linux/platform_device.h> 26c254e103Syong.liang #include <linux/reset-controller.h> 27a44a4553SMatthias Brugger #include <linux/types.h> 28a44a4553SMatthias Brugger #include <linux/watchdog.h> 291bbce779SWang Qing #include <linux/interrupt.h> 30a44a4553SMatthias Brugger 31a44a4553SMatthias Brugger #define WDT_MAX_TIMEOUT 31 321bbce779SWang Qing #define WDT_MIN_TIMEOUT 2 33a44a4553SMatthias Brugger #define WDT_LENGTH_TIMEOUT(n) ((n) << 5) 34a44a4553SMatthias Brugger 35a44a4553SMatthias Brugger #define WDT_LENGTH 0x04 36a44a4553SMatthias Brugger #define WDT_LENGTH_KEY 0x8 37a44a4553SMatthias Brugger 38a44a4553SMatthias Brugger #define WDT_RST 0x08 39a44a4553SMatthias Brugger #define WDT_RST_RELOAD 0x1971 40a44a4553SMatthias Brugger 41a44a4553SMatthias Brugger #define WDT_MODE 0x00 42a44a4553SMatthias Brugger #define WDT_MODE_EN (1 << 0) 43a44a4553SMatthias Brugger #define WDT_MODE_EXT_POL_LOW (0 << 1) 44a44a4553SMatthias Brugger #define WDT_MODE_EXT_POL_HIGH (1 << 1) 45a44a4553SMatthias Brugger #define WDT_MODE_EXRST_EN (1 << 2) 46a44a4553SMatthias Brugger #define WDT_MODE_IRQ_EN (1 << 3) 47a44a4553SMatthias Brugger #define WDT_MODE_AUTO_START (1 << 4) 48a44a4553SMatthias Brugger #define WDT_MODE_DUAL_EN (1 << 6) 49a44a4553SMatthias Brugger #define WDT_MODE_KEY 0x22000000 50a44a4553SMatthias Brugger 51a44a4553SMatthias Brugger #define WDT_SWRST 0x14 52a44a4553SMatthias Brugger #define WDT_SWRST_KEY 0x1209 53a44a4553SMatthias Brugger 54c254e103Syong.liang #define WDT_SWSYSRST 0x18U 55c254e103Syong.liang #define WDT_SWSYS_RST_KEY 0x88000000 56c254e103Syong.liang 57a44a4553SMatthias Brugger #define DRV_NAME "mtk-wdt" 58a44a4553SMatthias Brugger #define DRV_VERSION "1.0" 59a44a4553SMatthias Brugger 60a44a4553SMatthias Brugger static bool nowayout = WATCHDOG_NOWAYOUT; 61b82e6953SMarcus Folkesson static unsigned int timeout; 62a44a4553SMatthias Brugger 63a44a4553SMatthias Brugger struct mtk_wdt_dev { 64a44a4553SMatthias Brugger struct watchdog_device wdt_dev; 65a44a4553SMatthias Brugger void __iomem *wdt_base; 66c254e103Syong.liang spinlock_t lock; /* protects WDT_SWSYSRST reg */ 67c254e103Syong.liang struct reset_controller_dev rcdev; 6859b0f513SFengquan Chen bool disable_wdt_extrst; 69a44a4553SMatthias Brugger }; 70a44a4553SMatthias Brugger 71c254e103Syong.liang struct mtk_wdt_data { 72c254e103Syong.liang int toprgu_sw_rst_num; 73c254e103Syong.liang }; 74c254e103Syong.liang 759e5236e7Syong.liang static const struct mtk_wdt_data mt2712_data = { 769e5236e7Syong.liang .toprgu_sw_rst_num = MT2712_TOPRGU_SW_RST_NUM, 779e5236e7Syong.liang }; 789e5236e7Syong.liang 79c254e103Syong.liang static const struct mtk_wdt_data mt8183_data = { 80c254e103Syong.liang .toprgu_sw_rst_num = MT8183_TOPRGU_SW_RST_NUM, 81c254e103Syong.liang }; 82c254e103Syong.liang 83adc318a3SCrystal Guo static const struct mtk_wdt_data mt8192_data = { 84adc318a3SCrystal Guo .toprgu_sw_rst_num = MT8192_TOPRGU_SW_RST_NUM, 85adc318a3SCrystal Guo }; 86adc318a3SCrystal Guo 878c6b5ea6SChristine Zhu static const struct mtk_wdt_data mt8195_data = { 888c6b5ea6SChristine Zhu .toprgu_sw_rst_num = MT8195_TOPRGU_SW_RST_NUM, 898c6b5ea6SChristine Zhu }; 908c6b5ea6SChristine Zhu 91c254e103Syong.liang static int toprgu_reset_update(struct reset_controller_dev *rcdev, 92c254e103Syong.liang unsigned long id, bool assert) 93c254e103Syong.liang { 94c254e103Syong.liang unsigned int tmp; 95c254e103Syong.liang unsigned long flags; 96c254e103Syong.liang struct mtk_wdt_dev *data = 97c254e103Syong.liang container_of(rcdev, struct mtk_wdt_dev, rcdev); 98c254e103Syong.liang 99c254e103Syong.liang spin_lock_irqsave(&data->lock, flags); 100c254e103Syong.liang 101c254e103Syong.liang tmp = readl(data->wdt_base + WDT_SWSYSRST); 102c254e103Syong.liang if (assert) 103c254e103Syong.liang tmp |= BIT(id); 104c254e103Syong.liang else 105c254e103Syong.liang tmp &= ~BIT(id); 106c254e103Syong.liang tmp |= WDT_SWSYS_RST_KEY; 107c254e103Syong.liang writel(tmp, data->wdt_base + WDT_SWSYSRST); 108c254e103Syong.liang 109c254e103Syong.liang spin_unlock_irqrestore(&data->lock, flags); 110c254e103Syong.liang 111c254e103Syong.liang return 0; 112c254e103Syong.liang } 113c254e103Syong.liang 114c254e103Syong.liang static int toprgu_reset_assert(struct reset_controller_dev *rcdev, 115c254e103Syong.liang unsigned long id) 116c254e103Syong.liang { 117c254e103Syong.liang return toprgu_reset_update(rcdev, id, true); 118c254e103Syong.liang } 119c254e103Syong.liang 120c254e103Syong.liang static int toprgu_reset_deassert(struct reset_controller_dev *rcdev, 121c254e103Syong.liang unsigned long id) 122c254e103Syong.liang { 123c254e103Syong.liang return toprgu_reset_update(rcdev, id, false); 124c254e103Syong.liang } 125c254e103Syong.liang 126c254e103Syong.liang static int toprgu_reset(struct reset_controller_dev *rcdev, 127c254e103Syong.liang unsigned long id) 128c254e103Syong.liang { 129c254e103Syong.liang int ret; 130c254e103Syong.liang 131c254e103Syong.liang ret = toprgu_reset_assert(rcdev, id); 132c254e103Syong.liang if (ret) 133c254e103Syong.liang return ret; 134c254e103Syong.liang 135c254e103Syong.liang return toprgu_reset_deassert(rcdev, id); 136c254e103Syong.liang } 137c254e103Syong.liang 138c254e103Syong.liang static const struct reset_control_ops toprgu_reset_ops = { 139c254e103Syong.liang .assert = toprgu_reset_assert, 140c254e103Syong.liang .deassert = toprgu_reset_deassert, 141c254e103Syong.liang .reset = toprgu_reset, 142c254e103Syong.liang }; 143c254e103Syong.liang 144c254e103Syong.liang static int toprgu_register_reset_controller(struct platform_device *pdev, 145c254e103Syong.liang int rst_num) 146c254e103Syong.liang { 147c254e103Syong.liang int ret; 148c254e103Syong.liang struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev); 149c254e103Syong.liang 150c254e103Syong.liang spin_lock_init(&mtk_wdt->lock); 151c254e103Syong.liang 152c254e103Syong.liang mtk_wdt->rcdev.owner = THIS_MODULE; 153c254e103Syong.liang mtk_wdt->rcdev.nr_resets = rst_num; 154c254e103Syong.liang mtk_wdt->rcdev.ops = &toprgu_reset_ops; 155c254e103Syong.liang mtk_wdt->rcdev.of_node = pdev->dev.of_node; 156c254e103Syong.liang ret = devm_reset_controller_register(&pdev->dev, &mtk_wdt->rcdev); 157c254e103Syong.liang if (ret != 0) 158c254e103Syong.liang dev_err(&pdev->dev, 159c254e103Syong.liang "couldn't register wdt reset controller: %d\n", ret); 160c254e103Syong.liang return ret; 161c254e103Syong.liang } 162c254e103Syong.liang 1634d8b229dSGuenter Roeck static int mtk_wdt_restart(struct watchdog_device *wdt_dev, 1644d8b229dSGuenter Roeck unsigned long action, void *data) 165a44a4553SMatthias Brugger { 166e86adc3fSDamien Riegel struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 167a44a4553SMatthias Brugger void __iomem *wdt_base; 168a44a4553SMatthias Brugger 169a44a4553SMatthias Brugger wdt_base = mtk_wdt->wdt_base; 170a44a4553SMatthias Brugger 171a44a4553SMatthias Brugger while (1) { 172a44a4553SMatthias Brugger writel(WDT_SWRST_KEY, wdt_base + WDT_SWRST); 173a44a4553SMatthias Brugger mdelay(5); 174a44a4553SMatthias Brugger } 175a44a4553SMatthias Brugger 176e86adc3fSDamien Riegel return 0; 177a44a4553SMatthias Brugger } 178a44a4553SMatthias Brugger 179a44a4553SMatthias Brugger static int mtk_wdt_ping(struct watchdog_device *wdt_dev) 180a44a4553SMatthias Brugger { 181a44a4553SMatthias Brugger struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 182a44a4553SMatthias Brugger void __iomem *wdt_base = mtk_wdt->wdt_base; 183a44a4553SMatthias Brugger 184a44a4553SMatthias Brugger iowrite32(WDT_RST_RELOAD, wdt_base + WDT_RST); 185a44a4553SMatthias Brugger 186a44a4553SMatthias Brugger return 0; 187a44a4553SMatthias Brugger } 188a44a4553SMatthias Brugger 189a44a4553SMatthias Brugger static int mtk_wdt_set_timeout(struct watchdog_device *wdt_dev, 190a44a4553SMatthias Brugger unsigned int timeout) 191a44a4553SMatthias Brugger { 192a44a4553SMatthias Brugger struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 193a44a4553SMatthias Brugger void __iomem *wdt_base = mtk_wdt->wdt_base; 194a44a4553SMatthias Brugger u32 reg; 195a44a4553SMatthias Brugger 196a44a4553SMatthias Brugger wdt_dev->timeout = timeout; 1971bbce779SWang Qing /* 1981bbce779SWang Qing * In dual mode, irq will be triggered at timeout / 2 1991bbce779SWang Qing * the real timeout occurs at timeout 2001bbce779SWang Qing */ 2011bbce779SWang Qing if (wdt_dev->pretimeout) 2021bbce779SWang Qing wdt_dev->pretimeout = timeout / 2; 203a44a4553SMatthias Brugger 204a44a4553SMatthias Brugger /* 205a44a4553SMatthias Brugger * One bit is the value of 512 ticks 206a44a4553SMatthias Brugger * The clock has 32 KHz 207a44a4553SMatthias Brugger */ 2081bbce779SWang Qing reg = WDT_LENGTH_TIMEOUT((timeout - wdt_dev->pretimeout) << 6) 2091bbce779SWang Qing | WDT_LENGTH_KEY; 210a44a4553SMatthias Brugger iowrite32(reg, wdt_base + WDT_LENGTH); 211a44a4553SMatthias Brugger 212a44a4553SMatthias Brugger mtk_wdt_ping(wdt_dev); 213a44a4553SMatthias Brugger 214a44a4553SMatthias Brugger return 0; 215a44a4553SMatthias Brugger } 216a44a4553SMatthias Brugger 217bbece05cSfreddy.hsin static void mtk_wdt_init(struct watchdog_device *wdt_dev) 218bbece05cSfreddy.hsin { 219bbece05cSfreddy.hsin struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 220bbece05cSfreddy.hsin void __iomem *wdt_base; 221bbece05cSfreddy.hsin 222bbece05cSfreddy.hsin wdt_base = mtk_wdt->wdt_base; 223bbece05cSfreddy.hsin 224bbece05cSfreddy.hsin if (readl(wdt_base + WDT_MODE) & WDT_MODE_EN) { 225bbece05cSfreddy.hsin set_bit(WDOG_HW_RUNNING, &wdt_dev->status); 226bbece05cSfreddy.hsin mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 227bbece05cSfreddy.hsin } 228bbece05cSfreddy.hsin } 229bbece05cSfreddy.hsin 230a44a4553SMatthias Brugger static int mtk_wdt_stop(struct watchdog_device *wdt_dev) 231a44a4553SMatthias Brugger { 232a44a4553SMatthias Brugger struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 233a44a4553SMatthias Brugger void __iomem *wdt_base = mtk_wdt->wdt_base; 234a44a4553SMatthias Brugger u32 reg; 235a44a4553SMatthias Brugger 236a44a4553SMatthias Brugger reg = readl(wdt_base + WDT_MODE); 237a44a4553SMatthias Brugger reg &= ~WDT_MODE_EN; 2385da2bf1aSNicolas Boichat reg |= WDT_MODE_KEY; 239a44a4553SMatthias Brugger iowrite32(reg, wdt_base + WDT_MODE); 240a44a4553SMatthias Brugger 241a44a4553SMatthias Brugger return 0; 242a44a4553SMatthias Brugger } 243a44a4553SMatthias Brugger 244a44a4553SMatthias Brugger static int mtk_wdt_start(struct watchdog_device *wdt_dev) 245a44a4553SMatthias Brugger { 246a44a4553SMatthias Brugger u32 reg; 247a44a4553SMatthias Brugger struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev); 248a44a4553SMatthias Brugger void __iomem *wdt_base = mtk_wdt->wdt_base; 2499ffd906dSDan Carpenter int ret; 250a44a4553SMatthias Brugger 251a44a4553SMatthias Brugger ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout); 252a44a4553SMatthias Brugger if (ret < 0) 253a44a4553SMatthias Brugger return ret; 254a44a4553SMatthias Brugger 255a44a4553SMatthias Brugger reg = ioread32(wdt_base + WDT_MODE); 2561bbce779SWang Qing if (wdt_dev->pretimeout) 2571bbce779SWang Qing reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); 2581bbce779SWang Qing else 259a44a4553SMatthias Brugger reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); 26059b0f513SFengquan Chen if (mtk_wdt->disable_wdt_extrst) 26159b0f513SFengquan Chen reg &= ~WDT_MODE_EXRST_EN; 262a44a4553SMatthias Brugger reg |= (WDT_MODE_EN | WDT_MODE_KEY); 263a44a4553SMatthias Brugger iowrite32(reg, wdt_base + WDT_MODE); 264a44a4553SMatthias Brugger 265a44a4553SMatthias Brugger return 0; 266a44a4553SMatthias Brugger } 267a44a4553SMatthias Brugger 2681bbce779SWang Qing static int mtk_wdt_set_pretimeout(struct watchdog_device *wdd, 2691bbce779SWang Qing unsigned int timeout) 2701bbce779SWang Qing { 2711bbce779SWang Qing struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdd); 2721bbce779SWang Qing void __iomem *wdt_base = mtk_wdt->wdt_base; 2731bbce779SWang Qing u32 reg = ioread32(wdt_base + WDT_MODE); 2741bbce779SWang Qing 2751bbce779SWang Qing if (timeout && !wdd->pretimeout) { 2761bbce779SWang Qing wdd->pretimeout = wdd->timeout / 2; 2771bbce779SWang Qing reg |= (WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); 2781bbce779SWang Qing } else if (!timeout && wdd->pretimeout) { 2791bbce779SWang Qing wdd->pretimeout = 0; 2801bbce779SWang Qing reg &= ~(WDT_MODE_IRQ_EN | WDT_MODE_DUAL_EN); 2811bbce779SWang Qing } else { 2821bbce779SWang Qing return 0; 2831bbce779SWang Qing } 2841bbce779SWang Qing 2851bbce779SWang Qing reg |= WDT_MODE_KEY; 2861bbce779SWang Qing iowrite32(reg, wdt_base + WDT_MODE); 2871bbce779SWang Qing 2881bbce779SWang Qing return mtk_wdt_set_timeout(wdd, wdd->timeout); 2891bbce779SWang Qing } 2901bbce779SWang Qing 2911bbce779SWang Qing static irqreturn_t mtk_wdt_isr(int irq, void *arg) 2921bbce779SWang Qing { 2931bbce779SWang Qing struct watchdog_device *wdd = arg; 2941bbce779SWang Qing 2951bbce779SWang Qing watchdog_notify_pretimeout(wdd); 2961bbce779SWang Qing 2971bbce779SWang Qing return IRQ_HANDLED; 2981bbce779SWang Qing } 2991bbce779SWang Qing 300a44a4553SMatthias Brugger static const struct watchdog_info mtk_wdt_info = { 301a44a4553SMatthias Brugger .identity = DRV_NAME, 302a44a4553SMatthias Brugger .options = WDIOF_SETTIMEOUT | 303a44a4553SMatthias Brugger WDIOF_KEEPALIVEPING | 304a44a4553SMatthias Brugger WDIOF_MAGICCLOSE, 305a44a4553SMatthias Brugger }; 306a44a4553SMatthias Brugger 3071bbce779SWang Qing static const struct watchdog_info mtk_wdt_pt_info = { 3081bbce779SWang Qing .identity = DRV_NAME, 3091bbce779SWang Qing .options = WDIOF_SETTIMEOUT | 3101bbce779SWang Qing WDIOF_PRETIMEOUT | 3111bbce779SWang Qing WDIOF_KEEPALIVEPING | 3121bbce779SWang Qing WDIOF_MAGICCLOSE, 3131bbce779SWang Qing }; 3141bbce779SWang Qing 315a44a4553SMatthias Brugger static const struct watchdog_ops mtk_wdt_ops = { 316a44a4553SMatthias Brugger .owner = THIS_MODULE, 317a44a4553SMatthias Brugger .start = mtk_wdt_start, 318a44a4553SMatthias Brugger .stop = mtk_wdt_stop, 319a44a4553SMatthias Brugger .ping = mtk_wdt_ping, 320a44a4553SMatthias Brugger .set_timeout = mtk_wdt_set_timeout, 3211bbce779SWang Qing .set_pretimeout = mtk_wdt_set_pretimeout, 322e86adc3fSDamien Riegel .restart = mtk_wdt_restart, 323a44a4553SMatthias Brugger }; 324a44a4553SMatthias Brugger 325a44a4553SMatthias Brugger static int mtk_wdt_probe(struct platform_device *pdev) 326a44a4553SMatthias Brugger { 327a15f6e64SGuenter Roeck struct device *dev = &pdev->dev; 328a44a4553SMatthias Brugger struct mtk_wdt_dev *mtk_wdt; 329c254e103Syong.liang const struct mtk_wdt_data *wdt_data; 3301bbce779SWang Qing int err, irq; 331a44a4553SMatthias Brugger 332a15f6e64SGuenter Roeck mtk_wdt = devm_kzalloc(dev, sizeof(*mtk_wdt), GFP_KERNEL); 333a44a4553SMatthias Brugger if (!mtk_wdt) 334a44a4553SMatthias Brugger return -ENOMEM; 335a44a4553SMatthias Brugger 336a44a4553SMatthias Brugger platform_set_drvdata(pdev, mtk_wdt); 337a44a4553SMatthias Brugger 3380f0a6a28SGuenter Roeck mtk_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0); 339a44a4553SMatthias Brugger if (IS_ERR(mtk_wdt->wdt_base)) 340a44a4553SMatthias Brugger return PTR_ERR(mtk_wdt->wdt_base); 341a44a4553SMatthias Brugger 342*1bafac47STzung-Bi Shih irq = platform_get_irq_optional(pdev, 0); 3431bbce779SWang Qing if (irq > 0) { 3441bbce779SWang Qing err = devm_request_irq(&pdev->dev, irq, mtk_wdt_isr, 0, "wdt_bark", 3451bbce779SWang Qing &mtk_wdt->wdt_dev); 3461bbce779SWang Qing if (err) 3471bbce779SWang Qing return err; 3481bbce779SWang Qing 3491bbce779SWang Qing mtk_wdt->wdt_dev.info = &mtk_wdt_pt_info; 3501bbce779SWang Qing mtk_wdt->wdt_dev.pretimeout = WDT_MAX_TIMEOUT / 2; 3511bbce779SWang Qing } else { 3521bbce779SWang Qing if (irq == -EPROBE_DEFER) 3531bbce779SWang Qing return -EPROBE_DEFER; 3541bbce779SWang Qing 355a44a4553SMatthias Brugger mtk_wdt->wdt_dev.info = &mtk_wdt_info; 3561bbce779SWang Qing } 3571bbce779SWang Qing 358a44a4553SMatthias Brugger mtk_wdt->wdt_dev.ops = &mtk_wdt_ops; 359a44a4553SMatthias Brugger mtk_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; 360bbece05cSfreddy.hsin mtk_wdt->wdt_dev.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT * 1000; 361a44a4553SMatthias Brugger mtk_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; 362a15f6e64SGuenter Roeck mtk_wdt->wdt_dev.parent = dev; 363a44a4553SMatthias Brugger 364a15f6e64SGuenter Roeck watchdog_init_timeout(&mtk_wdt->wdt_dev, timeout, dev); 365a44a4553SMatthias Brugger watchdog_set_nowayout(&mtk_wdt->wdt_dev, nowayout); 366e86adc3fSDamien Riegel watchdog_set_restart_priority(&mtk_wdt->wdt_dev, 128); 367a44a4553SMatthias Brugger 368a44a4553SMatthias Brugger watchdog_set_drvdata(&mtk_wdt->wdt_dev, mtk_wdt); 369a44a4553SMatthias Brugger 370bbece05cSfreddy.hsin mtk_wdt_init(&mtk_wdt->wdt_dev); 371a44a4553SMatthias Brugger 372a15f6e64SGuenter Roeck watchdog_stop_on_reboot(&mtk_wdt->wdt_dev); 373a15f6e64SGuenter Roeck err = devm_watchdog_register_device(dev, &mtk_wdt->wdt_dev); 374a44a4553SMatthias Brugger if (unlikely(err)) 375a44a4553SMatthias Brugger return err; 376a44a4553SMatthias Brugger 377a15f6e64SGuenter Roeck dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)\n", 378a44a4553SMatthias Brugger mtk_wdt->wdt_dev.timeout, nowayout); 379a44a4553SMatthias Brugger 380c254e103Syong.liang wdt_data = of_device_get_match_data(dev); 381c254e103Syong.liang if (wdt_data) { 382c254e103Syong.liang err = toprgu_register_reset_controller(pdev, 383c254e103Syong.liang wdt_data->toprgu_sw_rst_num); 384c254e103Syong.liang if (err) 385c254e103Syong.liang return err; 386c254e103Syong.liang } 38759b0f513SFengquan Chen 38859b0f513SFengquan Chen mtk_wdt->disable_wdt_extrst = 38959b0f513SFengquan Chen of_property_read_bool(dev->of_node, "mediatek,disable-extrst"); 39059b0f513SFengquan Chen 391a44a4553SMatthias Brugger return 0; 392a44a4553SMatthias Brugger } 393a44a4553SMatthias Brugger 3949fab0692SGreta Zhang #ifdef CONFIG_PM_SLEEP 3959fab0692SGreta Zhang static int mtk_wdt_suspend(struct device *dev) 3969fab0692SGreta Zhang { 3979fab0692SGreta Zhang struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); 3989fab0692SGreta Zhang 3999fab0692SGreta Zhang if (watchdog_active(&mtk_wdt->wdt_dev)) 4009fab0692SGreta Zhang mtk_wdt_stop(&mtk_wdt->wdt_dev); 4019fab0692SGreta Zhang 4029fab0692SGreta Zhang return 0; 4039fab0692SGreta Zhang } 4049fab0692SGreta Zhang 4059fab0692SGreta Zhang static int mtk_wdt_resume(struct device *dev) 4069fab0692SGreta Zhang { 4079fab0692SGreta Zhang struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev); 4089fab0692SGreta Zhang 4099fab0692SGreta Zhang if (watchdog_active(&mtk_wdt->wdt_dev)) { 4109fab0692SGreta Zhang mtk_wdt_start(&mtk_wdt->wdt_dev); 4119fab0692SGreta Zhang mtk_wdt_ping(&mtk_wdt->wdt_dev); 4129fab0692SGreta Zhang } 4139fab0692SGreta Zhang 4149fab0692SGreta Zhang return 0; 4159fab0692SGreta Zhang } 4169fab0692SGreta Zhang #endif 4179fab0692SGreta Zhang 418a44a4553SMatthias Brugger static const struct of_device_id mtk_wdt_dt_ids[] = { 4199e5236e7Syong.liang { .compatible = "mediatek,mt2712-wdt", .data = &mt2712_data }, 420a44a4553SMatthias Brugger { .compatible = "mediatek,mt6589-wdt" }, 421c254e103Syong.liang { .compatible = "mediatek,mt8183-wdt", .data = &mt8183_data }, 422adc318a3SCrystal Guo { .compatible = "mediatek,mt8192-wdt", .data = &mt8192_data }, 4238c6b5ea6SChristine Zhu { .compatible = "mediatek,mt8195-wdt", .data = &mt8195_data }, 424a44a4553SMatthias Brugger { /* sentinel */ } 425a44a4553SMatthias Brugger }; 426a44a4553SMatthias Brugger MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids); 427a44a4553SMatthias Brugger 4289fab0692SGreta Zhang static const struct dev_pm_ops mtk_wdt_pm_ops = { 4299fab0692SGreta Zhang SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend, 4309fab0692SGreta Zhang mtk_wdt_resume) 4319fab0692SGreta Zhang }; 4329fab0692SGreta Zhang 433a44a4553SMatthias Brugger static struct platform_driver mtk_wdt_driver = { 434a44a4553SMatthias Brugger .probe = mtk_wdt_probe, 435a44a4553SMatthias Brugger .driver = { 436a44a4553SMatthias Brugger .name = DRV_NAME, 4379fab0692SGreta Zhang .pm = &mtk_wdt_pm_ops, 438a44a4553SMatthias Brugger .of_match_table = mtk_wdt_dt_ids, 439a44a4553SMatthias Brugger }, 440a44a4553SMatthias Brugger }; 441a44a4553SMatthias Brugger 442a44a4553SMatthias Brugger module_platform_driver(mtk_wdt_driver); 443a44a4553SMatthias Brugger 444a44a4553SMatthias Brugger module_param(timeout, uint, 0); 445a44a4553SMatthias Brugger MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); 446a44a4553SMatthias Brugger 447a44a4553SMatthias Brugger module_param(nowayout, bool, 0); 448a44a4553SMatthias Brugger MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 449a44a4553SMatthias Brugger __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 450a44a4553SMatthias Brugger 451a44a4553SMatthias Brugger MODULE_LICENSE("GPL"); 452a44a4553SMatthias Brugger MODULE_AUTHOR("Matthias Brugger <matthias.bgg@gmail.com>"); 453a44a4553SMatthias Brugger MODULE_DESCRIPTION("Mediatek WatchDog Timer Driver"); 454a44a4553SMatthias Brugger MODULE_VERSION(DRV_VERSION); 455