Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: pic32-dmt: Add PIC32 deadman timer driver

Adds support for the deadman timer peripheral found on PIC32 class devices.

The primary function of the deadman timer (DMT) is to reset the processor
in the event of a software malfunction. The DMT is a free-running
instruction fetch timer, which is clocked whenever an instruction fetch
occurs until a count match occurs. Instructions are not fetched when
the processor is in sleep mode.

Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
Signed-off-by: Joshua Henderson <joshua.henderson@microchip.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Wim Van Sebroeck <wim@iguana.be>
Cc: linux-kernel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-watchdog@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/12703/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by

Purna Chandra Mandal and committed by
Ralf Baechle
b0d8a082 bfa43267

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