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

msc313e_wdt.c (4345B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * MStar WDT driver
      4 *
      5 * Copyright (C) 2019 - 2021 Daniel Palmer
      6 * Copyright (C) 2021 Romain Perier
      7 *
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/io.h>
     12#include <linux/mod_devicetable.h>
     13#include <linux/module.h>
     14#include <linux/platform_device.h>
     15#include <linux/watchdog.h>
     16
     17#define REG_WDT_CLR			0x0
     18#define REG_WDT_MAX_PRD_L		0x10
     19#define REG_WDT_MAX_PRD_H		0x14
     20
     21#define MSC313E_WDT_MIN_TIMEOUT		1
     22#define MSC313E_WDT_DEFAULT_TIMEOUT	30
     23
     24static unsigned int timeout;
     25
     26module_param(timeout, int, 0);
     27MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
     28
     29struct msc313e_wdt_priv {
     30	void __iomem *base;
     31	struct watchdog_device wdev;
     32	struct clk *clk;
     33};
     34
     35static int msc313e_wdt_start(struct watchdog_device *wdev)
     36{
     37	struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
     38	u32 timeout;
     39	int err;
     40
     41	err = clk_prepare_enable(priv->clk);
     42	if (err)
     43		return err;
     44
     45	timeout = wdev->timeout * clk_get_rate(priv->clk);
     46	writew(timeout & 0xffff, priv->base + REG_WDT_MAX_PRD_L);
     47	writew((timeout >> 16) & 0xffff, priv->base + REG_WDT_MAX_PRD_H);
     48	writew(1, priv->base + REG_WDT_CLR);
     49	return 0;
     50}
     51
     52static int msc313e_wdt_ping(struct watchdog_device *wdev)
     53{
     54	struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
     55
     56	writew(1, priv->base + REG_WDT_CLR);
     57	return 0;
     58}
     59
     60static int msc313e_wdt_stop(struct watchdog_device *wdev)
     61{
     62	struct msc313e_wdt_priv *priv = watchdog_get_drvdata(wdev);
     63
     64	writew(0, priv->base + REG_WDT_MAX_PRD_L);
     65	writew(0, priv->base + REG_WDT_MAX_PRD_H);
     66	writew(0, priv->base + REG_WDT_CLR);
     67	clk_disable_unprepare(priv->clk);
     68	return 0;
     69}
     70
     71static int msc313e_wdt_settimeout(struct watchdog_device *wdev, unsigned int new_time)
     72{
     73	wdev->timeout = new_time;
     74
     75	return msc313e_wdt_start(wdev);
     76}
     77
     78static const struct watchdog_info msc313e_wdt_ident = {
     79	.identity = "MSC313e watchdog",
     80	.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
     81};
     82
     83static const struct watchdog_ops msc313e_wdt_ops = {
     84	.owner = THIS_MODULE,
     85	.start = msc313e_wdt_start,
     86	.stop = msc313e_wdt_stop,
     87	.ping = msc313e_wdt_ping,
     88	.set_timeout = msc313e_wdt_settimeout,
     89};
     90
     91static const struct of_device_id msc313e_wdt_of_match[] = {
     92	{ .compatible = "mstar,msc313e-wdt", },
     93	{ /* sentinel */ }
     94};
     95MODULE_DEVICE_TABLE(of, msc313e_wdt_of_match);
     96
     97static int msc313e_wdt_probe(struct platform_device *pdev)
     98{
     99	struct device *dev = &pdev->dev;
    100	struct msc313e_wdt_priv *priv;
    101
    102	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    103	if (!priv)
    104		return -ENOMEM;
    105
    106	priv->base = devm_platform_ioremap_resource(pdev, 0);
    107	if (IS_ERR(priv->base))
    108		return PTR_ERR(priv->base);
    109
    110	priv->clk = devm_clk_get(dev, NULL);
    111	if (IS_ERR(priv->clk)) {
    112		dev_err(dev, "No input clock\n");
    113		return PTR_ERR(priv->clk);
    114	}
    115
    116	priv->wdev.info = &msc313e_wdt_ident,
    117	priv->wdev.ops = &msc313e_wdt_ops,
    118	priv->wdev.parent = dev;
    119	priv->wdev.min_timeout = MSC313E_WDT_MIN_TIMEOUT;
    120	priv->wdev.max_timeout = U32_MAX / clk_get_rate(priv->clk);
    121	priv->wdev.timeout = MSC313E_WDT_DEFAULT_TIMEOUT;
    122
    123	/* If the period is non-zero the WDT is running */
    124	if (readw(priv->base + REG_WDT_MAX_PRD_L) | (readw(priv->base + REG_WDT_MAX_PRD_H) << 16))
    125		set_bit(WDOG_HW_RUNNING, &priv->wdev.status);
    126
    127	watchdog_set_drvdata(&priv->wdev, priv);
    128
    129	watchdog_init_timeout(&priv->wdev, timeout, dev);
    130	watchdog_stop_on_reboot(&priv->wdev);
    131	watchdog_stop_on_unregister(&priv->wdev);
    132
    133	return devm_watchdog_register_device(dev, &priv->wdev);
    134}
    135
    136static int __maybe_unused msc313e_wdt_suspend(struct device *dev)
    137{
    138	struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
    139
    140	if (watchdog_active(&priv->wdev))
    141		msc313e_wdt_stop(&priv->wdev);
    142
    143	return 0;
    144}
    145
    146static int __maybe_unused msc313e_wdt_resume(struct device *dev)
    147{
    148	struct msc313e_wdt_priv *priv = dev_get_drvdata(dev);
    149
    150	if (watchdog_active(&priv->wdev))
    151		msc313e_wdt_start(&priv->wdev);
    152
    153	return 0;
    154}
    155
    156static SIMPLE_DEV_PM_OPS(msc313e_wdt_pm_ops, msc313e_wdt_suspend, msc313e_wdt_resume);
    157
    158static struct platform_driver msc313e_wdt_driver = {
    159	.driver = {
    160		.name = "msc313e-wdt",
    161		.of_match_table = msc313e_wdt_of_match,
    162		.pm = &msc313e_wdt_pm_ops,
    163	},
    164	.probe = msc313e_wdt_probe,
    165};
    166module_platform_driver(msc313e_wdt_driver);
    167
    168MODULE_AUTHOR("Daniel Palmer <daniel@thingy.jp>");
    169MODULE_DESCRIPTION("Watchdog driver for MStar MSC313e");
    170MODULE_LICENSE("GPL v2");