0001
0002
0003
0004
0005
0006
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
0023 #define WDTCON_REG 0x00
0024
0025
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
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
0084 rate >>= 5;
0085 if (!rate)
0086 return 0;
0087
0088
0089 ps = pic32_wdt_get_post_scaler(wdt);
0090 terminal = BIT(ps);
0091
0092
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
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
0125
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 { }
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");