12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b0d8a082SPurna Chandra Mandal /* 3b0d8a082SPurna Chandra Mandal * PIC32 deadman timer driver 4b0d8a082SPurna Chandra Mandal * 5b0d8a082SPurna Chandra Mandal * Purna Chandra Mandal <purna.mandal@microchip.com> 6b0d8a082SPurna Chandra Mandal * Copyright (c) 2016, Microchip Technology Inc. 7b0d8a082SPurna Chandra Mandal */ 8b0d8a082SPurna Chandra Mandal #include <linux/clk.h> 9b0d8a082SPurna Chandra Mandal #include <linux/device.h> 10b0d8a082SPurna Chandra Mandal #include <linux/err.h> 11b0d8a082SPurna Chandra Mandal #include <linux/io.h> 12b0d8a082SPurna Chandra Mandal #include <linux/kernel.h> 13b0d8a082SPurna Chandra Mandal #include <linux/module.h> 14b0d8a082SPurna Chandra Mandal #include <linux/of.h> 15b0d8a082SPurna Chandra Mandal #include <linux/of_device.h> 16b0d8a082SPurna Chandra Mandal #include <linux/platform_device.h> 17b0d8a082SPurna Chandra Mandal #include <linux/pm.h> 18b0d8a082SPurna Chandra Mandal #include <linux/watchdog.h> 19b0d8a082SPurna Chandra Mandal 20b0d8a082SPurna Chandra Mandal #include <asm/mach-pic32/pic32.h> 21b0d8a082SPurna Chandra Mandal 22b0d8a082SPurna Chandra Mandal /* Deadman Timer Regs */ 23b0d8a082SPurna Chandra Mandal #define DMTCON_REG 0x00 24b0d8a082SPurna Chandra Mandal #define DMTPRECLR_REG 0x10 25b0d8a082SPurna Chandra Mandal #define DMTCLR_REG 0x20 26b0d8a082SPurna Chandra Mandal #define DMTSTAT_REG 0x30 27b0d8a082SPurna Chandra Mandal #define DMTCNT_REG 0x40 28b0d8a082SPurna Chandra Mandal #define DMTPSCNT_REG 0x60 29b0d8a082SPurna Chandra Mandal #define DMTPSINTV_REG 0x70 30b0d8a082SPurna Chandra Mandal 31b0d8a082SPurna Chandra Mandal /* Deadman Timer Regs fields */ 32b0d8a082SPurna Chandra Mandal #define DMT_ON BIT(15) 33b0d8a082SPurna Chandra Mandal #define DMT_STEP1_KEY BIT(6) 34b0d8a082SPurna Chandra Mandal #define DMT_STEP2_KEY BIT(3) 35b0d8a082SPurna Chandra Mandal #define DMTSTAT_WINOPN BIT(0) 36b0d8a082SPurna Chandra Mandal #define DMTSTAT_EVENT BIT(5) 37b0d8a082SPurna Chandra Mandal #define DMTSTAT_BAD2 BIT(6) 38b0d8a082SPurna Chandra Mandal #define DMTSTAT_BAD1 BIT(7) 39b0d8a082SPurna Chandra Mandal 40b0d8a082SPurna Chandra Mandal /* Reset Control Register fields for watchdog */ 41b0d8a082SPurna Chandra Mandal #define RESETCON_DMT_TIMEOUT BIT(5) 42b0d8a082SPurna Chandra Mandal 43b0d8a082SPurna Chandra Mandal struct pic32_dmt { 44b0d8a082SPurna Chandra Mandal void __iomem *regs; 45b0d8a082SPurna Chandra Mandal struct clk *clk; 46b0d8a082SPurna Chandra Mandal }; 47b0d8a082SPurna Chandra Mandal 48b0d8a082SPurna Chandra Mandal static inline void dmt_enable(struct pic32_dmt *dmt) 49b0d8a082SPurna Chandra Mandal { 50b0d8a082SPurna Chandra Mandal writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG)); 51b0d8a082SPurna Chandra Mandal } 52b0d8a082SPurna Chandra Mandal 53b0d8a082SPurna Chandra Mandal static inline void dmt_disable(struct pic32_dmt *dmt) 54b0d8a082SPurna Chandra Mandal { 55b0d8a082SPurna Chandra Mandal writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG)); 56b0d8a082SPurna Chandra Mandal /* 57b0d8a082SPurna Chandra Mandal * Cannot touch registers in the CPU cycle following clearing the 58b0d8a082SPurna Chandra Mandal * ON bit. 59b0d8a082SPurna Chandra Mandal */ 60b0d8a082SPurna Chandra Mandal nop(); 61b0d8a082SPurna Chandra Mandal } 62b0d8a082SPurna Chandra Mandal 63b0d8a082SPurna Chandra Mandal static inline int dmt_bad_status(struct pic32_dmt *dmt) 64b0d8a082SPurna Chandra Mandal { 65b0d8a082SPurna Chandra Mandal u32 val; 66b0d8a082SPurna Chandra Mandal 67b0d8a082SPurna Chandra Mandal val = readl(dmt->regs + DMTSTAT_REG); 68b0d8a082SPurna Chandra Mandal val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT); 69b0d8a082SPurna Chandra Mandal if (val) 70b0d8a082SPurna Chandra Mandal return -EAGAIN; 71b0d8a082SPurna Chandra Mandal 72b0d8a082SPurna Chandra Mandal return 0; 73b0d8a082SPurna Chandra Mandal } 74b0d8a082SPurna Chandra Mandal 75b0d8a082SPurna Chandra Mandal static inline int dmt_keepalive(struct pic32_dmt *dmt) 76b0d8a082SPurna Chandra Mandal { 77b0d8a082SPurna Chandra Mandal u32 v; 78b0d8a082SPurna Chandra Mandal u32 timeout = 500; 79b0d8a082SPurna Chandra Mandal 80b0d8a082SPurna Chandra Mandal /* set pre-clear key */ 81b0d8a082SPurna Chandra Mandal writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG); 82b0d8a082SPurna Chandra Mandal 83b0d8a082SPurna Chandra Mandal /* wait for DMT window to open */ 84b0d8a082SPurna Chandra Mandal while (--timeout) { 85b0d8a082SPurna Chandra Mandal v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN; 86b0d8a082SPurna Chandra Mandal if (v == DMTSTAT_WINOPN) 87b0d8a082SPurna Chandra Mandal break; 88b0d8a082SPurna Chandra Mandal } 89b0d8a082SPurna Chandra Mandal 90b0d8a082SPurna Chandra Mandal /* apply key2 */ 91b0d8a082SPurna Chandra Mandal writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG); 92b0d8a082SPurna Chandra Mandal 93b0d8a082SPurna Chandra Mandal /* check whether keys are latched correctly */ 94b0d8a082SPurna Chandra Mandal return dmt_bad_status(dmt); 95b0d8a082SPurna Chandra Mandal } 96b0d8a082SPurna Chandra Mandal 97b0d8a082SPurna Chandra Mandal static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt) 98b0d8a082SPurna Chandra Mandal { 99b0d8a082SPurna Chandra Mandal unsigned long rate; 100b0d8a082SPurna Chandra Mandal 101b0d8a082SPurna Chandra Mandal rate = clk_get_rate(dmt->clk); 102b0d8a082SPurna Chandra Mandal if (rate) 103b0d8a082SPurna Chandra Mandal return readl(dmt->regs + DMTPSCNT_REG) / rate; 104b0d8a082SPurna Chandra Mandal 105b0d8a082SPurna Chandra Mandal return 0; 106b0d8a082SPurna Chandra Mandal } 107b0d8a082SPurna Chandra Mandal 108b0d8a082SPurna Chandra Mandal static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt) 109b0d8a082SPurna Chandra Mandal { 110b0d8a082SPurna Chandra Mandal u32 v; 111b0d8a082SPurna Chandra Mandal void __iomem *rst_base; 112b0d8a082SPurna Chandra Mandal 113b0d8a082SPurna Chandra Mandal rst_base = ioremap(PIC32_BASE_RESET, 0x10); 114b0d8a082SPurna Chandra Mandal if (!rst_base) 115b0d8a082SPurna Chandra Mandal return 0; 116b0d8a082SPurna Chandra Mandal 117b0d8a082SPurna Chandra Mandal v = readl(rst_base); 118b0d8a082SPurna Chandra Mandal 119b0d8a082SPurna Chandra Mandal writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base)); 120b0d8a082SPurna Chandra Mandal 121b0d8a082SPurna Chandra Mandal iounmap(rst_base); 122b0d8a082SPurna Chandra Mandal return v & RESETCON_DMT_TIMEOUT; 123b0d8a082SPurna Chandra Mandal } 124b0d8a082SPurna Chandra Mandal 125b0d8a082SPurna Chandra Mandal static int pic32_dmt_start(struct watchdog_device *wdd) 126b0d8a082SPurna Chandra Mandal { 127b0d8a082SPurna Chandra Mandal struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); 128b0d8a082SPurna Chandra Mandal 129b0d8a082SPurna Chandra Mandal dmt_enable(dmt); 130b0d8a082SPurna Chandra Mandal return dmt_keepalive(dmt); 131b0d8a082SPurna Chandra Mandal } 132b0d8a082SPurna Chandra Mandal 133b0d8a082SPurna Chandra Mandal static int pic32_dmt_stop(struct watchdog_device *wdd) 134b0d8a082SPurna Chandra Mandal { 135b0d8a082SPurna Chandra Mandal struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); 136b0d8a082SPurna Chandra Mandal 137b0d8a082SPurna Chandra Mandal dmt_disable(dmt); 138b0d8a082SPurna Chandra Mandal 139b0d8a082SPurna Chandra Mandal return 0; 140b0d8a082SPurna Chandra Mandal } 141b0d8a082SPurna Chandra Mandal 142b0d8a082SPurna Chandra Mandal static int pic32_dmt_ping(struct watchdog_device *wdd) 143b0d8a082SPurna Chandra Mandal { 144b0d8a082SPurna Chandra Mandal struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); 145b0d8a082SPurna Chandra Mandal 146b0d8a082SPurna Chandra Mandal return dmt_keepalive(dmt); 147b0d8a082SPurna Chandra Mandal } 148b0d8a082SPurna Chandra Mandal 149b0d8a082SPurna Chandra Mandal static const struct watchdog_ops pic32_dmt_fops = { 150b0d8a082SPurna Chandra Mandal .owner = THIS_MODULE, 151b0d8a082SPurna Chandra Mandal .start = pic32_dmt_start, 152b0d8a082SPurna Chandra Mandal .stop = pic32_dmt_stop, 153b0d8a082SPurna Chandra Mandal .ping = pic32_dmt_ping, 154b0d8a082SPurna Chandra Mandal }; 155b0d8a082SPurna Chandra Mandal 156b0d8a082SPurna Chandra Mandal static const struct watchdog_info pic32_dmt_ident = { 157b0d8a082SPurna Chandra Mandal .options = WDIOF_KEEPALIVEPING | 158b0d8a082SPurna Chandra Mandal WDIOF_MAGICCLOSE, 159b0d8a082SPurna Chandra Mandal .identity = "PIC32 Deadman Timer", 160b0d8a082SPurna Chandra Mandal }; 161b0d8a082SPurna Chandra Mandal 162b0d8a082SPurna Chandra Mandal static struct watchdog_device pic32_dmt_wdd = { 163b0d8a082SPurna Chandra Mandal .info = &pic32_dmt_ident, 164b0d8a082SPurna Chandra Mandal .ops = &pic32_dmt_fops, 165b0d8a082SPurna Chandra Mandal }; 166b0d8a082SPurna Chandra Mandal 167a02b3d7cSGuenter Roeck static void pic32_clk_disable_unprepare(void *data) 168a02b3d7cSGuenter Roeck { 169a02b3d7cSGuenter Roeck clk_disable_unprepare(data); 170a02b3d7cSGuenter Roeck } 171a02b3d7cSGuenter Roeck 172b0d8a082SPurna Chandra Mandal static int pic32_dmt_probe(struct platform_device *pdev) 173b0d8a082SPurna Chandra Mandal { 174a02b3d7cSGuenter Roeck struct device *dev = &pdev->dev; 175b0d8a082SPurna Chandra Mandal int ret; 176b0d8a082SPurna Chandra Mandal struct pic32_dmt *dmt; 177b0d8a082SPurna Chandra Mandal struct watchdog_device *wdd = &pic32_dmt_wdd; 178b0d8a082SPurna Chandra Mandal 179a02b3d7cSGuenter Roeck dmt = devm_kzalloc(dev, sizeof(*dmt), GFP_KERNEL); 180fafdbabaSWei Yongjun if (!dmt) 181fafdbabaSWei Yongjun return -ENOMEM; 182b0d8a082SPurna Chandra Mandal 1830f0a6a28SGuenter Roeck dmt->regs = devm_platform_ioremap_resource(pdev, 0); 184b0d8a082SPurna Chandra Mandal if (IS_ERR(dmt->regs)) 185b0d8a082SPurna Chandra Mandal return PTR_ERR(dmt->regs); 186b0d8a082SPurna Chandra Mandal 187a02b3d7cSGuenter Roeck dmt->clk = devm_clk_get(dev, NULL); 188b0d8a082SPurna Chandra Mandal if (IS_ERR(dmt->clk)) { 189a02b3d7cSGuenter Roeck dev_err(dev, "clk not found\n"); 190b0d8a082SPurna Chandra Mandal return PTR_ERR(dmt->clk); 191b0d8a082SPurna Chandra Mandal } 192b0d8a082SPurna Chandra Mandal 193b0d8a082SPurna Chandra Mandal ret = clk_prepare_enable(dmt->clk); 194b0d8a082SPurna Chandra Mandal if (ret) 195b0d8a082SPurna Chandra Mandal return ret; 196a02b3d7cSGuenter Roeck ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare, 197a02b3d7cSGuenter Roeck dmt->clk); 198a02b3d7cSGuenter Roeck if (ret) 199a02b3d7cSGuenter Roeck return ret; 200b0d8a082SPurna Chandra Mandal 201b0d8a082SPurna Chandra Mandal wdd->timeout = pic32_dmt_get_timeout_secs(dmt); 202b0d8a082SPurna Chandra Mandal if (!wdd->timeout) { 203a02b3d7cSGuenter Roeck dev_err(dev, "failed to read watchdog register timeout\n"); 204a02b3d7cSGuenter Roeck return -EINVAL; 205b0d8a082SPurna Chandra Mandal } 206b0d8a082SPurna Chandra Mandal 207a02b3d7cSGuenter Roeck dev_info(dev, "timeout %d\n", wdd->timeout); 208b0d8a082SPurna Chandra Mandal 209b0d8a082SPurna Chandra Mandal wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0; 210b0d8a082SPurna Chandra Mandal 211b0d8a082SPurna Chandra Mandal watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); 212b0d8a082SPurna Chandra Mandal watchdog_set_drvdata(wdd, dmt); 213b0d8a082SPurna Chandra Mandal 214a02b3d7cSGuenter Roeck ret = devm_watchdog_register_device(dev, wdd); 215*888ca35dSWolfram Sang if (ret) 216b0d8a082SPurna Chandra Mandal return ret; 217b0d8a082SPurna Chandra Mandal 218a02b3d7cSGuenter Roeck platform_set_drvdata(pdev, wdd); 219b0d8a082SPurna Chandra Mandal return 0; 220b0d8a082SPurna Chandra Mandal } 221b0d8a082SPurna Chandra Mandal 222b0d8a082SPurna Chandra Mandal static const struct of_device_id pic32_dmt_of_ids[] = { 223b0d8a082SPurna Chandra Mandal { .compatible = "microchip,pic32mzda-dmt",}, 224b0d8a082SPurna Chandra Mandal { /* sentinel */ } 225b0d8a082SPurna Chandra Mandal }; 226b0d8a082SPurna Chandra Mandal MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids); 227b0d8a082SPurna Chandra Mandal 228b0d8a082SPurna Chandra Mandal static struct platform_driver pic32_dmt_driver = { 229b0d8a082SPurna Chandra Mandal .probe = pic32_dmt_probe, 230b0d8a082SPurna Chandra Mandal .driver = { 231b0d8a082SPurna Chandra Mandal .name = "pic32-dmt", 232b0d8a082SPurna Chandra Mandal .of_match_table = of_match_ptr(pic32_dmt_of_ids), 233b0d8a082SPurna Chandra Mandal } 234b0d8a082SPurna Chandra Mandal }; 235b0d8a082SPurna Chandra Mandal 236b0d8a082SPurna Chandra Mandal module_platform_driver(pic32_dmt_driver); 237b0d8a082SPurna Chandra Mandal 238b0d8a082SPurna Chandra Mandal MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>"); 239b0d8a082SPurna Chandra Mandal MODULE_DESCRIPTION("Microchip PIC32 DMT Driver"); 240b0d8a082SPurna Chandra Mandal MODULE_LICENSE("GPL"); 241