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

rzn1_wdt.c (5154B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Renesas RZ/N1 Watchdog timer.
      4 * This is a 12-bit timer driver from a (62.5/16384) MHz clock. It can't even
      5 * cope with 2 seconds.
      6 *
      7 * Copyright 2018 Renesas Electronics Europe Ltd.
      8 *
      9 * Derived from Ralink RT288x watchdog timer.
     10 */
     11
     12#include <linux/clk.h>
     13#include <linux/interrupt.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/of_address.h>
     17#include <linux/of_irq.h>
     18#include <linux/platform_device.h>
     19#include <linux/reboot.h>
     20#include <linux/watchdog.h>
     21
     22#define DEFAULT_TIMEOUT		60
     23
     24#define RZN1_WDT_RETRIGGER			0x0
     25#define RZN1_WDT_RETRIGGER_RELOAD_VAL		0
     26#define RZN1_WDT_RETRIGGER_RELOAD_VAL_MASK	0xfff
     27#define RZN1_WDT_RETRIGGER_PRESCALE		BIT(12)
     28#define RZN1_WDT_RETRIGGER_ENABLE		BIT(13)
     29#define RZN1_WDT_RETRIGGER_WDSI			(0x2 << 14)
     30
     31#define RZN1_WDT_PRESCALER			16384
     32#define RZN1_WDT_MAX				4095
     33
     34struct rzn1_watchdog {
     35	struct watchdog_device		wdtdev;
     36	void __iomem			*base;
     37	unsigned long			clk_rate_khz;
     38};
     39
     40static inline uint32_t max_heart_beat_ms(unsigned long clk_rate_khz)
     41{
     42	return (RZN1_WDT_MAX * RZN1_WDT_PRESCALER) / clk_rate_khz;
     43}
     44
     45static inline uint32_t compute_reload_value(uint32_t tick_ms,
     46					    unsigned long clk_rate_khz)
     47{
     48	return (tick_ms * clk_rate_khz) / RZN1_WDT_PRESCALER;
     49}
     50
     51static int rzn1_wdt_ping(struct watchdog_device *w)
     52{
     53	struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
     54
     55	/* Any value retrigggers the watchdog */
     56	writel(0, wdt->base + RZN1_WDT_RETRIGGER);
     57
     58	return 0;
     59}
     60
     61static int rzn1_wdt_start(struct watchdog_device *w)
     62{
     63	struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
     64	u32 val;
     65
     66	/*
     67	 * The hardware allows you to write to this reg only once.
     68	 * Since this includes the reload value, there is no way to change the
     69	 * timeout once started. Also note that the WDT clock is half the bus
     70	 * fabric clock rate, so if the bus fabric clock rate is changed after
     71	 * the WDT is started, the WDT interval will be wrong.
     72	 */
     73	val = RZN1_WDT_RETRIGGER_WDSI;
     74	val |= RZN1_WDT_RETRIGGER_ENABLE;
     75	val |= RZN1_WDT_RETRIGGER_PRESCALE;
     76	val |= compute_reload_value(w->max_hw_heartbeat_ms, wdt->clk_rate_khz);
     77	writel(val, wdt->base + RZN1_WDT_RETRIGGER);
     78
     79	return 0;
     80}
     81
     82static irqreturn_t rzn1_wdt_irq(int irq, void *_wdt)
     83{
     84	pr_crit("RZN1 Watchdog. Initiating system reboot\n");
     85	emergency_restart();
     86
     87	return IRQ_HANDLED;
     88}
     89
     90static struct watchdog_info rzn1_wdt_info = {
     91	.identity = "RZ/N1 Watchdog",
     92	.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
     93};
     94
     95static const struct watchdog_ops rzn1_wdt_ops = {
     96	.owner = THIS_MODULE,
     97	.start = rzn1_wdt_start,
     98	.ping = rzn1_wdt_ping,
     99};
    100
    101static void rzn1_wdt_clk_disable_unprepare(void *data)
    102{
    103	clk_disable_unprepare(data);
    104}
    105
    106static int rzn1_wdt_probe(struct platform_device *pdev)
    107{
    108	struct device *dev = &pdev->dev;
    109	struct rzn1_watchdog *wdt;
    110	struct device_node *np = dev->of_node;
    111	struct clk *clk;
    112	unsigned long clk_rate;
    113	int ret;
    114	int irq;
    115
    116	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
    117	if (!wdt)
    118		return -ENOMEM;
    119
    120	wdt->base = devm_platform_ioremap_resource(pdev, 0);
    121	if (IS_ERR(wdt->base))
    122		return PTR_ERR(wdt->base);
    123
    124	irq = platform_get_irq(pdev, 0);
    125	if (irq < 0)
    126		return irq;
    127
    128	ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0,
    129			       np->name, wdt);
    130	if (ret) {
    131		dev_err(dev, "failed to request irq %d\n", irq);
    132		return ret;
    133	}
    134
    135	clk = devm_clk_get(dev, NULL);
    136	if (IS_ERR(clk)) {
    137		dev_err(dev, "failed to get the clock\n");
    138		return PTR_ERR(clk);
    139	}
    140
    141	ret = clk_prepare_enable(clk);
    142	if (ret) {
    143		dev_err(dev, "failed to prepare/enable the clock\n");
    144		return ret;
    145	}
    146
    147	ret = devm_add_action_or_reset(dev, rzn1_wdt_clk_disable_unprepare,
    148				       clk);
    149	if (ret)
    150		return ret;
    151
    152	clk_rate = clk_get_rate(clk);
    153	if (!clk_rate) {
    154		dev_err(dev, "failed to get the clock rate\n");
    155		return -EINVAL;
    156	}
    157
    158	wdt->clk_rate_khz = clk_rate / 1000;
    159	wdt->wdtdev.info = &rzn1_wdt_info,
    160	wdt->wdtdev.ops = &rzn1_wdt_ops,
    161	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
    162	wdt->wdtdev.parent = dev;
    163	/*
    164	 * The period of the watchdog cannot be changed once set
    165	 * and is limited to a very short period.
    166	 * Configure it for a 1s period once and for all, and
    167	 * rely on the heart-beat provided by the watchdog core
    168	 * to make this usable by the user-space.
    169	 */
    170	wdt->wdtdev.max_hw_heartbeat_ms = max_heart_beat_ms(wdt->clk_rate_khz);
    171	if (wdt->wdtdev.max_hw_heartbeat_ms > 1000)
    172		wdt->wdtdev.max_hw_heartbeat_ms = 1000;
    173
    174	wdt->wdtdev.timeout = DEFAULT_TIMEOUT;
    175	ret = watchdog_init_timeout(&wdt->wdtdev, 0, dev);
    176	if (ret)
    177		return ret;
    178
    179	watchdog_set_drvdata(&wdt->wdtdev, wdt);
    180
    181	return devm_watchdog_register_device(dev, &wdt->wdtdev);
    182}
    183
    184
    185static const struct of_device_id rzn1_wdt_match[] = {
    186	{ .compatible = "renesas,rzn1-wdt" },
    187	{},
    188};
    189MODULE_DEVICE_TABLE(of, rzn1_wdt_match);
    190
    191static struct platform_driver rzn1_wdt_driver = {
    192	.probe		= rzn1_wdt_probe,
    193	.driver		= {
    194		.name		= KBUILD_MODNAME,
    195		.of_match_table	= rzn1_wdt_match,
    196	},
    197};
    198
    199module_platform_driver(rzn1_wdt_driver);
    200
    201MODULE_DESCRIPTION("Renesas RZ/N1 hardware watchdog");
    202MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
    203MODULE_LICENSE("GPL");