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