0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040 #include <linux/clk.h>
0041 #include <linux/io.h>
0042 #include <linux/log2.h>
0043 #include <linux/module.h>
0044 #include <linux/mod_devicetable.h>
0045 #include <linux/platform_device.h>
0046 #include <linux/slab.h>
0047 #include <linux/watchdog.h>
0048
0049
0050 #define PDC_WDT_SOFT_RESET 0x00
0051 #define PDC_WDT_CONFIG 0x04
0052 #define PDC_WDT_CONFIG_ENABLE BIT(31)
0053 #define PDC_WDT_CONFIG_DELAY_MASK 0x1f
0054
0055 #define PDC_WDT_TICKLE1 0x08
0056 #define PDC_WDT_TICKLE1_MAGIC 0xabcd1234
0057 #define PDC_WDT_TICKLE2 0x0c
0058 #define PDC_WDT_TICKLE2_MAGIC 0x4321dcba
0059
0060 #define PDC_WDT_TICKLE_STATUS_MASK 0x7
0061 #define PDC_WDT_TICKLE_STATUS_SHIFT 0
0062 #define PDC_WDT_TICKLE_STATUS_HRESET 0x0
0063 #define PDC_WDT_TICKLE_STATUS_TIMEOUT 0x1
0064 #define PDC_WDT_TICKLE_STATUS_TICKLE 0x2
0065 #define PDC_WDT_TICKLE_STATUS_SRESET 0x3
0066 #define PDC_WDT_TICKLE_STATUS_USER 0x4
0067
0068
0069 #define PDC_WDT_MIN_TIMEOUT 1
0070 #define PDC_WDT_DEF_TIMEOUT 64
0071
0072 static int heartbeat;
0073 module_param(heartbeat, int, 0);
0074 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
0075 "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
0076
0077 static bool nowayout = WATCHDOG_NOWAYOUT;
0078 module_param(nowayout, bool, 0);
0079 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
0080 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0081
0082 struct pdc_wdt_dev {
0083 struct watchdog_device wdt_dev;
0084 struct clk *wdt_clk;
0085 struct clk *sys_clk;
0086 void __iomem *base;
0087 };
0088
0089 static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev)
0090 {
0091 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
0092
0093 writel(PDC_WDT_TICKLE1_MAGIC, wdt->base + PDC_WDT_TICKLE1);
0094 writel(PDC_WDT_TICKLE2_MAGIC, wdt->base + PDC_WDT_TICKLE2);
0095
0096 return 0;
0097 }
0098
0099 static int pdc_wdt_stop(struct watchdog_device *wdt_dev)
0100 {
0101 unsigned int val;
0102 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
0103
0104 val = readl(wdt->base + PDC_WDT_CONFIG);
0105 val &= ~PDC_WDT_CONFIG_ENABLE;
0106 writel(val, wdt->base + PDC_WDT_CONFIG);
0107
0108
0109 pdc_wdt_keepalive(wdt_dev);
0110
0111 return 0;
0112 }
0113
0114 static void __pdc_wdt_set_timeout(struct pdc_wdt_dev *wdt)
0115 {
0116 unsigned long clk_rate = clk_get_rate(wdt->wdt_clk);
0117 unsigned int val;
0118
0119 val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK;
0120 val |= order_base_2(wdt->wdt_dev.timeout * clk_rate) - 1;
0121 writel(val, wdt->base + PDC_WDT_CONFIG);
0122 }
0123
0124 static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev,
0125 unsigned int new_timeout)
0126 {
0127 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
0128
0129 wdt->wdt_dev.timeout = new_timeout;
0130
0131 __pdc_wdt_set_timeout(wdt);
0132
0133 return 0;
0134 }
0135
0136
0137 static int pdc_wdt_start(struct watchdog_device *wdt_dev)
0138 {
0139 unsigned int val;
0140 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
0141
0142 __pdc_wdt_set_timeout(wdt);
0143
0144 val = readl(wdt->base + PDC_WDT_CONFIG);
0145 val |= PDC_WDT_CONFIG_ENABLE;
0146 writel(val, wdt->base + PDC_WDT_CONFIG);
0147
0148 return 0;
0149 }
0150
0151 static int pdc_wdt_restart(struct watchdog_device *wdt_dev,
0152 unsigned long action, void *data)
0153 {
0154 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev);
0155
0156
0157 writel(0x1, wdt->base + PDC_WDT_SOFT_RESET);
0158
0159 return 0;
0160 }
0161
0162 static const struct watchdog_info pdc_wdt_info = {
0163 .identity = "IMG PDC Watchdog",
0164 .options = WDIOF_SETTIMEOUT |
0165 WDIOF_KEEPALIVEPING |
0166 WDIOF_MAGICCLOSE,
0167 };
0168
0169 static const struct watchdog_ops pdc_wdt_ops = {
0170 .owner = THIS_MODULE,
0171 .start = pdc_wdt_start,
0172 .stop = pdc_wdt_stop,
0173 .ping = pdc_wdt_keepalive,
0174 .set_timeout = pdc_wdt_set_timeout,
0175 .restart = pdc_wdt_restart,
0176 };
0177
0178 static void pdc_clk_disable_unprepare(void *data)
0179 {
0180 clk_disable_unprepare(data);
0181 }
0182
0183 static int pdc_wdt_probe(struct platform_device *pdev)
0184 {
0185 struct device *dev = &pdev->dev;
0186 u64 div;
0187 int ret, val;
0188 unsigned long clk_rate;
0189 struct pdc_wdt_dev *pdc_wdt;
0190
0191 pdc_wdt = devm_kzalloc(dev, sizeof(*pdc_wdt), GFP_KERNEL);
0192 if (!pdc_wdt)
0193 return -ENOMEM;
0194
0195 pdc_wdt->base = devm_platform_ioremap_resource(pdev, 0);
0196 if (IS_ERR(pdc_wdt->base))
0197 return PTR_ERR(pdc_wdt->base);
0198
0199 pdc_wdt->sys_clk = devm_clk_get(dev, "sys");
0200 if (IS_ERR(pdc_wdt->sys_clk)) {
0201 dev_err(dev, "failed to get the sys clock\n");
0202 return PTR_ERR(pdc_wdt->sys_clk);
0203 }
0204
0205 pdc_wdt->wdt_clk = devm_clk_get(dev, "wdt");
0206 if (IS_ERR(pdc_wdt->wdt_clk)) {
0207 dev_err(dev, "failed to get the wdt clock\n");
0208 return PTR_ERR(pdc_wdt->wdt_clk);
0209 }
0210
0211 ret = clk_prepare_enable(pdc_wdt->sys_clk);
0212 if (ret) {
0213 dev_err(dev, "could not prepare or enable sys clock\n");
0214 return ret;
0215 }
0216 ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
0217 pdc_wdt->sys_clk);
0218 if (ret)
0219 return ret;
0220
0221 ret = clk_prepare_enable(pdc_wdt->wdt_clk);
0222 if (ret) {
0223 dev_err(dev, "could not prepare or enable wdt clock\n");
0224 return ret;
0225 }
0226 ret = devm_add_action_or_reset(dev, pdc_clk_disable_unprepare,
0227 pdc_wdt->wdt_clk);
0228 if (ret)
0229 return ret;
0230
0231
0232 clk_rate = clk_get_rate(pdc_wdt->wdt_clk);
0233 if (clk_rate == 0) {
0234 dev_err(dev, "failed to get clock rate\n");
0235 return -EINVAL;
0236 }
0237
0238 if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) {
0239 dev_err(dev, "invalid clock rate\n");
0240 return -EINVAL;
0241 }
0242
0243 if (order_base_2(clk_rate) == 0)
0244 pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT + 1;
0245 else
0246 pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT;
0247
0248 pdc_wdt->wdt_dev.info = &pdc_wdt_info;
0249 pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
0250
0251 div = 1ULL << (PDC_WDT_CONFIG_DELAY_MASK + 1);
0252 do_div(div, clk_rate);
0253 pdc_wdt->wdt_dev.max_timeout = div;
0254 pdc_wdt->wdt_dev.timeout = PDC_WDT_DEF_TIMEOUT;
0255 pdc_wdt->wdt_dev.parent = dev;
0256 watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
0257
0258 watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, dev);
0259
0260 pdc_wdt_stop(&pdc_wdt->wdt_dev);
0261
0262
0263 val = readl(pdc_wdt->base + PDC_WDT_TICKLE1);
0264 val = (val & PDC_WDT_TICKLE_STATUS_MASK) >> PDC_WDT_TICKLE_STATUS_SHIFT;
0265 switch (val) {
0266 case PDC_WDT_TICKLE_STATUS_TICKLE:
0267 case PDC_WDT_TICKLE_STATUS_TIMEOUT:
0268 pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET;
0269 dev_info(dev, "watchdog module last reset due to timeout\n");
0270 break;
0271 case PDC_WDT_TICKLE_STATUS_HRESET:
0272 dev_info(dev,
0273 "watchdog module last reset due to hard reset\n");
0274 break;
0275 case PDC_WDT_TICKLE_STATUS_SRESET:
0276 dev_info(dev,
0277 "watchdog module last reset due to soft reset\n");
0278 break;
0279 case PDC_WDT_TICKLE_STATUS_USER:
0280 dev_info(dev,
0281 "watchdog module last reset due to user reset\n");
0282 break;
0283 default:
0284 dev_info(dev, "contains an illegal status code (%08x)\n", val);
0285 break;
0286 }
0287
0288 watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout);
0289 watchdog_set_restart_priority(&pdc_wdt->wdt_dev, 128);
0290
0291 platform_set_drvdata(pdev, pdc_wdt);
0292
0293 watchdog_stop_on_reboot(&pdc_wdt->wdt_dev);
0294 watchdog_stop_on_unregister(&pdc_wdt->wdt_dev);
0295 return devm_watchdog_register_device(dev, &pdc_wdt->wdt_dev);
0296 }
0297
0298 static const struct of_device_id pdc_wdt_match[] = {
0299 { .compatible = "img,pdc-wdt" },
0300 {}
0301 };
0302 MODULE_DEVICE_TABLE(of, pdc_wdt_match);
0303
0304 static struct platform_driver pdc_wdt_driver = {
0305 .driver = {
0306 .name = "imgpdc-wdt",
0307 .of_match_table = pdc_wdt_match,
0308 },
0309 .probe = pdc_wdt_probe,
0310 };
0311 module_platform_driver(pdc_wdt_driver);
0312
0313 MODULE_AUTHOR("Jude Abraham <Jude.Abraham@imgtec.com>");
0314 MODULE_AUTHOR("Naidu Tellapati <Naidu.Tellapati@imgtec.com>");
0315 MODULE_DESCRIPTION("Imagination Technologies PDC Watchdog Timer Driver");
0316 MODULE_LICENSE("GPL v2");