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

visconti_wdt.c (5064B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2020 TOSHIBA CORPORATION
      4 * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation
      5 * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
      6 */
      7
      8#include <linux/clk.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/watchdog.h>
     15
     16#define WDT_CNT			0x00
     17#define WDT_MIN			0x04
     18#define WDT_MAX			0x08
     19#define WDT_CTL			0x0c
     20#define WDT_CMD			0x10
     21#define WDT_CMD_CLEAR		0x4352
     22#define WDT_CMD_START_STOP	0x5354
     23#define WDT_DIV			0x30
     24
     25#define VISCONTI_WDT_FREQ	2000000 /* 2MHz */
     26#define WDT_DEFAULT_TIMEOUT	10U /* in seconds */
     27
     28static bool nowayout = WATCHDOG_NOWAYOUT;
     29module_param(nowayout, bool, 0);
     30MODULE_PARM_DESC(
     31	nowayout,
     32	"Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)")");
     33
     34struct visconti_wdt_priv {
     35	struct watchdog_device wdev;
     36	void __iomem *base;
     37	u32 div;
     38};
     39
     40static int visconti_wdt_start(struct watchdog_device *wdev)
     41{
     42	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
     43	u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
     44
     45	writel(priv->div, priv->base + WDT_DIV);
     46	writel(0, priv->base + WDT_MIN);
     47	writel(timeout, priv->base + WDT_MAX);
     48	writel(0, priv->base + WDT_CTL);
     49	writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
     50
     51	return 0;
     52}
     53
     54static int visconti_wdt_stop(struct watchdog_device *wdev)
     55{
     56	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
     57
     58	writel(1, priv->base + WDT_CTL);
     59	writel(WDT_CMD_START_STOP, priv->base + WDT_CMD);
     60
     61	return 0;
     62}
     63
     64static int visconti_wdt_ping(struct watchdog_device *wdd)
     65{
     66	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd);
     67
     68	writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
     69
     70	return 0;
     71}
     72
     73static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev)
     74{
     75	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
     76	u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ;
     77	u32 cnt = readl(priv->base + WDT_CNT);
     78
     79	if (timeout <= cnt)
     80		return 0;
     81	timeout -= cnt;
     82
     83	return timeout / VISCONTI_WDT_FREQ;
     84}
     85
     86static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout)
     87{
     88	u32 val;
     89	struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev);
     90
     91	wdev->timeout = timeout;
     92	val = wdev->timeout * VISCONTI_WDT_FREQ;
     93
     94	/* Clear counter before setting timeout because WDT expires */
     95	writel(WDT_CMD_CLEAR, priv->base + WDT_CMD);
     96	writel(val, priv->base + WDT_MAX);
     97
     98	return 0;
     99}
    100
    101static const struct watchdog_info visconti_wdt_info = {
    102	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
    103	.identity = "Visconti Watchdog",
    104};
    105
    106static const struct watchdog_ops visconti_wdt_ops = {
    107	.owner		= THIS_MODULE,
    108	.start		= visconti_wdt_start,
    109	.stop		= visconti_wdt_stop,
    110	.ping		= visconti_wdt_ping,
    111	.get_timeleft	= visconti_wdt_get_timeleft,
    112	.set_timeout	= visconti_wdt_set_timeout,
    113};
    114
    115static void visconti_clk_disable_unprepare(void *data)
    116{
    117	clk_disable_unprepare(data);
    118}
    119
    120static int visconti_wdt_probe(struct platform_device *pdev)
    121{
    122	struct watchdog_device *wdev;
    123	struct visconti_wdt_priv *priv;
    124	struct device *dev = &pdev->dev;
    125	struct clk *clk;
    126	int ret;
    127	unsigned long clk_freq;
    128
    129	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    130	if (!priv)
    131		return -ENOMEM;
    132
    133	priv->base = devm_platform_ioremap_resource(pdev, 0);
    134	if (IS_ERR(priv->base))
    135		return PTR_ERR(priv->base);
    136
    137	clk = devm_clk_get(dev, NULL);
    138	if (IS_ERR(clk))
    139		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n");
    140
    141	ret = clk_prepare_enable(clk);
    142	if (ret) {
    143		dev_err(dev, "Could not enable clock\n");
    144		return ret;
    145	}
    146
    147	ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk);
    148	if (ret)
    149		return ret;
    150
    151	clk_freq = clk_get_rate(clk);
    152	if (!clk_freq)
    153		return -EINVAL;
    154
    155	priv->div = clk_freq / VISCONTI_WDT_FREQ;
    156
    157	/* Initialize struct watchdog_device. */
    158	wdev = &priv->wdev;
    159	wdev->info = &visconti_wdt_info;
    160	wdev->ops = &visconti_wdt_ops;
    161	wdev->parent = dev;
    162	wdev->min_timeout = 1;
    163	wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ;
    164	wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT);
    165
    166	watchdog_set_drvdata(wdev, priv);
    167	watchdog_set_nowayout(wdev, nowayout);
    168	watchdog_stop_on_unregister(wdev);
    169
    170	/* This overrides the default timeout only if DT configuration was found */
    171	ret = watchdog_init_timeout(wdev, 0, dev);
    172	if (ret)
    173		dev_warn(dev, "Specified timeout value invalid, using default\n");
    174
    175	return devm_watchdog_register_device(dev, wdev);
    176}
    177
    178static const struct of_device_id visconti_wdt_of_match[] = {
    179	{ .compatible = "toshiba,visconti-wdt", },
    180	{}
    181};
    182MODULE_DEVICE_TABLE(of, visconti_wdt_of_match);
    183
    184static struct platform_driver visconti_wdt_driver = {
    185	.driver = {
    186			.name = "visconti_wdt",
    187			.of_match_table = visconti_wdt_of_match,
    188		},
    189	.probe = visconti_wdt_probe,
    190};
    191module_platform_driver(visconti_wdt_driver);
    192
    193MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver");
    194MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
    195MODULE_LICENSE("GPL v2");