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