274 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Renesas RZ/V2H(P) WDT Watchdog Driver4 *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 0x0025#define WDTCR_TOPS_16384 0x0326 27#define WDTCR_CKS_CLK_1 0x0028#define WDTCR_CKS_CLK_256 0x5029 30#define WDTCR_RPES_0 0x30031#define WDTCR_RPES_75 0x00032 33#define WDTCR_RPSS_25 0x0034#define WDTCR_RPSS_100 0x300035 36#define WDTRCR_RSTIRQS BIT(7)37 38#define MAX_TIMEOUT_CYCLES 1638439#define CLOCK_DIV_BY_256 25640 41#define WDT_DEFAULT_TIMEOUT 60U42 43static bool nowayout = WATCHDOG_NOWAYOUT;44module_param(nowayout, bool, 0);45MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="46 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");47 48struct 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 56static 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 on62 * 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 70static 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 84static 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 * WDTCR104 * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256105 * - 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 the114 * WDTRR register. Hence, call the ping operation after loading the counter.115 */116 rzv2h_wdt_ping(wdev);117 118 return 0;119}120 121static 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 137static const struct watchdog_info rzv2h_wdt_ident = {138 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,139 .identity = "Renesas RZ/V2H WDT Watchdog",140};141 142static 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 Reset168 * Control Register (WDTRCR) is possible once between the169 * 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 * WDTCR182 * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1183 * - 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 198static 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 206static 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 257static const struct of_device_id rzv2h_wdt_ids[] = {258 { .compatible = "renesas,r9a09g057-wdt", },259 { /* sentinel */ }260};261MODULE_DEVICE_TABLE(of, rzv2h_wdt_ids);262 263static 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};270module_platform_driver(rzv2h_wdt_driver);271MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");272MODULE_DESCRIPTION("Renesas RZ/V2H(P) WDT Watchdog Driver");273MODULE_LICENSE("GPL");274