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