From: Fu Wei fu.wei@linaro.org
Signed-off-by: Fu Wei fu.wei@linaro.org --- drivers/watchdog/Kconfig | 10 + drivers/watchdog/Makefile | 1 + drivers/watchdog/sbsa_gwdt.c | 578 +++++++++++++++++++++++++++++++++++++++++++ drivers/watchdog/sbsa_gwdt.h | 99 ++++++++ 4 files changed, 688 insertions(+) create mode 100644 drivers/watchdog/sbsa_gwdt.c create mode 100644 drivers/watchdog/sbsa_gwdt.h
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e5e7c55..46d6f46 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -152,6 +152,16 @@ config ARM_SP805_WATCHDOG ARM Primecell SP805 Watchdog timer. This will reboot your system when the timeout is reached.
+config ARM_SBSA_WATCHDOG + tristate "ARM SBSA Generic Watchdog" + depends on ARM64 && ARM_AMBA + select WATCHDOG_CORE + help + ARM SBSA Generic Watchdog timer. This has two Watchdog Signal(WS0/WS1), + will trigger a warnning interrupt(do panic) in the first timeout(WS0); + will reboot your system when the second timeout(WS1) is reached. + More details: DEN0029B - Server Base System Architecture (SBSA) + config AT91RM9200_WATCHDOG tristate "AT91RM9200 watchdog" depends on SOC_AT91RM9200 && MFD_SYSCON diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5c19294..471f1b7c 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o +obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c new file mode 100644 index 0000000..feee069 --- /dev/null +++ b/drivers/watchdog/sbsa_gwdt.c @@ -0,0 +1,578 @@ +/* + * SBSA(Server Base System Architecture) Generic Watchdog driver + * + * Copyright (c) 2015, Linaro Ltd. + * Author: Fu Wei fu.wei@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Note: This SBSA Generic watchdog driver is compatible with + * the pretimeout concept of Linux kernel. + * But timeout and pretimeout are set by the different REGs. + * The first watch period is set by writing WCV directly, + * that can support more than 10s timeout with the system counter + * at 400MHz. + * And the second watch period is set by WOR which will be loaded + * automatically by hardware, when WS0 is triggered. + * More details: DEN0029B - Server Base System Architecture (SBSA) + * + * Kernel/API: P---------| pretimeout + * |-------------------------------T timeout + * SBSA GWDT: P--WOR---WS1 pretimeout + * |-------WCV----------WS0~~~~~~~~T timeout + */ + +#include <asm/arch_timer.h> + +#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> + +#include "sbsa_gwdt.h" + +/* For the second watch period, + * the timeout value comes from WOR(a 32bit wide. register). + * This gives a maximum watch period of around 10s + * at a maximum system counter frequency. + * + * The System Counter shall run at maximum of 400MHz. + * (Server Base System Architecture ARM-DEN-0029 Version 2.3) + */ +#define DEFAULT_TIMEOUT 15 /* seconds, the 1st + 2nd watch period*/ +#define DEFAULT_PRETIMEOUT 5 /* seconds, the 2nd watch period*/ + +/*store the System Counter clock frequency, in Hz.*/ +static u32 sbsa_gwdt_rate; + +static unsigned int timeout = 0; +module_param(timeout, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_TIMEOUT) ")"); + +static unsigned int max_timeout = UINT_MAX; +module_param(max_timeout, uint, S_IRUGO); +MODULE_PARM_DESC(max_timeout, + "Watchdog max timeout in seconds. (>=0, default=" + __MODULE_STRING(UINT_MAX) ")"); + +static unsigned int pretimeout = 0; +module_param(pretimeout, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(pretimeout, + "Watchdog pretimeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_PRETIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, S_IRUGO); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +/* + * Architected system timer support. + */ +static void sbsa_gwdt_cf_write(unsigned int reg, u32 val, + struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + writel_relaxed(val, device_data->control_base + reg); +} + +static void sbsa_gwdt_rf_write(unsigned int reg, u32 val, + struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + writel_relaxed(val, device_data->refresh_base + reg); +} + +static u32 sbsa_gwdt_cf_read(unsigned int reg, struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + return readl_relaxed(device_data->control_base + reg); +} + +static u32 sbsa_gwdt_rf_read(unsigned int reg, struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + return readl_relaxed(device_data->refresh_base + reg); +} + +/* + * help founctions for accessing 64bit WCV register + * mutex_lock must be called prior to calling this function. + */ +static u64 sbsa_gwdt_get_wcv(struct watchdog_device *wdd) +{ + u32 wcv_lo, wcv_hi; + + wcv_lo = sbsa_gwdt_cf_read(SBSA_GWDT_WCV_LO, wdd); + wcv_hi = sbsa_gwdt_cf_read(SBSA_GWDT_WCV_HI, wdd); + + return (((u64)wcv_hi << 32) | wcv_lo); +} + +void sbsa_gwdt_set_wcv(struct watchdog_device *wdd, u64 value) +{ + u32 wcv_lo, wcv_hi; + wcv_lo = (u32)(value & U32_MAX); + wcv_hi = (u32)((value >> 32) & U32_MAX); + + sbsa_gwdt_cf_write(SBSA_GWDT_WCV_LO, wcv_lo, wdd); + sbsa_gwdt_cf_write(SBSA_GWDT_WCV_HI, wcv_hi, wdd); + + if (sbsa_gwdt_get_wcv(wdd) != value) + pr_err("sbsa_gwdt: set wcv fail!\n"); + + return; +} + +void reload_timeout_to_wcv(struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + u64 cntvct, wcv; + /* refresh the wcv */ + cntvct = arch_counter_get_cntvct(); + wcv = cntvct + (wdd->timeout - gwdt->pretimeout) * sbsa_gwdt_rate; + sbsa_gwdt_set_wcv(wdd, wcv); + + return; +} +/* Use the following function to set the limit of timeout + * after updating pretimeout */ +void sbsa_gwdt_set_timeout_limits(struct sbsa_gwdt *gwdt) +{ + struct watchdog_device *wdd = &gwdt->wdd; + unsigned int first_period_max = (U64_MAX / sbsa_gwdt_rate); + + wdd->min_timeout = gwdt->pretimeout + 1; + wdd->max_timeout = min((gwdt->pretimeout + first_period_max), + max_timeout); + + return; +} + +/* Use the following function to check if a pretimeout value is invalid */ +static inline bool watchdog_pretimeout_invalid(struct sbsa_gwdt *gwdt, + unsigned int t) +{ + return ((gwdt->max_pretimeout != 0) && (t > gwdt->max_pretimeout)); +} + +static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + if (watchdog_timeout_invalid(wdd, timeout)) { + pr_err("sbsa_gwdt: timeout %d is out of range(%d~%d), unchanged\n", + timeout, wdd->min_timeout, wdd->max_timeout); + return -EINVAL; + } + wdd->timeout = timeout; + /* refresh the wcv */ + reload_timeout_to_wcv(wdd); + + return 0; +} + +static int sbsa_gwdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + u32 wor; + + if (watchdog_pretimeout_invalid(gwdt, pretimeout)) { + pr_err("sbsa_gwdt: pretimeout %d is out of range(0~%d),skip\n", + pretimeout, gwdt->max_pretimeout); + return -EINVAL; + } + gwdt->pretimeout = pretimeout; + sbsa_gwdt_set_timeout_limits(gwdt); + + /* refresh the WOR, that will cause an explicit watchdog refresh */ + wor = pretimeout * sbsa_gwdt_rate; + sbsa_gwdt_cf_write(SBSA_GWDT_WOR, wor, wdd); + + return 0; +} + +static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd) +{ + u64 timeleft = sbsa_gwdt_get_wcv(wdd) - arch_counter_get_cntvct(); + + return (unsigned int)(timeleft / sbsa_gwdt_rate); +} + +static int sbsa_gwdt_start(struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + u32 status; + int ret; + + ret = sbsa_gwdt_set_timeout(wdd, wdd->timeout); + if (ret) + return ret; + + mutex_lock(&device_data->lock); + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + status |= SBSA_GWDT_WCS_EN; + /* writing WCS will cause an explicit watchdog refresh */ + sbsa_gwdt_cf_write(SBSA_GWDT_WCS, status, wdd); + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + mutex_unlock(&device_data->lock); + + /* Check if the watchdog was enabled */ + if (!(status & SBSA_GWDT_WCS_EN)) + return -EACCES; + + return 0; +} + +static int sbsa_gwdt_stop(struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + u32 status; + + mutex_lock(&device_data->lock); + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + status &= ~SBSA_GWDT_WCS_EN; + /* writing WCS will cause an explicit watchdog refresh */ + sbsa_gwdt_cf_write(SBSA_GWDT_WCS, status, wdd); + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + mutex_unlock(&device_data->lock); + + /* Check if the watchdog was disabled */ + if (status & SBSA_GWDT_WCS_EN) + return -EACCES; + + return 0; +} + +static int sbsa_gwdt_keepalive(struct watchdog_device *wdd) +{ + struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + + mutex_lock(&device_data->lock); + /* Writing WRR for an explicit watchdog refresh + * You can write anyting(like 0xc0ffee) + */ + sbsa_gwdt_rf_write(SBSA_GWDT_WRR, 0xc0ffee, wdd); + mutex_unlock(&device_data->lock); + + reload_timeout_to_wcv(wdd); + return 0; +} + +static long sbsa_gwdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, + unsigned long arg) +{ + struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd); + void __user *argp = (void __user *)arg; + int ret = -ENOIOCTLCMD; + unsigned int __user *p = argp; + unsigned int new_value; + + /* FIXME: what else do we need here?? */ + switch (cmd) { + case WDIOC_SETPRETIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + ret = sbsa_gwdt_set_pretimeout(wdd, new_value); + break; + case WDIOC_GETPRETIMEOUT: + ret = put_user(gwdt->pretimeout, (unsigned int __user *)arg); + break; + } + + return ret; +} + +static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id) +{ + struct sbsa_gwdt *gwdt = (struct sbsa_gwdt *)dev_id; + struct watchdog_device *wdd = &gwdt->wdd; + u32 status; + unsigned int left; + + pr_debug("SBSA Watchdog: in %s\n", __func__); + + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + + if (status & SBSA_GWDT_WCS_WS0) { + pr_crit("SBSA Watchdog WS0 is triggered\n"); + left = sbsa_gwdt_get_timeleft(wdd); + pr_crit("please try to feed the dog in %ds!\n", left); + + /* FIXME:anything we can do here? */ + panic("SBSA Watchdog pre-timeout"); + } + + return IRQ_HANDLED; +} + +static struct watchdog_info sbsa_gwdt_info = { + .identity = "SBSA Generic Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT | + WDIOF_CARDRESET, +}; + +static struct watchdog_ops sbsa_gwdt_ops = { + .owner = THIS_MODULE, + .start = sbsa_gwdt_start, + .stop = sbsa_gwdt_stop, + .ping = sbsa_gwdt_keepalive, + .set_timeout = sbsa_gwdt_set_timeout, + .ioctl = sbsa_gwdt_ioctl, + .get_timeleft = sbsa_gwdt_get_timeleft, +}; + + +static int sbsa_gwdt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + struct sbsa_gwdt *gwdt; + struct sbsa_gwdt_device_data *device_data; + + struct watchdog_device *wdd; + + struct resource *res; + void *rf_base, *cf_base; + int irq; + u32 status, flags; + u32 w_iidr, idr; + + int ret = 0; + + /* + * Try to determine the frequency from the cp15 interface + */ + sbsa_gwdt_rate = arch_timer_get_cntfrq(); + /* Check the system counter frequency. */ + if (!sbsa_gwdt_rate) + dev_err(dev, "System Counter frequency not available\n"); + + gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); + if (!gwdt) + return -ENOMEM; + + wdd = &gwdt->wdd; + wdd->parent = dev; + device_data = &gwdt->device_data; + mutex_init(&device_data->lock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "refresh_frame"); + rf_base = devm_ioremap_resource(dev, res); + if (IS_ERR(rf_base)) + return PTR_ERR(rf_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "control_frame"); + cf_base = devm_ioremap_resource(dev, res); + if (IS_ERR(rf_base)) + return PTR_ERR(rf_base); + + irq = platform_get_irq_byname(pdev, "ws0"); + if (IS_ERR(irq)) { + dev_err(dev, "required ws0 interrupt missing\n"); + return PTR_ERR(irq); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ws0"); + if (!res) + dev_warn(dev, "ws0 interrupt flags missing, ignore\n"); + else + flags = res->flags; + + device_data->refresh_base = rf_base; + device_data->control_base = cf_base; + device_data->irq = irq; + device_data->flags = flags; + + ret = devm_request_irq(dev, irq, sbsa_gwdt_interrupt, + IRQF_TIMER, pdev->name, gwdt); + if (ret) { + dev_err(dev, "unable to request IRQ %d, disabling device\n", + irq); + return ret; + } + + /* get device information from refresh/control frame */ + w_iidr = sbsa_gwdt_rf_read(SBSA_GWDT_W_IIDR, &gwdt->wdd); + idr = sbsa_gwdt_rf_read(SBSA_GWDT_IDR, &gwdt->wdd); + dev_info(dev, "W_IIDR %x IDR %x in RF.\n", w_iidr, idr); + + /* update device information to device_data for future use*/ + device_data->info.pid = SBSA_GWDT_W_IIDR_PID(w_iidr); + device_data->info.arch_ver = SBSA_GWDT_W_IIDR_ARCH_VER(w_iidr); + device_data->info.revision = SBSA_GWDT_W_IIDR_REV(w_iidr); + device_data->info.vid_bank = SBSA_GWDT_W_IIDR_VID_BANK(w_iidr); + device_data->info.vid = SBSA_GWDT_W_IIDR_VID(w_iidr); + + wdd->info = &sbsa_gwdt_info; + wdd->ops = &sbsa_gwdt_ops; + + watchdog_set_drvdata(wdd, gwdt); + + status = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + + if (status & SBSA_GWDT_WCS_WS1) { + dev_warn(dev, "System was reseted by SBSA Watchdog, WCS %x\n", + status); + wdd->bootstatus |= WDIOF_CARDRESET; + gwdt->wcv_dump = sbsa_gwdt_get_wcv(wdd); + } + + watchdog_set_nowayout(wdd, nowayout); + + gwdt->max_pretimeout = min((U32_MAX / sbsa_gwdt_rate), + (max_timeout - 1)); + + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + u32 timeouts[2]; + ret = of_property_read_u32_array(pdev->dev.of_node, + "timeout-sec", timeouts, 2); + if (ret && timeout && pretimeout) { + timeout = timeouts[0]; + pretimeout = timeouts[1]; + } + } + if ((!timeout) || (!pretimeout)) { + dev_info(dev, "get timeouts from DT or param fail." + "Use DEFAULT.\n"); + timeout = DEFAULT_TIMEOUT; + pretimeout = DEFAULT_PRETIMEOUT; + } + + sbsa_gwdt_set_pretimeout(wdd, pretimeout); + sbsa_gwdt_set_timeout(wdd, timeout); + + platform_set_drvdata(pdev, gwdt); + ret = watchdog_register_device(wdd); + if (ret) + return ret; + + /* Check if watchdog is already enabled */ + if (status & SBSA_GWDT_WCS_EN) { + dev_warn(dev, "already enabled!\n"); + sbsa_gwdt_keepalive(wdd); + } + + dev_info(dev, "registered with %ds timeout, %ds pretimeout\n", + wdd->timeout, gwdt->pretimeout); + + return 0; +} + +static void sbsa_gwdt_shutdown(struct platform_device *pdev) +{ + struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev); + + sbsa_gwdt_stop(&gwdt->wdd); +} + +static int sbsa_gwdt_remove(struct platform_device *pdev) +{ + struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev); + struct watchdog_device *wdd = &gwdt->wdd; + int ret = 0; + + if (!nowayout) + ret = sbsa_gwdt_stop(wdd); + watchdog_unregister_device(wdd); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +/* Disable watchdog if it is active during suspend */ +static int sbsa_gwdt_suspend(struct device *dev) +{ + struct sbsa_gwdt *gwdt = dev_get_drvdata(dev); + struct sbsa_gwdt_device_data *device_data = &gwdt->device_data; + struct watchdog_device *wdd = &gwdt->wdd; + + mutex_lock(&device_data->lock); + gwdt->pm_status_store = sbsa_gwdt_cf_read(SBSA_GWDT_WCS, wdd); + mutex_unlock(&device_data->lock); + + if (gwdt->pm_status_store & SBSA_GWDT_WCS_EN) + return sbsa_gwdt_stop(wdd); + + return 0; +} + +/* Enable watchdog and configure it if necessary */ +static int sbsa_gwdt_resume(struct device *dev) +{ + struct sbsa_gwdt *gwdt = dev_get_drvdata(dev); + struct watchdog_device *wdd = &gwdt->wdd; + + /* + * If watchdog was stopped before suspend be sure it gets disabled + * again, for the case BIOS has enabled it during resume + */ + if (gwdt->pm_status_store & SBSA_GWDT_WCS_EN) + return sbsa_gwdt_start(wdd); + else + return sbsa_gwdt_stop(wdd); +} +#endif + +#if IS_BUILTIN(CONFIG_OF) +static const struct of_device_id sbsa_gwdt_of_match[] = { + { .compatible = "arm,sbsa-gwdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sbsa_gwdt_of_match); +#endif + +static struct dev_pm_ops sbsa_gwdt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume) +}; + +static struct platform_driver sbsa_gwdt_driver = { + .driver = { + .name = "sbsa-gwdt", + .pm = &sbsa_gwdt_pm_ops, + .of_match_table = sbsa_gwdt_of_match, + }, + .probe = sbsa_gwdt_probe, + .remove = sbsa_gwdt_remove, + .shutdown = sbsa_gwdt_shutdown, +}; + +module_platform_driver(sbsa_gwdt_driver); + +MODULE_DESCRIPTION("SBSA Generic Watchdog Driver"); +MODULE_AUTHOR("Fu Wei fu.wei@linaro.org"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/watchdog/sbsa_gwdt.h b/drivers/watchdog/sbsa_gwdt.h new file mode 100644 index 0000000..5dc8ace --- /dev/null +++ b/drivers/watchdog/sbsa_gwdt.h @@ -0,0 +1,99 @@ +/* + * SBSA(Server Base System Architecture) Generic Watchdog driver definitions + * + * Copyright (c) 2015, Linaro Ltd. + * Author: Fu Wei fu.wei@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + */ + +#ifndef _SBSA_GENERIC_WATCHDOG_H_ +#define _SBSA_GENERIC_WATCHDOG_H_ + +/* SBSA Generic Watchdog register definitions */ +/* refresh frame */ +#define SBSA_GWDT_WRR (0x000) + +/* control frame */ +#define SBSA_GWDT_WCS (0x000) +#define SBSA_GWDT_WOR (0x008) +#define SBSA_GWDT_WCV_LO (0x010) +#define SBSA_GWDT_WCV_HI (0x014) + +/* refresh/control frame */ +#define SBSA_GWDT_W_IIDR (0xfcc) +#define SBSA_GWDT_IDR (0xfd0) + +/* Watchdog Control and Status Register */ +#define SBSA_GWDT_WCS_EN (1 << 0) +#define SBSA_GWDT_WCS_WS0 (1 << 1) +#define SBSA_GWDT_WCS_WS1 (1 << 2) + +/* Watchdog Interface Identification Register */ +#define SBSA_GWDT_W_IIDR_PID(x) ((x >> 20) & 0xfff) +#define SBSA_GWDT_W_IIDR_ARCH_VER(x) ((x >> 16) & 0xf) +#define SBSA_GWDT_W_IIDR_REV(x) ((x >> 12) & 0xf) +#define SBSA_GWDT_W_IIDR_IMPLEMENTER(x) (x & 0xfff) +#define SBSA_GWDT_W_IIDR_VID_BANK(x) ((x >> 8) & 0xf) +#define SBSA_GWDT_W_IIDR_VID(x) (x & 0x7f) + +/* Watchdog Identification Register */ +#define SBSA_GWDT_IDR_W_PIDR2 (0xfe8) +#define SBSA_GWDT_IDR_W_PIDR2_ARCH_VER(x) ((x >> 4) & 0xf) + +/** + * struct sbsa_gwdt_info - SBSA Generic Watchdog device hardware information + * @pid: GWDT ProductID + * @arch_ver: GWDT architecture version + * @revision: GWDT revision number + * @vid_bank: GWDT The JEP106 continuation code of the implementer + * @vid: GWDT The JEP106 identity code of the implementer + * @refresh_pa: the watchdog refresh frame(PA) + * @control_pa: the watchdog control frame(PA) + */ +struct sbsa_gwdt_info { + unsigned int pid; + unsigned int arch_ver; + unsigned int revision; + unsigned int vid_bank; + unsigned int vid; + void *refresh_pa; + void *control_pa; +}; + +/** + * struct sbsa_gwdt_device_data - Internal representation of the SBSA GWDT + * @refresh_base: VA of the watchdog refresh frame + * @control_base: VA of the watchdog control frame + * @irq: Watchdog Timer GSIV (WS0) + * @flags: Watchdog Timer Flags + * @dev: Pointer to kernel device structure + * @info: SBSA Generic Watchdog info structure + * @lock: GWDT mutex + */ +struct sbsa_gwdt_device_data { + void __iomem *refresh_base; + void __iomem *control_base; + int irq; + u32 flags; + struct device *dev; + struct sbsa_gwdt_info info; + struct mutex lock; +}; + +struct sbsa_gwdt { + struct sbsa_gwdt_device_data device_data; + struct watchdog_device wdd; + unsigned int pretimeout; /* seconds */ + unsigned int max_pretimeout; + u64 wcv_dump; +#ifdef CONFIG_PM_SLEEP + u8 pm_status_store; +#endif +}; + +#define to_sbsa_gwdt(e) container_of(e, struct sbsa_gwdt, wdd) + +#endif /* _SBSA_GENERIC_WATCHDOG_H_ */