1 /* 2 * Driver for Atmel SAMA5D4 Watchdog Timer 3 * 4 * Copyright (C) 2015 Atmel Corporation 5 * 6 * Licensed under GPLv2. 7 */ 8 9 #include <linux/interrupt.h> 10 #include <linux/io.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/of_irq.h> 15 #include <linux/platform_device.h> 16 #include <linux/reboot.h> 17 #include <linux/watchdog.h> 18 19 #include "at91sam9_wdt.h" 20 21 /* minimum and maximum watchdog timeout, in seconds */ 22 #define MIN_WDT_TIMEOUT 1 23 #define MAX_WDT_TIMEOUT 16 24 #define WDT_DEFAULT_TIMEOUT MAX_WDT_TIMEOUT 25 26 #define WDT_SEC2TICKS(s) ((s) ? (((s) << 8) - 1) : 0) 27 28 struct sama5d4_wdt { 29 struct watchdog_device wdd; 30 void __iomem *reg_base; 31 u32 config; 32 }; 33 34 static int wdt_timeout = WDT_DEFAULT_TIMEOUT; 35 static bool nowayout = WATCHDOG_NOWAYOUT; 36 37 module_param(wdt_timeout, int, 0); 38 MODULE_PARM_DESC(wdt_timeout, 39 "Watchdog timeout in seconds. (default = " 40 __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")"); 41 42 module_param(nowayout, bool, 0); 43 MODULE_PARM_DESC(nowayout, 44 "Watchdog cannot be stopped once started (default=" 45 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 46 47 #define wdt_read(wdt, field) \ 48 readl_relaxed((wdt)->reg_base + (field)) 49 50 #define wdt_write(wtd, field, val) \ 51 writel_relaxed((val), (wdt)->reg_base + (field)) 52 53 static int sama5d4_wdt_start(struct watchdog_device *wdd) 54 { 55 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 56 u32 reg; 57 58 reg = wdt_read(wdt, AT91_WDT_MR); 59 reg &= ~AT91_WDT_WDDIS; 60 wdt_write(wdt, AT91_WDT_MR, reg); 61 62 return 0; 63 } 64 65 static int sama5d4_wdt_stop(struct watchdog_device *wdd) 66 { 67 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 68 u32 reg; 69 70 reg = wdt_read(wdt, AT91_WDT_MR); 71 reg |= AT91_WDT_WDDIS; 72 wdt_write(wdt, AT91_WDT_MR, reg); 73 74 return 0; 75 } 76 77 static int sama5d4_wdt_ping(struct watchdog_device *wdd) 78 { 79 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 80 81 wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); 82 83 return 0; 84 } 85 86 static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd, 87 unsigned int timeout) 88 { 89 struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd); 90 u32 value = WDT_SEC2TICKS(timeout); 91 u32 reg; 92 93 reg = wdt_read(wdt, AT91_WDT_MR); 94 reg &= ~AT91_WDT_WDV; 95 reg &= ~AT91_WDT_WDD; 96 reg |= AT91_WDT_SET_WDV(value); 97 reg |= AT91_WDT_SET_WDD(value); 98 wdt_write(wdt, AT91_WDT_MR, reg); 99 100 wdd->timeout = timeout; 101 102 return 0; 103 } 104 105 static const struct watchdog_info sama5d4_wdt_info = { 106 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 107 .identity = "Atmel SAMA5D4 Watchdog", 108 }; 109 110 static struct watchdog_ops sama5d4_wdt_ops = { 111 .owner = THIS_MODULE, 112 .start = sama5d4_wdt_start, 113 .stop = sama5d4_wdt_stop, 114 .ping = sama5d4_wdt_ping, 115 .set_timeout = sama5d4_wdt_set_timeout, 116 }; 117 118 static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id) 119 { 120 struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id); 121 122 if (wdt_read(wdt, AT91_WDT_SR)) { 123 pr_crit("Atmel Watchdog Software Reset\n"); 124 emergency_restart(); 125 pr_crit("Reboot didn't succeed\n"); 126 } 127 128 return IRQ_HANDLED; 129 } 130 131 static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt) 132 { 133 const char *tmp; 134 135 wdt->config = AT91_WDT_WDDIS; 136 137 if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) && 138 !strcmp(tmp, "software")) 139 wdt->config |= AT91_WDT_WDFIEN; 140 else 141 wdt->config |= AT91_WDT_WDRSTEN; 142 143 if (of_property_read_bool(np, "atmel,idle-halt")) 144 wdt->config |= AT91_WDT_WDIDLEHLT; 145 146 if (of_property_read_bool(np, "atmel,dbg-halt")) 147 wdt->config |= AT91_WDT_WDDBGHLT; 148 149 return 0; 150 } 151 152 static int sama5d4_wdt_init(struct sama5d4_wdt *wdt) 153 { 154 struct watchdog_device *wdd = &wdt->wdd; 155 u32 value = WDT_SEC2TICKS(wdd->timeout); 156 u32 reg; 157 158 /* 159 * Because the fields WDV and WDD must not be modified when the WDDIS 160 * bit is set, so clear the WDDIS bit before writing the WDT_MR. 161 */ 162 reg = wdt_read(wdt, AT91_WDT_MR); 163 reg &= ~AT91_WDT_WDDIS; 164 wdt_write(wdt, AT91_WDT_MR, reg); 165 166 reg = wdt->config; 167 reg |= AT91_WDT_SET_WDD(value); 168 reg |= AT91_WDT_SET_WDV(value); 169 170 wdt_write(wdt, AT91_WDT_MR, reg); 171 172 return 0; 173 } 174 175 static int sama5d4_wdt_probe(struct platform_device *pdev) 176 { 177 struct watchdog_device *wdd; 178 struct sama5d4_wdt *wdt; 179 struct resource *res; 180 void __iomem *regs; 181 u32 irq = 0; 182 int ret; 183 184 wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 185 if (!wdt) 186 return -ENOMEM; 187 188 wdd = &wdt->wdd; 189 wdd->timeout = wdt_timeout; 190 wdd->info = &sama5d4_wdt_info; 191 wdd->ops = &sama5d4_wdt_ops; 192 wdd->min_timeout = MIN_WDT_TIMEOUT; 193 wdd->max_timeout = MAX_WDT_TIMEOUT; 194 195 watchdog_set_drvdata(wdd, wdt); 196 197 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 198 regs = devm_ioremap_resource(&pdev->dev, res); 199 if (IS_ERR(regs)) 200 return PTR_ERR(regs); 201 202 wdt->reg_base = regs; 203 204 if (pdev->dev.of_node) { 205 irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 206 if (!irq) 207 dev_warn(&pdev->dev, "failed to get IRQ from DT\n"); 208 209 ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt); 210 if (ret) 211 return ret; 212 } 213 214 if ((wdt->config & AT91_WDT_WDFIEN) && irq) { 215 ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler, 216 IRQF_SHARED | IRQF_IRQPOLL | 217 IRQF_NO_SUSPEND, pdev->name, pdev); 218 if (ret) { 219 dev_err(&pdev->dev, 220 "cannot register interrupt handler\n"); 221 return ret; 222 } 223 } 224 225 ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev); 226 if (ret) { 227 dev_err(&pdev->dev, "unable to set timeout value\n"); 228 return ret; 229 } 230 231 ret = sama5d4_wdt_init(wdt); 232 if (ret) 233 return ret; 234 235 watchdog_set_nowayout(wdd, nowayout); 236 237 ret = watchdog_register_device(wdd); 238 if (ret) { 239 dev_err(&pdev->dev, "failed to register watchdog device\n"); 240 return ret; 241 } 242 243 platform_set_drvdata(pdev, wdt); 244 245 dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n", 246 wdt_timeout, nowayout); 247 248 return 0; 249 } 250 251 static int sama5d4_wdt_remove(struct platform_device *pdev) 252 { 253 struct sama5d4_wdt *wdt = platform_get_drvdata(pdev); 254 255 sama5d4_wdt_stop(&wdt->wdd); 256 257 watchdog_unregister_device(&wdt->wdd); 258 259 return 0; 260 } 261 262 static const struct of_device_id sama5d4_wdt_of_match[] = { 263 { .compatible = "atmel,sama5d4-wdt", }, 264 { } 265 }; 266 MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match); 267 268 static struct platform_driver sama5d4_wdt_driver = { 269 .probe = sama5d4_wdt_probe, 270 .remove = sama5d4_wdt_remove, 271 .driver = { 272 .name = "sama5d4_wdt", 273 .of_match_table = sama5d4_wdt_of_match, 274 } 275 }; 276 module_platform_driver(sama5d4_wdt_driver); 277 278 MODULE_AUTHOR("Atmel Corporation"); 279 MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver"); 280 MODULE_LICENSE("GPL v2"); 281