cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sunplus_wdt.c (5422B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * sunplus Watchdog Driver
      4 *
      5 * Copyright (C) 2021 Sunplus Technology Co., Ltd.
      6 *
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/io.h>
     11#include <linux/module.h>
     12#include <linux/mod_devicetable.h>
     13#include <linux/platform_device.h>
     14#include <linux/reset.h>
     15#include <linux/watchdog.h>
     16
     17#define WDT_CTRL		0x00
     18#define WDT_CNT			0x04
     19
     20#define WDT_STOP		0x3877
     21#define WDT_RESUME		0x4A4B
     22#define WDT_CLRIRQ		0x7482
     23#define WDT_UNLOCK		0xAB00
     24#define WDT_LOCK		0xAB01
     25#define WDT_CONMAX		0xDEAF
     26
     27/* TIMEOUT_MAX = ffff0/90kHz =11.65, so longer than 11 seconds will time out. */
     28#define SP_WDT_MAX_TIMEOUT	11U
     29#define SP_WDT_DEFAULT_TIMEOUT	10
     30
     31#define STC_CLK			90000
     32
     33#define DEVICE_NAME		"sunplus-wdt"
     34
     35static unsigned int timeout;
     36module_param(timeout, int, 0);
     37MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
     38
     39static bool nowayout = WATCHDOG_NOWAYOUT;
     40module_param(nowayout, bool, 0);
     41MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
     42			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
     43
     44struct sp_wdt_priv {
     45	struct watchdog_device wdev;
     46	void __iomem *base;
     47	struct clk *clk;
     48	struct reset_control *rstc;
     49};
     50
     51static int sp_wdt_restart(struct watchdog_device *wdev,
     52			  unsigned long action, void *data)
     53{
     54	struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
     55	void __iomem *base = priv->base;
     56
     57	writel(WDT_STOP, base + WDT_CTRL);
     58	writel(WDT_UNLOCK, base + WDT_CTRL);
     59	writel(0x0001, base + WDT_CNT);
     60	writel(WDT_LOCK, base + WDT_CTRL);
     61	writel(WDT_RESUME, base + WDT_CTRL);
     62
     63	return 0;
     64}
     65
     66static int sp_wdt_ping(struct watchdog_device *wdev)
     67{
     68	struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
     69	void __iomem *base = priv->base;
     70	u32 count;
     71
     72	if (wdev->timeout > SP_WDT_MAX_TIMEOUT) {
     73		/* WDT_CONMAX sets the count to the maximum (down-counting). */
     74		writel(WDT_CONMAX, base + WDT_CTRL);
     75	} else {
     76		writel(WDT_UNLOCK, base + WDT_CTRL);
     77		/*
     78		 * Watchdog timer is a 20-bit down-counting based on STC_CLK.
     79		 * This register bits[16:0] is from bit[19:4] of the watchdog
     80		 * timer counter.
     81		 */
     82		count = (wdev->timeout * STC_CLK) >> 4;
     83		writel(count, base + WDT_CNT);
     84		writel(WDT_LOCK, base + WDT_CTRL);
     85	}
     86
     87	return 0;
     88}
     89
     90static int sp_wdt_stop(struct watchdog_device *wdev)
     91{
     92	struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
     93	void __iomem *base = priv->base;
     94
     95	writel(WDT_STOP, base + WDT_CTRL);
     96
     97	return 0;
     98}
     99
    100static int sp_wdt_start(struct watchdog_device *wdev)
    101{
    102	struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
    103	void __iomem *base = priv->base;
    104
    105	writel(WDT_RESUME, base + WDT_CTRL);
    106
    107	return 0;
    108}
    109
    110static unsigned int sp_wdt_get_timeleft(struct watchdog_device *wdev)
    111{
    112	struct sp_wdt_priv *priv = watchdog_get_drvdata(wdev);
    113	void __iomem *base = priv->base;
    114	u32 val;
    115
    116	val = readl(base + WDT_CNT);
    117	val &= 0xffff;
    118	val = val << 4;
    119
    120	return val;
    121}
    122
    123static const struct watchdog_info sp_wdt_info = {
    124	.identity	= DEVICE_NAME,
    125	.options	= WDIOF_SETTIMEOUT |
    126			  WDIOF_MAGICCLOSE |
    127			  WDIOF_KEEPALIVEPING,
    128};
    129
    130static const struct watchdog_ops sp_wdt_ops = {
    131	.owner		= THIS_MODULE,
    132	.start		= sp_wdt_start,
    133	.stop		= sp_wdt_stop,
    134	.ping		= sp_wdt_ping,
    135	.get_timeleft	= sp_wdt_get_timeleft,
    136	.restart	= sp_wdt_restart,
    137};
    138
    139static void sp_clk_disable_unprepare(void *data)
    140{
    141	clk_disable_unprepare(data);
    142}
    143
    144static void sp_reset_control_assert(void *data)
    145{
    146	reset_control_assert(data);
    147}
    148
    149static int sp_wdt_probe(struct platform_device *pdev)
    150{
    151	struct device *dev = &pdev->dev;
    152	struct sp_wdt_priv *priv;
    153	int ret;
    154
    155	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    156	if (!priv)
    157		return -ENOMEM;
    158
    159	priv->clk = devm_clk_get(dev, NULL);
    160	if (IS_ERR(priv->clk))
    161		return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clock\n");
    162
    163	ret = clk_prepare_enable(priv->clk);
    164	if (ret)
    165		return dev_err_probe(dev, ret, "Failed to enable clock\n");
    166
    167	ret = devm_add_action_or_reset(dev, sp_clk_disable_unprepare, priv->clk);
    168	if (ret)
    169		return ret;
    170
    171	/* The timer and watchdog shared the STC reset */
    172	priv->rstc = devm_reset_control_get_shared(dev, NULL);
    173	if (IS_ERR(priv->rstc))
    174		return dev_err_probe(dev, PTR_ERR(priv->rstc), "Failed to get reset\n");
    175
    176	reset_control_deassert(priv->rstc);
    177
    178	ret = devm_add_action_or_reset(dev, sp_reset_control_assert, priv->rstc);
    179	if (ret)
    180		return ret;
    181
    182	priv->base = devm_platform_ioremap_resource(pdev, 0);
    183	if (IS_ERR(priv->base))
    184		return PTR_ERR(priv->base);
    185
    186	priv->wdev.info = &sp_wdt_info;
    187	priv->wdev.ops = &sp_wdt_ops;
    188	priv->wdev.timeout = SP_WDT_DEFAULT_TIMEOUT;
    189	priv->wdev.max_hw_heartbeat_ms = SP_WDT_MAX_TIMEOUT * 1000;
    190	priv->wdev.min_timeout = 1;
    191	priv->wdev.parent = dev;
    192
    193	watchdog_set_drvdata(&priv->wdev, priv);
    194	watchdog_init_timeout(&priv->wdev, timeout, dev);
    195	watchdog_set_nowayout(&priv->wdev, nowayout);
    196	watchdog_stop_on_reboot(&priv->wdev);
    197	watchdog_set_restart_priority(&priv->wdev, 128);
    198
    199	return devm_watchdog_register_device(dev, &priv->wdev);
    200}
    201
    202static const struct of_device_id sp_wdt_of_match[] = {
    203	{.compatible = "sunplus,sp7021-wdt", },
    204	{ /* sentinel */ }
    205};
    206MODULE_DEVICE_TABLE(of, sp_wdt_of_match);
    207
    208static struct platform_driver sp_wdt_driver = {
    209	.probe = sp_wdt_probe,
    210	.driver = {
    211		   .name = DEVICE_NAME,
    212		   .of_match_table = sp_wdt_of_match,
    213	},
    214};
    215
    216module_platform_driver(sp_wdt_driver);
    217
    218MODULE_AUTHOR("Xiantao Hu <xt.hu@cqplus1.com>");
    219MODULE_DESCRIPTION("Sunplus Watchdog Timer Driver");
    220MODULE_LICENSE("GPL");