brintos

brintos / linux-shallow public Read only

0
0
Text · 4.7 KiB · 7d3192d Raw
188 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Renesas RZ/N1 Watchdog timer.4 * This is a 12-bit timer driver from a (62.5/16384) MHz clock. It can't even5 * 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		6023 24#define RZN1_WDT_RETRIGGER			0x025#define RZN1_WDT_RETRIGGER_RELOAD_VAL		026#define RZN1_WDT_RETRIGGER_RELOAD_VAL_MASK	0xfff27#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			1638432#define RZN1_WDT_MAX				409533 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 the69	 * timeout once started. Also note that the WDT clock is half the bus70	 * fabric clock rate, so if the bus fabric clock rate is changed after71	 * 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 int rzn1_wdt_probe(struct platform_device *pdev)102{103	struct device *dev = &pdev->dev;104	struct rzn1_watchdog *wdt;105	struct device_node *np = dev->of_node;106	struct clk *clk;107	unsigned long clk_rate;108	int ret;109	int irq;110 111	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);112	if (!wdt)113		return -ENOMEM;114 115	wdt->base = devm_platform_ioremap_resource(pdev, 0);116	if (IS_ERR(wdt->base))117		return PTR_ERR(wdt->base);118 119	irq = platform_get_irq(pdev, 0);120	if (irq < 0)121		return irq;122 123	ret = devm_request_irq(dev, irq, rzn1_wdt_irq, 0,124			       np->name, wdt);125	if (ret) {126		dev_err(dev, "failed to request irq %d\n", irq);127		return ret;128	}129 130	clk = devm_clk_get_enabled(dev, NULL);131	if (IS_ERR(clk)) {132		dev_err(dev, "failed to get the clock\n");133		return PTR_ERR(clk);134	}135 136	clk_rate = clk_get_rate(clk);137	if (!clk_rate) {138		dev_err(dev, "failed to get the clock rate\n");139		return -EINVAL;140	}141 142	wdt->clk_rate_khz = clk_rate / 1000;143	wdt->wdtdev.info = &rzn1_wdt_info;144	wdt->wdtdev.ops = &rzn1_wdt_ops;145	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;146	wdt->wdtdev.parent = dev;147	/*148	 * The period of the watchdog cannot be changed once set149	 * and is limited to a very short period.150	 * Configure it for a 1s period once and for all, and151	 * rely on the heart-beat provided by the watchdog core152	 * to make this usable by the user-space.153	 */154	wdt->wdtdev.max_hw_heartbeat_ms = max_heart_beat_ms(wdt->clk_rate_khz);155	if (wdt->wdtdev.max_hw_heartbeat_ms > 1000)156		wdt->wdtdev.max_hw_heartbeat_ms = 1000;157 158	wdt->wdtdev.timeout = DEFAULT_TIMEOUT;159	ret = watchdog_init_timeout(&wdt->wdtdev, 0, dev);160	if (ret)161		return ret;162 163	watchdog_set_drvdata(&wdt->wdtdev, wdt);164 165	return devm_watchdog_register_device(dev, &wdt->wdtdev);166}167 168 169static const struct of_device_id rzn1_wdt_match[] = {170	{ .compatible = "renesas,rzn1-wdt" },171	{},172};173MODULE_DEVICE_TABLE(of, rzn1_wdt_match);174 175static struct platform_driver rzn1_wdt_driver = {176	.probe		= rzn1_wdt_probe,177	.driver		= {178		.name		= KBUILD_MODNAME,179		.of_match_table	= rzn1_wdt_match,180	},181};182 183module_platform_driver(rzn1_wdt_driver);184 185MODULE_DESCRIPTION("Renesas RZ/N1 hardware watchdog");186MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");187MODULE_LICENSE("GPL");188