1 /* 2 * Imagination Technologies PowerDown Controller Watchdog Timer. 3 * 4 * Copyright (c) 2014 Imagination Technologies Ltd. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published by 8 * the Free Software Foundation. 9 * 10 * Based on drivers/watchdog/sunxi_wdt.c Copyright (c) 2013 Carlo Caione 11 * 2012 Henrik Nordstrom 12 */ 13 14 #include <linux/clk.h> 15 #include <linux/io.h> 16 #include <linux/log2.h> 17 #include <linux/module.h> 18 #include <linux/platform_device.h> 19 #include <linux/slab.h> 20 #include <linux/watchdog.h> 21 22 /* registers */ 23 #define PDC_WDT_SOFT_RESET 0x00 24 #define PDC_WDT_CONFIG 0x04 25 #define PDC_WDT_CONFIG_ENABLE BIT(31) 26 #define PDC_WDT_CONFIG_DELAY_MASK 0x1f 27 28 #define PDC_WDT_TICKLE1 0x08 29 #define PDC_WDT_TICKLE1_MAGIC 0xabcd1234 30 #define PDC_WDT_TICKLE2 0x0c 31 #define PDC_WDT_TICKLE2_MAGIC 0x4321dcba 32 33 #define PDC_WDT_TICKLE_STATUS_MASK 0x7 34 #define PDC_WDT_TICKLE_STATUS_SHIFT 0 35 #define PDC_WDT_TICKLE_STATUS_HRESET 0x0 /* Hard reset */ 36 #define PDC_WDT_TICKLE_STATUS_TIMEOUT 0x1 /* Timeout */ 37 #define PDC_WDT_TICKLE_STATUS_TICKLE 0x2 /* Tickled incorrectly */ 38 #define PDC_WDT_TICKLE_STATUS_SRESET 0x3 /* Soft reset */ 39 #define PDC_WDT_TICKLE_STATUS_USER 0x4 /* User reset */ 40 41 /* Timeout values are in seconds */ 42 #define PDC_WDT_MIN_TIMEOUT 1 43 #define PDC_WDT_DEF_TIMEOUT 64 44 45 static int heartbeat = PDC_WDT_DEF_TIMEOUT; 46 module_param(heartbeat, int, 0); 47 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds " 48 "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")"); 49 50 static bool nowayout = WATCHDOG_NOWAYOUT; 51 module_param(nowayout, bool, 0); 52 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 53 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 54 55 struct pdc_wdt_dev { 56 struct watchdog_device wdt_dev; 57 struct clk *wdt_clk; 58 struct clk *sys_clk; 59 void __iomem *base; 60 }; 61 62 static int pdc_wdt_keepalive(struct watchdog_device *wdt_dev) 63 { 64 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); 65 66 writel(PDC_WDT_TICKLE1_MAGIC, wdt->base + PDC_WDT_TICKLE1); 67 writel(PDC_WDT_TICKLE2_MAGIC, wdt->base + PDC_WDT_TICKLE2); 68 69 return 0; 70 } 71 72 static int pdc_wdt_stop(struct watchdog_device *wdt_dev) 73 { 74 unsigned int val; 75 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); 76 77 val = readl(wdt->base + PDC_WDT_CONFIG); 78 val &= ~PDC_WDT_CONFIG_ENABLE; 79 writel(val, wdt->base + PDC_WDT_CONFIG); 80 81 /* Must tickle to finish the stop */ 82 pdc_wdt_keepalive(wdt_dev); 83 84 return 0; 85 } 86 87 static int pdc_wdt_set_timeout(struct watchdog_device *wdt_dev, 88 unsigned int new_timeout) 89 { 90 unsigned int val; 91 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); 92 unsigned long clk_rate = clk_get_rate(wdt->wdt_clk); 93 94 wdt->wdt_dev.timeout = new_timeout; 95 96 val = readl(wdt->base + PDC_WDT_CONFIG) & ~PDC_WDT_CONFIG_DELAY_MASK; 97 val |= order_base_2(new_timeout * clk_rate) - 1; 98 writel(val, wdt->base + PDC_WDT_CONFIG); 99 100 return 0; 101 } 102 103 /* Start the watchdog timer (delay should already be set) */ 104 static int pdc_wdt_start(struct watchdog_device *wdt_dev) 105 { 106 unsigned int val; 107 struct pdc_wdt_dev *wdt = watchdog_get_drvdata(wdt_dev); 108 109 val = readl(wdt->base + PDC_WDT_CONFIG); 110 val |= PDC_WDT_CONFIG_ENABLE; 111 writel(val, wdt->base + PDC_WDT_CONFIG); 112 113 return 0; 114 } 115 116 static struct watchdog_info pdc_wdt_info = { 117 .identity = "IMG PDC Watchdog", 118 .options = WDIOF_SETTIMEOUT | 119 WDIOF_KEEPALIVEPING | 120 WDIOF_MAGICCLOSE, 121 }; 122 123 static const struct watchdog_ops pdc_wdt_ops = { 124 .owner = THIS_MODULE, 125 .start = pdc_wdt_start, 126 .stop = pdc_wdt_stop, 127 .ping = pdc_wdt_keepalive, 128 .set_timeout = pdc_wdt_set_timeout, 129 }; 130 131 static int pdc_wdt_probe(struct platform_device *pdev) 132 { 133 int ret, val; 134 unsigned long clk_rate; 135 struct resource *res; 136 struct pdc_wdt_dev *pdc_wdt; 137 138 pdc_wdt = devm_kzalloc(&pdev->dev, sizeof(*pdc_wdt), GFP_KERNEL); 139 if (!pdc_wdt) 140 return -ENOMEM; 141 142 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 143 pdc_wdt->base = devm_ioremap_resource(&pdev->dev, res); 144 if (IS_ERR(pdc_wdt->base)) 145 return PTR_ERR(pdc_wdt->base); 146 147 pdc_wdt->sys_clk = devm_clk_get(&pdev->dev, "sys"); 148 if (IS_ERR(pdc_wdt->sys_clk)) { 149 dev_err(&pdev->dev, "failed to get the sys clock\n"); 150 return PTR_ERR(pdc_wdt->sys_clk); 151 } 152 153 pdc_wdt->wdt_clk = devm_clk_get(&pdev->dev, "wdt"); 154 if (IS_ERR(pdc_wdt->wdt_clk)) { 155 dev_err(&pdev->dev, "failed to get the wdt clock\n"); 156 return PTR_ERR(pdc_wdt->wdt_clk); 157 } 158 159 ret = clk_prepare_enable(pdc_wdt->sys_clk); 160 if (ret) { 161 dev_err(&pdev->dev, "could not prepare or enable sys clock\n"); 162 return ret; 163 } 164 165 ret = clk_prepare_enable(pdc_wdt->wdt_clk); 166 if (ret) { 167 dev_err(&pdev->dev, "could not prepare or enable wdt clock\n"); 168 goto disable_sys_clk; 169 } 170 171 /* We use the clock rate to calculate the max timeout */ 172 clk_rate = clk_get_rate(pdc_wdt->wdt_clk); 173 if (clk_rate == 0) { 174 dev_err(&pdev->dev, "failed to get clock rate\n"); 175 ret = -EINVAL; 176 goto disable_wdt_clk; 177 } 178 179 if (order_base_2(clk_rate) > PDC_WDT_CONFIG_DELAY_MASK + 1) { 180 dev_err(&pdev->dev, "invalid clock rate\n"); 181 ret = -EINVAL; 182 goto disable_wdt_clk; 183 } 184 185 if (order_base_2(clk_rate) == 0) 186 pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT + 1; 187 else 188 pdc_wdt->wdt_dev.min_timeout = PDC_WDT_MIN_TIMEOUT; 189 190 pdc_wdt->wdt_dev.info = &pdc_wdt_info; 191 pdc_wdt->wdt_dev.ops = &pdc_wdt_ops; 192 pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK; 193 pdc_wdt->wdt_dev.parent = &pdev->dev; 194 watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt); 195 196 ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev); 197 if (ret < 0) { 198 pdc_wdt->wdt_dev.timeout = pdc_wdt->wdt_dev.max_timeout; 199 dev_warn(&pdev->dev, 200 "Initial timeout out of range! setting max timeout\n"); 201 } 202 203 pdc_wdt_stop(&pdc_wdt->wdt_dev); 204 205 /* Find what caused the last reset */ 206 val = readl(pdc_wdt->base + PDC_WDT_TICKLE1); 207 val = (val & PDC_WDT_TICKLE_STATUS_MASK) >> PDC_WDT_TICKLE_STATUS_SHIFT; 208 switch (val) { 209 case PDC_WDT_TICKLE_STATUS_TICKLE: 210 case PDC_WDT_TICKLE_STATUS_TIMEOUT: 211 pdc_wdt->wdt_dev.bootstatus |= WDIOF_CARDRESET; 212 dev_info(&pdev->dev, 213 "watchdog module last reset due to timeout\n"); 214 break; 215 case PDC_WDT_TICKLE_STATUS_HRESET: 216 dev_info(&pdev->dev, 217 "watchdog module last reset due to hard reset\n"); 218 break; 219 case PDC_WDT_TICKLE_STATUS_SRESET: 220 dev_info(&pdev->dev, 221 "watchdog module last reset due to soft reset\n"); 222 break; 223 case PDC_WDT_TICKLE_STATUS_USER: 224 dev_info(&pdev->dev, 225 "watchdog module last reset due to user reset\n"); 226 break; 227 default: 228 dev_info(&pdev->dev, 229 "contains an illegal status code (%08x)\n", val); 230 break; 231 } 232 233 watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout); 234 235 platform_set_drvdata(pdev, pdc_wdt); 236 237 ret = watchdog_register_device(&pdc_wdt->wdt_dev); 238 if (ret) 239 goto disable_wdt_clk; 240 241 return 0; 242 243 disable_wdt_clk: 244 clk_disable_unprepare(pdc_wdt->wdt_clk); 245 disable_sys_clk: 246 clk_disable_unprepare(pdc_wdt->sys_clk); 247 return ret; 248 } 249 250 static void pdc_wdt_shutdown(struct platform_device *pdev) 251 { 252 struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); 253 254 pdc_wdt_stop(&pdc_wdt->wdt_dev); 255 } 256 257 static int pdc_wdt_remove(struct platform_device *pdev) 258 { 259 struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); 260 261 pdc_wdt_stop(&pdc_wdt->wdt_dev); 262 watchdog_unregister_device(&pdc_wdt->wdt_dev); 263 clk_disable_unprepare(pdc_wdt->wdt_clk); 264 clk_disable_unprepare(pdc_wdt->sys_clk); 265 266 return 0; 267 } 268 269 static const struct of_device_id pdc_wdt_match[] = { 270 { .compatible = "img,pdc-wdt" }, 271 {} 272 }; 273 MODULE_DEVICE_TABLE(of, pdc_wdt_match); 274 275 static struct platform_driver pdc_wdt_driver = { 276 .driver = { 277 .name = "imgpdc-wdt", 278 .of_match_table = pdc_wdt_match, 279 }, 280 .probe = pdc_wdt_probe, 281 .remove = pdc_wdt_remove, 282 .shutdown = pdc_wdt_shutdown, 283 }; 284 module_platform_driver(pdc_wdt_driver); 285 286 MODULE_AUTHOR("Jude Abraham <Jude.Abraham@imgtec.com>"); 287 MODULE_AUTHOR("Naidu Tellapati <Naidu.Tellapati@imgtec.com>"); 288 MODULE_DESCRIPTION("Imagination Technologies PDC Watchdog Timer Driver"); 289 MODULE_LICENSE("GPL v2"); 290