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

Configure Feed

Select the types of activity you want to include in your feed.

watchdog: Add Watchdog Timer driver for RZ/V2H(P)

Add Watchdog Timer driver for RZ/V2H(P) SoC.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20240829193831.80768-3-prabhakar.mahadev-lad.rj@bp.renesas.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Lad Prabhakar and committed by
Wim Van Sebroeck
f6febd0a 892067cd

+282
+9
drivers/watchdog/Kconfig
··· 953 953 This driver adds watchdog support for the integrated watchdogs in the 954 954 Renesas RZ/G2L SoCs. These watchdogs can be used to reset a system. 955 955 956 + config RENESAS_RZV2HWDT 957 + tristate "Renesas RZ/V2H(P) WDT Watchdog" 958 + depends on ARCH_R9A09G057 || COMPILE_TEST 959 + depends on PM || COMPILE_TEST 960 + select WATCHDOG_CORE 961 + help 962 + This driver adds watchdog support for the integrated watchdogs in the 963 + Renesas RZ/V2H(P) SoCs. These watchdogs can be used to reset a system. 964 + 956 965 config ASPEED_WATCHDOG 957 966 tristate "Aspeed BMC watchdog support" 958 967 depends on ARCH_ASPEED || COMPILE_TEST
+1
drivers/watchdog/Makefile
··· 86 86 obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o 87 87 obj-$(CONFIG_RENESAS_RZN1WDT) += rzn1_wdt.o 88 88 obj-$(CONFIG_RENESAS_RZG2LWDT) += rzg2l_wdt.o 89 + obj-$(CONFIG_RENESAS_RZV2HWDT) += rzv2h_wdt.o 89 90 obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o 90 91 obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o 91 92 obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
+272
drivers/watchdog/rzv2h_wdt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Renesas RZ/V2H(P) WDT Watchdog Driver 4 + * 5 + * Copyright (C) 2024 Renesas Electronics Corporation. 6 + */ 7 + #include <linux/clk.h> 8 + #include <linux/delay.h> 9 + #include <linux/io.h> 10 + #include <linux/kernel.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/pm_runtime.h> 15 + #include <linux/reset.h> 16 + #include <linux/units.h> 17 + #include <linux/watchdog.h> 18 + 19 + #define WDTRR 0x00 /* WDT Refresh Register RW, 8 */ 20 + #define WDTCR 0x02 /* WDT Control Register RW, 16 */ 21 + #define WDTSR 0x04 /* WDT Status Register RW, 16 */ 22 + #define WDTRCR 0x06 /* WDT Reset Control Register RW, 8 */ 23 + 24 + #define WDTCR_TOPS_1024 0x00 25 + #define WDTCR_TOPS_16384 0x03 26 + 27 + #define WDTCR_CKS_CLK_1 0x00 28 + #define WDTCR_CKS_CLK_256 0x50 29 + 30 + #define WDTCR_RPES_0 0x300 31 + #define WDTCR_RPES_75 0x000 32 + 33 + #define WDTCR_RPSS_25 0x00 34 + #define WDTCR_RPSS_100 0x3000 35 + 36 + #define WDTRCR_RSTIRQS BIT(7) 37 + 38 + #define MAX_TIMEOUT_CYCLES 16384 39 + #define CLOCK_DIV_BY_256 256 40 + 41 + #define WDT_DEFAULT_TIMEOUT 60U 42 + 43 + static bool nowayout = WATCHDOG_NOWAYOUT; 44 + module_param(nowayout, bool, 0); 45 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 46 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 47 + 48 + struct rzv2h_wdt_priv { 49 + void __iomem *base; 50 + struct clk *pclk; 51 + struct clk *oscclk; 52 + struct reset_control *rstc; 53 + struct watchdog_device wdev; 54 + }; 55 + 56 + static int rzv2h_wdt_ping(struct watchdog_device *wdev) 57 + { 58 + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); 59 + 60 + /* 61 + * The down-counter is refreshed and starts counting operation on 62 + * a write of the values 00h and FFh to the WDTRR register. 63 + */ 64 + writeb(0x0, priv->base + WDTRR); 65 + writeb(0xFF, priv->base + WDTRR); 66 + 67 + return 0; 68 + } 69 + 70 + static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr) 71 + { 72 + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); 73 + 74 + /* Configure the timeout, clock division ratio, and window start and end positions. */ 75 + writew(wdtcr, priv->base + WDTCR); 76 + 77 + /* Enable interrupt output to the ICU. */ 78 + writeb(0, priv->base + WDTRCR); 79 + 80 + /* Clear underflow flag and refresh error flag. */ 81 + writew(0, priv->base + WDTSR); 82 + } 83 + 84 + static int rzv2h_wdt_start(struct watchdog_device *wdev) 85 + { 86 + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); 87 + int ret; 88 + 89 + ret = pm_runtime_resume_and_get(wdev->parent); 90 + if (ret) 91 + return ret; 92 + 93 + ret = reset_control_deassert(priv->rstc); 94 + if (ret) { 95 + pm_runtime_put(wdev->parent); 96 + return ret; 97 + } 98 + 99 + /* delay to handle clock halt after de-assert operation */ 100 + udelay(3); 101 + 102 + /* 103 + * WDTCR 104 + * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256 105 + * - RPSS[13:12] - Window Start Position Select - 11b: 100% 106 + * - RPES[9:8] - Window End Position Select - 11b: 0% 107 + * - TOPS[1:0] - Timeout Period Select - 11b: 16384 cycles (3FFFh) 108 + */ 109 + rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_256 | WDTCR_RPSS_100 | 110 + WDTCR_RPES_0 | WDTCR_TOPS_16384); 111 + 112 + /* 113 + * Down counting starts after writing the sequence 00h -> FFh to the 114 + * WDTRR register. Hence, call the ping operation after loading the counter. 115 + */ 116 + rzv2h_wdt_ping(wdev); 117 + 118 + return 0; 119 + } 120 + 121 + static int rzv2h_wdt_stop(struct watchdog_device *wdev) 122 + { 123 + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); 124 + int ret; 125 + 126 + ret = reset_control_assert(priv->rstc); 127 + if (ret) 128 + return ret; 129 + 130 + ret = pm_runtime_put(wdev->parent); 131 + if (ret < 0) 132 + return ret; 133 + 134 + return 0; 135 + } 136 + 137 + static const struct watchdog_info rzv2h_wdt_ident = { 138 + .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 139 + .identity = "Renesas RZ/V2H WDT Watchdog", 140 + }; 141 + 142 + static int rzv2h_wdt_restart(struct watchdog_device *wdev, 143 + unsigned long action, void *data) 144 + { 145 + struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); 146 + int ret; 147 + 148 + if (!watchdog_active(wdev)) { 149 + ret = clk_enable(priv->pclk); 150 + if (ret) 151 + return ret; 152 + 153 + ret = clk_enable(priv->oscclk); 154 + if (ret) { 155 + clk_disable(priv->pclk); 156 + return ret; 157 + } 158 + 159 + ret = reset_control_deassert(priv->rstc); 160 + if (ret) { 161 + clk_disable(priv->oscclk); 162 + clk_disable(priv->pclk); 163 + return ret; 164 + } 165 + } else { 166 + /* 167 + * Writing to the WDT Control Register (WDTCR) or WDT Reset 168 + * Control Register (WDTRCR) is possible once between the 169 + * release from the reset state and the first refresh operation. 170 + * Therefore, issue a reset if the watchdog is active. 171 + */ 172 + ret = reset_control_reset(priv->rstc); 173 + if (ret) 174 + return ret; 175 + } 176 + 177 + /* delay to handle clock halt after de-assert operation */ 178 + udelay(3); 179 + 180 + /* 181 + * WDTCR 182 + * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1 183 + * - RPSS[13:12] - Window Start Position Select - 00b: 25% 184 + * - RPES[9:8] - Window End Position Select - 00b: 75% 185 + * - TOPS[1:0] - Timeout Period Select - 00b: 1024 cycles (03FFh) 186 + */ 187 + rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_1 | WDTCR_RPSS_25 | 188 + WDTCR_RPES_75 | WDTCR_TOPS_1024); 189 + 190 + rzv2h_wdt_ping(wdev); 191 + 192 + /* wait for underflow to trigger... */ 193 + udelay(5); 194 + 195 + return 0; 196 + } 197 + 198 + static const struct watchdog_ops rzv2h_wdt_ops = { 199 + .owner = THIS_MODULE, 200 + .start = rzv2h_wdt_start, 201 + .stop = rzv2h_wdt_stop, 202 + .ping = rzv2h_wdt_ping, 203 + .restart = rzv2h_wdt_restart, 204 + }; 205 + 206 + static int rzv2h_wdt_probe(struct platform_device *pdev) 207 + { 208 + struct device *dev = &pdev->dev; 209 + struct rzv2h_wdt_priv *priv; 210 + int ret; 211 + 212 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 213 + if (!priv) 214 + return -ENOMEM; 215 + 216 + priv->base = devm_platform_ioremap_resource(pdev, 0); 217 + if (IS_ERR(priv->base)) 218 + return PTR_ERR(priv->base); 219 + 220 + priv->pclk = devm_clk_get_prepared(&pdev->dev, "pclk"); 221 + if (IS_ERR(priv->pclk)) 222 + return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk"); 223 + 224 + priv->oscclk = devm_clk_get_prepared(&pdev->dev, "oscclk"); 225 + if (IS_ERR(priv->oscclk)) 226 + return dev_err_probe(&pdev->dev, PTR_ERR(priv->oscclk), "no oscclk"); 227 + 228 + priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); 229 + if (IS_ERR(priv->rstc)) 230 + return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc), 231 + "failed to get cpg reset"); 232 + 233 + priv->wdev.max_hw_heartbeat_ms = (MILLI * MAX_TIMEOUT_CYCLES * CLOCK_DIV_BY_256) / 234 + clk_get_rate(priv->oscclk); 235 + dev_dbg(dev, "max hw timeout of %dms\n", priv->wdev.max_hw_heartbeat_ms); 236 + 237 + ret = devm_pm_runtime_enable(&pdev->dev); 238 + if (ret) 239 + return ret; 240 + 241 + priv->wdev.min_timeout = 1; 242 + priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; 243 + priv->wdev.info = &rzv2h_wdt_ident; 244 + priv->wdev.ops = &rzv2h_wdt_ops; 245 + priv->wdev.parent = dev; 246 + watchdog_set_drvdata(&priv->wdev, priv); 247 + watchdog_set_nowayout(&priv->wdev, nowayout); 248 + watchdog_stop_on_unregister(&priv->wdev); 249 + 250 + ret = watchdog_init_timeout(&priv->wdev, 0, dev); 251 + if (ret) 252 + dev_warn(dev, "Specified timeout invalid, using default"); 253 + 254 + return devm_watchdog_register_device(&pdev->dev, &priv->wdev); 255 + } 256 + 257 + static const struct of_device_id rzv2h_wdt_ids[] = { 258 + { .compatible = "renesas,r9a09g057-wdt", }, 259 + { /* sentinel */ } 260 + }; 261 + MODULE_DEVICE_TABLE(of, rzv2h_wdt_ids); 262 + 263 + static struct platform_driver rzv2h_wdt_driver = { 264 + .driver = { 265 + .name = "rzv2h_wdt", 266 + .of_match_table = rzv2h_wdt_ids, 267 + }, 268 + .probe = rzv2h_wdt_probe, 269 + }; 270 + module_platform_driver(rzv2h_wdt_driver); 271 + MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>"); 272 + MODULE_DESCRIPTION("Renesas RZ/V2H(P) WDT Watchdog Driver");