Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PIC32 watchdog driver
0004  *
0005  * Joshua Henderson <joshua.henderson@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 /* Watchdog Timer Registers */
0023 #define WDTCON_REG      0x00
0024 
0025 /* Watchdog Timer Control Register fields */
0026 #define WDTCON_WIN_EN       BIT(0)
0027 #define WDTCON_RMCS_MASK    0x0003
0028 #define WDTCON_RMCS_SHIFT   0x0006
0029 #define WDTCON_RMPS_MASK    0x001F
0030 #define WDTCON_RMPS_SHIFT   0x0008
0031 #define WDTCON_ON       BIT(15)
0032 #define WDTCON_CLR_KEY      0x5743
0033 
0034 /* Reset Control Register fields for watchdog */
0035 #define RESETCON_TIMEOUT_IDLE   BIT(2)
0036 #define RESETCON_TIMEOUT_SLEEP  BIT(3)
0037 #define RESETCON_WDT_TIMEOUT    BIT(4)
0038 
0039 struct pic32_wdt {
0040     void __iomem    *regs;
0041     void __iomem    *rst_base;
0042     struct clk  *clk;
0043 };
0044 
0045 static inline bool pic32_wdt_is_win_enabled(struct pic32_wdt *wdt)
0046 {
0047     return !!(readl(wdt->regs + WDTCON_REG) & WDTCON_WIN_EN);
0048 }
0049 
0050 static inline u32 pic32_wdt_get_post_scaler(struct pic32_wdt *wdt)
0051 {
0052     u32 v = readl(wdt->regs + WDTCON_REG);
0053 
0054     return (v >> WDTCON_RMPS_SHIFT) & WDTCON_RMPS_MASK;
0055 }
0056 
0057 static inline u32 pic32_wdt_get_clk_id(struct pic32_wdt *wdt)
0058 {
0059     u32 v = readl(wdt->regs + WDTCON_REG);
0060 
0061     return (v >> WDTCON_RMCS_SHIFT) & WDTCON_RMCS_MASK;
0062 }
0063 
0064 static int pic32_wdt_bootstatus(struct pic32_wdt *wdt)
0065 {
0066     u32 v = readl(wdt->rst_base);
0067 
0068     writel(RESETCON_WDT_TIMEOUT, PIC32_CLR(wdt->rst_base));
0069 
0070     return v & RESETCON_WDT_TIMEOUT;
0071 }
0072 
0073 static u32 pic32_wdt_get_timeout_secs(struct pic32_wdt *wdt, struct device *dev)
0074 {
0075     unsigned long rate;
0076     u32 period, ps, terminal;
0077 
0078     rate = clk_get_rate(wdt->clk);
0079 
0080     dev_dbg(dev, "wdt: clk_id %d, clk_rate %lu (prescale)\n",
0081         pic32_wdt_get_clk_id(wdt), rate);
0082 
0083     /* default, prescaler of 32 (i.e. div-by-32) is implicit. */
0084     rate >>= 5;
0085     if (!rate)
0086         return 0;
0087 
0088     /* calculate terminal count from postscaler. */
0089     ps = pic32_wdt_get_post_scaler(wdt);
0090     terminal = BIT(ps);
0091 
0092     /* find time taken (in secs) to reach terminal count */
0093     period = terminal / rate;
0094     dev_dbg(dev,
0095         "wdt: clk_rate %lu (postscale) / terminal %d, timeout %dsec\n",
0096         rate, terminal, period);
0097 
0098     return period;
0099 }
0100 
0101 static void pic32_wdt_keepalive(struct pic32_wdt *wdt)
0102 {
0103     /* write key through single half-word */
0104     writew(WDTCON_CLR_KEY, wdt->regs + WDTCON_REG + 2);
0105 }
0106 
0107 static int pic32_wdt_start(struct watchdog_device *wdd)
0108 {
0109     struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
0110 
0111     writel(WDTCON_ON, PIC32_SET(wdt->regs + WDTCON_REG));
0112     pic32_wdt_keepalive(wdt);
0113 
0114     return 0;
0115 }
0116 
0117 static int pic32_wdt_stop(struct watchdog_device *wdd)
0118 {
0119     struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
0120 
0121     writel(WDTCON_ON, PIC32_CLR(wdt->regs + WDTCON_REG));
0122 
0123     /*
0124      * Cannot touch registers in the CPU cycle following clearing the
0125      * ON bit.
0126      */
0127     nop();
0128 
0129     return 0;
0130 }
0131 
0132 static int pic32_wdt_ping(struct watchdog_device *wdd)
0133 {
0134     struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);
0135 
0136     pic32_wdt_keepalive(wdt);
0137 
0138     return 0;
0139 }
0140 
0141 static const struct watchdog_ops pic32_wdt_fops = {
0142     .owner      = THIS_MODULE,
0143     .start      = pic32_wdt_start,
0144     .stop       = pic32_wdt_stop,
0145     .ping       = pic32_wdt_ping,
0146 };
0147 
0148 static const struct watchdog_info pic32_wdt_ident = {
0149     .options = WDIOF_KEEPALIVEPING |
0150             WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
0151     .identity = "PIC32 Watchdog",
0152 };
0153 
0154 static struct watchdog_device pic32_wdd = {
0155     .info       = &pic32_wdt_ident,
0156     .ops        = &pic32_wdt_fops,
0157 };
0158 
0159 static const struct of_device_id pic32_wdt_dt_ids[] = {
0160     { .compatible = "microchip,pic32mzda-wdt", },
0161     { /* sentinel */ }
0162 };
0163 MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids);
0164 
0165 static void pic32_clk_disable_unprepare(void *data)
0166 {
0167     clk_disable_unprepare(data);
0168 }
0169 
0170 static int pic32_wdt_drv_probe(struct platform_device *pdev)
0171 {
0172     struct device *dev = &pdev->dev;
0173     int ret;
0174     struct watchdog_device *wdd = &pic32_wdd;
0175     struct pic32_wdt *wdt;
0176 
0177     wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
0178     if (!wdt)
0179         return -ENOMEM;
0180 
0181     wdt->regs = devm_platform_ioremap_resource(pdev, 0);
0182     if (IS_ERR(wdt->regs))
0183         return PTR_ERR(wdt->regs);
0184 
0185     wdt->rst_base = devm_ioremap(dev, PIC32_BASE_RESET, 0x10);
0186     if (!wdt->rst_base)
0187         return -ENOMEM;
0188 
0189     wdt->clk = devm_clk_get(dev, NULL);
0190     if (IS_ERR(wdt->clk)) {
0191         dev_err(dev, "clk not found\n");
0192         return PTR_ERR(wdt->clk);
0193     }
0194 
0195     ret = clk_prepare_enable(wdt->clk);
0196     if (ret) {
0197         dev_err(dev, "clk enable failed\n");
0198         return ret;
0199     }
0200     ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare,
0201                        wdt->clk);
0202     if (ret)
0203         return ret;
0204 
0205     if (pic32_wdt_is_win_enabled(wdt)) {
0206         dev_err(dev, "windowed-clear mode is not supported.\n");
0207         return -ENODEV;
0208     }
0209 
0210     wdd->timeout = pic32_wdt_get_timeout_secs(wdt, dev);
0211     if (!wdd->timeout) {
0212         dev_err(dev, "failed to read watchdog register timeout\n");
0213         return -EINVAL;
0214     }
0215 
0216     dev_info(dev, "timeout %d\n", wdd->timeout);
0217 
0218     wdd->bootstatus = pic32_wdt_bootstatus(wdt) ? WDIOF_CARDRESET : 0;
0219 
0220     watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
0221     watchdog_set_drvdata(wdd, wdt);
0222 
0223     ret = devm_watchdog_register_device(dev, wdd);
0224     if (ret)
0225         return ret;
0226 
0227     platform_set_drvdata(pdev, wdd);
0228 
0229     return 0;
0230 }
0231 
0232 static struct platform_driver pic32_wdt_driver = {
0233     .probe      = pic32_wdt_drv_probe,
0234     .driver     = {
0235         .name       = "pic32-wdt",
0236         .of_match_table = of_match_ptr(pic32_wdt_dt_ids),
0237     }
0238 };
0239 
0240 module_platform_driver(pic32_wdt_driver);
0241 
0242 MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");
0243 MODULE_DESCRIPTION("Microchip PIC32 Watchdog Timer");
0244 MODULE_LICENSE("GPL");