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