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