Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: renesas-wdt: add driver

Add support for watchdogs (RWDT and SWDT) found on RCar Gen3 based SoCs
from Renesas.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Wolfram Sang and committed by
Wim Van Sebroeck
bd99b68e e26e74b1

+247
+25
Documentation/devicetree/bindings/watchdog/renesas-wdt.txt
··· 1 + Renesas Watchdog Timer (WDT) Controller 2 + 3 + Required properties: 4 + - compatible : Should be "renesas,r8a7795-wdt", or "renesas,rcar-gen3-wdt" 5 + 6 + When compatible with the generic version, nodes must list the SoC-specific 7 + version corresponding to the platform first, followed by the generic 8 + version. 9 + 10 + - reg : Should contain WDT registers location and length 11 + - clocks : the clock feeding the watchdog timer. 12 + 13 + Optional properties: 14 + - timeout-sec : Contains the watchdog timeout in seconds 15 + - power-domains : the power domain the WDT belongs to 16 + 17 + Examples: 18 + 19 + wdt0: watchdog@e6020000 { 20 + compatible = "renesas,r8a7795-wdt", "renesas,rcar-gen3-wdt"; 21 + reg = <0 0xe6020000 0 0x0c>; 22 + clocks = <&cpg CPG_MOD 402>; 23 + power-domains = <&cpg>; 24 + timeout-sec = <60>; 25 + };
+8
drivers/watchdog/Kconfig
··· 661 661 To compile this driver as a module, choose M here: the 662 662 module will be called atlas7_wdt. 663 663 664 + config RENESAS_WDT 665 + tristate "Renesas WDT Watchdog" 666 + depends on ARCH_RENESAS || COMPILE_TEST 667 + select WATCHDOG_CORE 668 + help 669 + This driver adds watchdog support for the integrated watchdogs in the 670 + Renesas R-Car and other SH-Mobile SoCs (usually named RWDT or SWDT). 671 + 664 672 # AVR32 Architecture 665 673 666 674 config AT32AP700X_WDT
+1
drivers/watchdog/Makefile
··· 73 73 obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o 74 74 obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o 75 75 obj-$(CONFIG_ATLAS7_WATCHDOG) += atlas7_wdt.o 76 + obj-$(CONFIG_RENESAS_WDT) += renesas_wdt.o 76 77 77 78 # AVR32 Architecture 78 79 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+213
drivers/watchdog/renesas_wdt.c
··· 1 + /* 2 + * Watchdog driver for Renesas WDT watchdog 3 + * 4 + * Copyright (C) 2015-16 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> 5 + * Copyright (C) 2015-16 Renesas Electronics Corporation 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + */ 11 + #include <linux/bitops.h> 12 + #include <linux/clk.h> 13 + #include <linux/io.h> 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/of.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/pm_runtime.h> 19 + #include <linux/watchdog.h> 20 + 21 + #define RWTCNT 0 22 + #define RWTCSRA 4 23 + #define RWTCSRA_WOVF BIT(4) 24 + #define RWTCSRA_WRFLG BIT(5) 25 + #define RWTCSRA_TME BIT(7) 26 + 27 + #define RWDT_DEFAULT_TIMEOUT 60U 28 + 29 + static const unsigned int clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 }; 30 + 31 + static bool nowayout = WATCHDOG_NOWAYOUT; 32 + module_param(nowayout, bool, 0); 33 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 34 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 35 + 36 + struct rwdt_priv { 37 + void __iomem *base; 38 + struct watchdog_device wdev; 39 + struct clk *clk; 40 + unsigned int clks_per_sec; 41 + u8 cks; 42 + }; 43 + 44 + static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg) 45 + { 46 + if (reg == RWTCNT) 47 + val |= 0x5a5a0000; 48 + else 49 + val |= 0xa5a5a500; 50 + 51 + writel_relaxed(val, priv->base + reg); 52 + } 53 + 54 + static int rwdt_init_timeout(struct watchdog_device *wdev) 55 + { 56 + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 57 + 58 + rwdt_write(priv, 65536 - wdev->timeout * priv->clks_per_sec, RWTCNT); 59 + 60 + return 0; 61 + } 62 + 63 + static int rwdt_start(struct watchdog_device *wdev) 64 + { 65 + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 66 + 67 + clk_prepare_enable(priv->clk); 68 + 69 + rwdt_write(priv, priv->cks, RWTCSRA); 70 + rwdt_init_timeout(wdev); 71 + 72 + while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG) 73 + cpu_relax(); 74 + 75 + rwdt_write(priv, priv->cks | RWTCSRA_TME, RWTCSRA); 76 + 77 + return 0; 78 + } 79 + 80 + static int rwdt_stop(struct watchdog_device *wdev) 81 + { 82 + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 83 + 84 + rwdt_write(priv, priv->cks, RWTCSRA); 85 + clk_disable_unprepare(priv->clk); 86 + 87 + return 0; 88 + } 89 + 90 + static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev) 91 + { 92 + struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 93 + u16 val = readw_relaxed(priv->base + RWTCNT); 94 + 95 + return DIV_ROUND_CLOSEST(65536 - val, priv->clks_per_sec); 96 + } 97 + 98 + static const struct watchdog_info rwdt_ident = { 99 + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 100 + .identity = "Renesas WDT Watchdog", 101 + }; 102 + 103 + static const struct watchdog_ops rwdt_ops = { 104 + .owner = THIS_MODULE, 105 + .start = rwdt_start, 106 + .stop = rwdt_stop, 107 + .ping = rwdt_init_timeout, 108 + .get_timeleft = rwdt_get_timeleft, 109 + }; 110 + 111 + static int rwdt_probe(struct platform_device *pdev) 112 + { 113 + struct rwdt_priv *priv; 114 + struct resource *res; 115 + unsigned long rate; 116 + unsigned int clks_per_sec; 117 + int ret, i; 118 + 119 + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 120 + if (!priv) 121 + return -ENOMEM; 122 + 123 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 124 + priv->base = devm_ioremap_resource(&pdev->dev, res); 125 + if (IS_ERR(priv->base)) 126 + return PTR_ERR(priv->base); 127 + 128 + priv->clk = devm_clk_get(&pdev->dev, NULL); 129 + if (IS_ERR(priv->clk)) 130 + return PTR_ERR(priv->clk); 131 + 132 + rate = clk_get_rate(priv->clk); 133 + if (!rate) 134 + return -ENOENT; 135 + 136 + for (i = ARRAY_SIZE(clk_divs) - 1; i >= 0; i--) { 137 + clks_per_sec = DIV_ROUND_UP(rate, clk_divs[i]); 138 + if (clks_per_sec) { 139 + priv->clks_per_sec = clks_per_sec; 140 + priv->cks = i; 141 + break; 142 + } 143 + } 144 + 145 + if (!clks_per_sec) { 146 + dev_err(&pdev->dev, "Can't find suitable clock divider\n"); 147 + return -ERANGE; 148 + } 149 + 150 + pm_runtime_enable(&pdev->dev); 151 + pm_runtime_get_sync(&pdev->dev); 152 + 153 + priv->wdev.info = &rwdt_ident, 154 + priv->wdev.ops = &rwdt_ops, 155 + priv->wdev.parent = &pdev->dev; 156 + priv->wdev.min_timeout = 1; 157 + priv->wdev.max_timeout = 65536 / clks_per_sec; 158 + priv->wdev.timeout = min(priv->wdev.max_timeout, RWDT_DEFAULT_TIMEOUT); 159 + 160 + platform_set_drvdata(pdev, priv); 161 + watchdog_set_drvdata(&priv->wdev, priv); 162 + watchdog_set_nowayout(&priv->wdev, nowayout); 163 + 164 + /* This overrides the default timeout only if DT configuration was found */ 165 + ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); 166 + if (ret) 167 + dev_warn(&pdev->dev, "Specified timeout value invalid, using default\n"); 168 + 169 + ret = watchdog_register_device(&priv->wdev); 170 + if (ret < 0) { 171 + pm_runtime_put(&pdev->dev); 172 + pm_runtime_disable(&pdev->dev); 173 + return ret; 174 + } 175 + 176 + return 0; 177 + } 178 + 179 + static int rwdt_remove(struct platform_device *pdev) 180 + { 181 + struct rwdt_priv *priv = platform_get_drvdata(pdev); 182 + 183 + watchdog_unregister_device(&priv->wdev); 184 + pm_runtime_put(&pdev->dev); 185 + pm_runtime_disable(&pdev->dev); 186 + 187 + return 0; 188 + } 189 + 190 + /* 191 + * This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP 192 + * to work there, one also needs a RESET (RST) driver which does not exist yet 193 + * due to HW issues. This needs to be solved before adding compatibles here. 194 + */ 195 + static const struct of_device_id rwdt_ids[] = { 196 + { .compatible = "renesas,rcar-gen3-wdt", }, 197 + { /* sentinel */ } 198 + }; 199 + MODULE_DEVICE_TABLE(of, rwdt_ids); 200 + 201 + static struct platform_driver rwdt_driver = { 202 + .driver = { 203 + .name = "renesas_wdt", 204 + .of_match_table = rwdt_ids, 205 + }, 206 + .probe = rwdt_probe, 207 + .remove = rwdt_remove, 208 + }; 209 + module_platform_driver(rwdt_driver); 210 + 211 + MODULE_DESCRIPTION("Renesas WDT Watchdog Driver"); 212 + MODULE_LICENSE("GPL v2"); 213 + MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");