Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PIC32 deadman timer driver
0004  *
0005  * Purna Chandra Mandal <purna.mandal@microchip.com>
0006  * Copyright (c) 2016, Microchip Technology Inc.
0007  */
0008 #include <linux/clk.h>
0009 #include <linux/device.h>
0010 #include <linux/err.h>
0011 #include <linux/io.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/of.h>
0015 #include <linux/of_device.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/pm.h>
0018 #include <linux/watchdog.h>
0019 
0020 #include <asm/mach-pic32/pic32.h>
0021 
0022 /* Deadman Timer Regs */
0023 #define DMTCON_REG  0x00
0024 #define DMTPRECLR_REG   0x10
0025 #define DMTCLR_REG  0x20
0026 #define DMTSTAT_REG 0x30
0027 #define DMTCNT_REG  0x40
0028 #define DMTPSCNT_REG    0x60
0029 #define DMTPSINTV_REG   0x70
0030 
0031 /* Deadman Timer Regs fields */
0032 #define DMT_ON          BIT(15)
0033 #define DMT_STEP1_KEY       BIT(6)
0034 #define DMT_STEP2_KEY       BIT(3)
0035 #define DMTSTAT_WINOPN      BIT(0)
0036 #define DMTSTAT_EVENT       BIT(5)
0037 #define DMTSTAT_BAD2        BIT(6)
0038 #define DMTSTAT_BAD1        BIT(7)
0039 
0040 /* Reset Control Register fields for watchdog */
0041 #define RESETCON_DMT_TIMEOUT    BIT(5)
0042 
0043 struct pic32_dmt {
0044     void __iomem    *regs;
0045     struct clk  *clk;
0046 };
0047 
0048 static inline void dmt_enable(struct pic32_dmt *dmt)
0049 {
0050     writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));
0051 }
0052 
0053 static inline void dmt_disable(struct pic32_dmt *dmt)
0054 {
0055     writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG));
0056     /*
0057      * Cannot touch registers in the CPU cycle following clearing the
0058      * ON bit.
0059      */
0060     nop();
0061 }
0062 
0063 static inline int dmt_bad_status(struct pic32_dmt *dmt)
0064 {
0065     u32 val;
0066 
0067     val = readl(dmt->regs + DMTSTAT_REG);
0068     val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT);
0069     if (val)
0070         return -EAGAIN;
0071 
0072     return 0;
0073 }
0074 
0075 static inline int dmt_keepalive(struct pic32_dmt *dmt)
0076 {
0077     u32 v;
0078     u32 timeout = 500;
0079 
0080     /* set pre-clear key */
0081     writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG);
0082 
0083     /* wait for DMT window to open */
0084     while (--timeout) {
0085         v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN;
0086         if (v == DMTSTAT_WINOPN)
0087             break;
0088     }
0089 
0090     /* apply key2 */
0091     writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG);
0092 
0093     /* check whether keys are latched correctly */
0094     return dmt_bad_status(dmt);
0095 }
0096 
0097 static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt)
0098 {
0099     unsigned long rate;
0100 
0101     rate = clk_get_rate(dmt->clk);
0102     if (rate)
0103         return readl(dmt->regs + DMTPSCNT_REG) / rate;
0104 
0105     return 0;
0106 }
0107 
0108 static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt)
0109 {
0110     u32 v;
0111     void __iomem *rst_base;
0112 
0113     rst_base = ioremap(PIC32_BASE_RESET, 0x10);
0114     if (!rst_base)
0115         return 0;
0116 
0117     v = readl(rst_base);
0118 
0119     writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base));
0120 
0121     iounmap(rst_base);
0122     return v & RESETCON_DMT_TIMEOUT;
0123 }
0124 
0125 static int pic32_dmt_start(struct watchdog_device *wdd)
0126 {
0127     struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
0128 
0129     dmt_enable(dmt);
0130     return dmt_keepalive(dmt);
0131 }
0132 
0133 static int pic32_dmt_stop(struct watchdog_device *wdd)
0134 {
0135     struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
0136 
0137     dmt_disable(dmt);
0138 
0139     return 0;
0140 }
0141 
0142 static int pic32_dmt_ping(struct watchdog_device *wdd)
0143 {
0144     struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
0145 
0146     return dmt_keepalive(dmt);
0147 }
0148 
0149 static const struct watchdog_ops pic32_dmt_fops = {
0150     .owner      = THIS_MODULE,
0151     .start      = pic32_dmt_start,
0152     .stop       = pic32_dmt_stop,
0153     .ping       = pic32_dmt_ping,
0154 };
0155 
0156 static const struct watchdog_info pic32_dmt_ident = {
0157     .options    = WDIOF_KEEPALIVEPING |
0158               WDIOF_MAGICCLOSE,
0159     .identity   = "PIC32 Deadman Timer",
0160 };
0161 
0162 static struct watchdog_device pic32_dmt_wdd = {
0163     .info       = &pic32_dmt_ident,
0164     .ops        = &pic32_dmt_fops,
0165 };
0166 
0167 static void pic32_clk_disable_unprepare(void *data)
0168 {
0169     clk_disable_unprepare(data);
0170 }
0171 
0172 static int pic32_dmt_probe(struct platform_device *pdev)
0173 {
0174     struct device *dev = &pdev->dev;
0175     int ret;
0176     struct pic32_dmt *dmt;
0177     struct watchdog_device *wdd = &pic32_dmt_wdd;
0178 
0179     dmt = devm_kzalloc(dev, sizeof(*dmt), GFP_KERNEL);
0180     if (!dmt)
0181         return -ENOMEM;
0182 
0183     dmt->regs = devm_platform_ioremap_resource(pdev, 0);
0184     if (IS_ERR(dmt->regs))
0185         return PTR_ERR(dmt->regs);
0186 
0187     dmt->clk = devm_clk_get(dev, NULL);
0188     if (IS_ERR(dmt->clk)) {
0189         dev_err(dev, "clk not found\n");
0190         return PTR_ERR(dmt->clk);
0191     }
0192 
0193     ret = clk_prepare_enable(dmt->clk);
0194     if (ret)
0195         return ret;
0196     ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare,
0197                        dmt->clk);
0198     if (ret)
0199         return ret;
0200 
0201     wdd->timeout = pic32_dmt_get_timeout_secs(dmt);
0202     if (!wdd->timeout) {
0203         dev_err(dev, "failed to read watchdog register timeout\n");
0204         return -EINVAL;
0205     }
0206 
0207     dev_info(dev, "timeout %d\n", wdd->timeout);
0208 
0209     wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0;
0210 
0211     watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
0212     watchdog_set_drvdata(wdd, dmt);
0213 
0214     ret = devm_watchdog_register_device(dev, wdd);
0215     if (ret)
0216         return ret;
0217 
0218     platform_set_drvdata(pdev, wdd);
0219     return 0;
0220 }
0221 
0222 static const struct of_device_id pic32_dmt_of_ids[] = {
0223     { .compatible = "microchip,pic32mzda-dmt",},
0224     { /* sentinel */ }
0225 };
0226 MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);
0227 
0228 static struct platform_driver pic32_dmt_driver = {
0229     .probe      = pic32_dmt_probe,
0230     .driver     = {
0231         .name       = "pic32-dmt",
0232         .of_match_table = of_match_ptr(pic32_dmt_of_ids),
0233     }
0234 };
0235 
0236 module_platform_driver(pic32_dmt_driver);
0237 
0238 MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
0239 MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");
0240 MODULE_LICENSE("GPL");