brintos

brintos / linux-shallow public Read only

0
0
Text · 8.5 KiB · 2a35f89 Raw
348 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Renesas RZ/G2L WDT Watchdog Driver4 *5 * Copyright (C) 2021 Renesas Electronics Corporation6 */7#include <linux/bitops.h>8#include <linux/clk.h>9#include <linux/delay.h>10#include <linux/io.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/pm_runtime.h>16#include <linux/reset.h>17#include <linux/units.h>18#include <linux/watchdog.h>19 20#define WDTCNT		0x0021#define WDTSET		0x0422#define WDTTIM		0x0823#define WDTINT		0x0C24#define PECR		0x1025#define PEEN		0x1426#define WDTCNT_WDTEN	BIT(0)27#define WDTINT_INTDISP	BIT(0)28#define PEEN_FORCE	BIT(0)29 30#define WDT_DEFAULT_TIMEOUT		60U31 32/* Setting period time register only 12 bit set in WDTSET[31:20] */33#define WDTSET_COUNTER_MASK		(0xFFF00000)34#define WDTSET_COUNTER_VAL(f)		((f) << 20)35 36#define F2CYCLE_NSEC(f)			(1000000000 / (f))37 38#define RZV2M_A_NSEC			73039 40static bool nowayout = WATCHDOG_NOWAYOUT;41module_param(nowayout, bool, 0);42MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="43				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");44 45enum rz_wdt_type {46	WDT_RZG2L,47	WDT_RZV2M,48};49 50struct rzg2l_wdt_priv {51	void __iomem *base;52	struct watchdog_device wdev;53	struct reset_control *rstc;54	unsigned long osc_clk_rate;55	unsigned long delay;56	struct clk *pclk;57	struct clk *osc_clk;58	enum rz_wdt_type devtype;59};60 61static void rzg2l_wdt_wait_delay(struct rzg2l_wdt_priv *priv)62{63	/* delay timer when change the setting register */64	ndelay(priv->delay);65}66 67static u32 rzg2l_wdt_get_cycle_usec(unsigned long cycle, u32 wdttime)68{69	u64 timer_cycle_us = 1024 * 1024ULL * (wdttime + 1) * MICRO;70 71	return div64_ul(timer_cycle_us, cycle);72}73 74static void rzg2l_wdt_write(struct rzg2l_wdt_priv *priv, u32 val, unsigned int reg)75{76	if (reg == WDTSET)77		val &= WDTSET_COUNTER_MASK;78 79	writel_relaxed(val, priv->base + reg);80	/* Registers other than the WDTINT is always synchronized with WDT_CLK */81	if (reg != WDTINT)82		rzg2l_wdt_wait_delay(priv);83}84 85static void rzg2l_wdt_init_timeout(struct watchdog_device *wdev)86{87	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);88	u32 time_out;89 90	/* Clear Lapsed Time Register and clear Interrupt */91	rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);92	/* 2 consecutive overflow cycle needed to trigger reset */93	time_out = (wdev->timeout * (MICRO / 2)) /94		   rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0);95	rzg2l_wdt_write(priv, WDTSET_COUNTER_VAL(time_out), WDTSET);96}97 98static int rzg2l_wdt_start(struct watchdog_device *wdev)99{100	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);101	int ret;102 103	ret = pm_runtime_resume_and_get(wdev->parent);104	if (ret)105		return ret;106 107	ret = reset_control_deassert(priv->rstc);108	if (ret) {109		pm_runtime_put(wdev->parent);110		return ret;111	}112 113	/* Initialize time out */114	rzg2l_wdt_init_timeout(wdev);115 116	/* Initialize watchdog counter register */117	rzg2l_wdt_write(priv, 0, WDTTIM);118 119	/* Enable watchdog timer*/120	rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);121 122	return 0;123}124 125static int rzg2l_wdt_stop(struct watchdog_device *wdev)126{127	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);128	int ret;129 130	ret = reset_control_assert(priv->rstc);131	if (ret)132		return ret;133 134	ret = pm_runtime_put(wdev->parent);135	if (ret < 0)136		return ret;137 138	return 0;139}140 141static int rzg2l_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)142{143	int ret = 0;144 145	wdev->timeout = timeout;146 147	/*148	 * If the watchdog is active, reset the module for updating the WDTSET149	 * register by calling rzg2l_wdt_stop() (which internally calls reset_control_reset()150	 * to reset the module) so that it is updated with new timeout values.151	 */152	if (watchdog_active(wdev)) {153		ret = rzg2l_wdt_stop(wdev);154		if (ret)155			return ret;156 157		ret = rzg2l_wdt_start(wdev);158	}159 160	return ret;161}162 163static int rzg2l_wdt_restart(struct watchdog_device *wdev,164			     unsigned long action, void *data)165{166	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);167	int ret;168 169	clk_prepare_enable(priv->pclk);170	clk_prepare_enable(priv->osc_clk);171 172	if (priv->devtype == WDT_RZG2L) {173		ret = reset_control_deassert(priv->rstc);174		if (ret)175			return ret;176 177		/* Generate Reset (WDTRSTB) Signal on parity error */178		rzg2l_wdt_write(priv, 0, PECR);179 180		/* Force parity error */181		rzg2l_wdt_write(priv, PEEN_FORCE, PEEN);182	} else {183		/* RZ/V2M doesn't have parity error registers */184		ret = reset_control_reset(priv->rstc);185		if (ret)186			return ret;187 188		wdev->timeout = 0;189 190		/* Initialize time out */191		rzg2l_wdt_init_timeout(wdev);192 193		/* Initialize watchdog counter register */194		rzg2l_wdt_write(priv, 0, WDTTIM);195 196		/* Enable watchdog timer*/197		rzg2l_wdt_write(priv, WDTCNT_WDTEN, WDTCNT);198 199		/* Wait 2 consecutive overflow cycles for reset */200		mdelay(DIV_ROUND_UP(2 * 0xFFFFF * 1000, priv->osc_clk_rate));201	}202 203	return 0;204}205 206static const struct watchdog_info rzg2l_wdt_ident = {207	.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,208	.identity = "Renesas RZ/G2L WDT Watchdog",209};210 211static int rzg2l_wdt_ping(struct watchdog_device *wdev)212{213	struct rzg2l_wdt_priv *priv = watchdog_get_drvdata(wdev);214 215	rzg2l_wdt_write(priv, WDTINT_INTDISP, WDTINT);216 217	return 0;218}219 220static const struct watchdog_ops rzg2l_wdt_ops = {221	.owner = THIS_MODULE,222	.start = rzg2l_wdt_start,223	.stop = rzg2l_wdt_stop,224	.ping = rzg2l_wdt_ping,225	.set_timeout = rzg2l_wdt_set_timeout,226	.restart = rzg2l_wdt_restart,227};228 229static void rzg2l_wdt_pm_disable(void *data)230{231	struct watchdog_device *wdev = data;232 233	pm_runtime_disable(wdev->parent);234}235 236static int rzg2l_wdt_probe(struct platform_device *pdev)237{238	struct device *dev = &pdev->dev;239	struct rzg2l_wdt_priv *priv;240	unsigned long pclk_rate;241	int ret;242 243	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);244	if (!priv)245		return -ENOMEM;246 247	priv->base = devm_platform_ioremap_resource(pdev, 0);248	if (IS_ERR(priv->base))249		return PTR_ERR(priv->base);250 251	/* Get watchdog main clock */252	priv->osc_clk = devm_clk_get(&pdev->dev, "oscclk");253	if (IS_ERR(priv->osc_clk))254		return dev_err_probe(&pdev->dev, PTR_ERR(priv->osc_clk), "no oscclk");255 256	priv->osc_clk_rate = clk_get_rate(priv->osc_clk);257	if (!priv->osc_clk_rate)258		return dev_err_probe(&pdev->dev, -EINVAL, "oscclk rate is 0");259 260	/* Get Peripheral clock */261	priv->pclk = devm_clk_get(&pdev->dev, "pclk");262	if (IS_ERR(priv->pclk))263		return dev_err_probe(&pdev->dev, PTR_ERR(priv->pclk), "no pclk");264 265	pclk_rate = clk_get_rate(priv->pclk);266	if (!pclk_rate)267		return dev_err_probe(&pdev->dev, -EINVAL, "pclk rate is 0");268 269	priv->delay = F2CYCLE_NSEC(priv->osc_clk_rate) * 6 + F2CYCLE_NSEC(pclk_rate) * 9;270 271	priv->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);272	if (IS_ERR(priv->rstc))273		return dev_err_probe(&pdev->dev, PTR_ERR(priv->rstc),274				     "failed to get cpg reset");275 276	priv->devtype = (uintptr_t)of_device_get_match_data(dev);277 278	pm_runtime_enable(&pdev->dev);279 280	priv->wdev.info = &rzg2l_wdt_ident;281	priv->wdev.ops = &rzg2l_wdt_ops;282	priv->wdev.parent = dev;283	priv->wdev.min_timeout = 1;284	priv->wdev.max_timeout = rzg2l_wdt_get_cycle_usec(priv->osc_clk_rate, 0xfff) /285				 USEC_PER_SEC;286	priv->wdev.timeout = WDT_DEFAULT_TIMEOUT;287 288	watchdog_set_drvdata(&priv->wdev, priv);289	dev_set_drvdata(dev, priv);290	ret = devm_add_action_or_reset(&pdev->dev, rzg2l_wdt_pm_disable, &priv->wdev);291	if (ret)292		return ret;293 294	watchdog_set_nowayout(&priv->wdev, nowayout);295	watchdog_stop_on_unregister(&priv->wdev);296 297	ret = watchdog_init_timeout(&priv->wdev, 0, dev);298	if (ret)299		dev_warn(dev, "Specified timeout invalid, using default");300 301	return devm_watchdog_register_device(&pdev->dev, &priv->wdev);302}303 304static const struct of_device_id rzg2l_wdt_ids[] = {305	{ .compatible = "renesas,rzg2l-wdt", .data = (void *)WDT_RZG2L },306	{ .compatible = "renesas,rzv2m-wdt", .data = (void *)WDT_RZV2M },307	{ /* sentinel */ }308};309MODULE_DEVICE_TABLE(of, rzg2l_wdt_ids);310 311static int rzg2l_wdt_suspend_late(struct device *dev)312{313	struct rzg2l_wdt_priv *priv = dev_get_drvdata(dev);314 315	if (!watchdog_active(&priv->wdev))316		return 0;317 318	return rzg2l_wdt_stop(&priv->wdev);319}320 321static int rzg2l_wdt_resume_early(struct device *dev)322{323	struct rzg2l_wdt_priv *priv = dev_get_drvdata(dev);324 325	if (!watchdog_active(&priv->wdev))326		return 0;327 328	return rzg2l_wdt_start(&priv->wdev);329}330 331static const struct dev_pm_ops rzg2l_wdt_pm_ops = {332	LATE_SYSTEM_SLEEP_PM_OPS(rzg2l_wdt_suspend_late, rzg2l_wdt_resume_early)333};334 335static struct platform_driver rzg2l_wdt_driver = {336	.driver = {337		.name = "rzg2l_wdt",338		.of_match_table = rzg2l_wdt_ids,339		.pm = &rzg2l_wdt_pm_ops,340	},341	.probe = rzg2l_wdt_probe,342};343module_platform_driver(rzg2l_wdt_driver);344 345MODULE_DESCRIPTION("Renesas RZ/G2L WDT Watchdog Driver");346MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>");347MODULE_LICENSE("GPL v2");348