diff mbox series

[v2,1/3] watchdog: Add support for K3 RTI watchdog

Message ID 6a9a9f9c875c6cd3e0c1738297f81da150e45bcd.1592910910.git.jan.kiszka@siemens.com
State Accepted
Commit d388f360edacf6292a5ee7a37474799a3fe9f2ac
Headers show
Series watchdog: K3: Add RTI watchdog support | expand

Commit Message

Jan Kiszka June 23, 2020, 11:15 a.m. UTC
From: Jan Kiszka <jan.kiszka at siemens.com>

This is based on the Linux kernel driver for the RTI watchdog.

To actually reset the system on an AM65x, it requires firmware running
on the R5 that accepts the NMI and issues the actual system reset via
TISCI. Kind of an iTCO, except that this watchdog hardware has support
for no-way-out, and only for that.

On the J721E, reset works without extra firmware help when routing the
RTI interrupt via the ESM.

Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
---
 drivers/watchdog/Kconfig   |   7 +++
 drivers/watchdog/Makefile  |   1 +
 drivers/watchdog/rti_wdt.c | 123 +++++++++++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+)
 create mode 100644 drivers/watchdog/rti_wdt.c

Comments

Lokesh Vutla June 23, 2020, 11:47 a.m. UTC | #1
On 23/06/20 4:45 pm, Jan Kiszka wrote:
> From: Jan Kiszka <jan.kiszka at siemens.com>
> 
> This is based on the Linux kernel driver for the RTI watchdog.
> 
> To actually reset the system on an AM65x, it requires firmware running
> on the R5 that accepts the NMI and issues the actual system reset via
> TISCI. Kind of an iTCO, except that this watchdog hardware has support
> for no-way-out, and only for that.
> 
> On the J721E, reset works without extra firmware help when routing the
> RTI interrupt via the ESM.
> 
> Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
> ---
>  drivers/watchdog/Kconfig   |   7 +++
>  drivers/watchdog/Makefile  |   1 +
>  drivers/watchdog/rti_wdt.c | 123 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 131 insertions(+)
>  create mode 100644 drivers/watchdog/rti_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index bf06180cdd..ee99bd2af1 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -155,6 +155,13 @@ config WDT_ORION
>  	  Select this to enable Orion watchdog timer, which can be found on some
>  	  Marvell Armada chips.
>  
> +config WDT_K3_RTI
> +	bool "Texas Instruments K3 RTI watchdog"
> +	depends on WDT && ARCH_K3
> +	help
> +	  Say Y here if you want to include support for the K3 watchdog
> +	  timer (RTI module) available in the K3 generation of processors.
> +
>  config WDT_SANDBOX
>  	bool "Enable Watchdog Timer support for Sandbox"
>  	depends on SANDBOX && WDT
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 519bbd3a40..16bdbf4970 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
>  obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
>  obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
>  obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
> +obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
>  obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
>  obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
>  obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
> diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
> new file mode 100644
> index 0000000000..ebe29c7409
> --- /dev/null
> +++ b/drivers/watchdog/rti_wdt.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (c) Siemens AG, 2020
> + *
> + * Authors:
> + *   Jan Kiszka <jan.kiszka at siemens.com>
> + *
> + * Derived from linux/drivers/watchdog/rti_wdt.c
> + */
> +
> +#include <common.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <power-domain.h>
> +#include <wdt.h>
> +#include <asm/io.h>
> +
> +/* Timer register set definition */
> +#define RTIDWDCTRL		0x90
> +#define RTIDWDPRLD		0x94
> +#define RTIWDSTATUS		0x98
> +#define RTIWDKEY		0x9c
> +#define RTIDWDCNTR		0xa0
> +#define RTIWWDRXCTRL		0xa4
> +#define RTIWWDSIZECTRL		0xa8
> +
> +#define RTIWWDRX_NMI		0xa
> +
> +#define RTIWWDSIZE_50P		0x50
> +
> +#define WDENABLE_KEY		0xa98559da
> +
> +#define WDKEY_SEQ0		0xe51a
> +#define WDKEY_SEQ1		0xa35c
> +
> +#define WDT_PRELOAD_SHIFT	13
> +
> +#define WDT_PRELOAD_MAX		0xfff
> +
> +struct rti_wdt_priv {
> +	phys_addr_t regs;
> +	unsigned int clk_khz;
> +};
> +
> +static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
> +{
> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
> +	u32 timer_margin;
> +	int ret;
> +
> +	if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
> +		return -EBUSY;
> +
> +	timer_margin = timeout_ms * priv->clk_khz / 1000;
> +	timer_margin >>= WDT_PRELOAD_SHIFT;
> +	if (timer_margin > WDT_PRELOAD_MAX)
> +		timer_margin = WDT_PRELOAD_MAX;
> +
> +	writel(timer_margin, priv->regs + RTIDWDPRLD);
> +	writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
> +	writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
> +
> +	readl(priv->regs + RTIWWDSIZECTRL);
> +
> +	writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);

What happens if the watchdog timer expires in U-Boot?

Is the watchdog started from U-Boot command line?

> +
> +	return 0;
> +}
> +
> +static int rti_wdt_reset(struct udevice *dev)
> +{
> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
> +	u32 prld;
> +
> +	/* Make sure we do not reset too early */
> +	prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
> +	if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
> +		return -EPERM;
> +
> +	writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
> +	writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
> +
> +	return 0;
> +}
> +
> +static int rti_wdt_probe(struct udevice *dev)
> +{
> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
> +	struct clk clk;
> +	int ret;
> +
> +	priv->regs = devfdt_get_addr(dev);
> +	if (!priv->regs)
> +		return -EINVAL;
> +
> +	ret = clk_get_by_index(dev, 0, &clk);
> +	if (ret)
> +		return ret;
> +
> +	priv->clk_khz = clk_get_rate(&clk);
> +
> +	return 0;
> +}
> +
> +static const struct wdt_ops rti_wdt_ops = {
> +	.start = rti_wdt_start,
> +	.reset = rti_wdt_reset,
> +};
> +
> +static const struct udevice_id rti_wdt_ids[] = {
> +	{ .compatible = "ti,j7-rti-wdt" },

Is this compatible matching with Kernel's compatible?

Thanks and regards,
Lokesh
Jan Kiszka June 23, 2020, 12:26 p.m. UTC | #2
On 23.06.20 13:47, Lokesh Vutla wrote:
> 
> 
> On 23/06/20 4:45 pm, Jan Kiszka wrote:
>> From: Jan Kiszka <jan.kiszka at siemens.com>
>>
>> This is based on the Linux kernel driver for the RTI watchdog.
>>
>> To actually reset the system on an AM65x, it requires firmware running
>> on the R5 that accepts the NMI and issues the actual system reset via
>> TISCI. Kind of an iTCO, except that this watchdog hardware has support
>> for no-way-out, and only for that.
>>
>> On the J721E, reset works without extra firmware help when routing the
>> RTI interrupt via the ESM.
>>
>> Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
>> ---
>>   drivers/watchdog/Kconfig   |   7 +++
>>   drivers/watchdog/Makefile  |   1 +
>>   drivers/watchdog/rti_wdt.c | 123 +++++++++++++++++++++++++++++++++++++
>>   3 files changed, 131 insertions(+)
>>   create mode 100644 drivers/watchdog/rti_wdt.c
>>
>> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
>> index bf06180cdd..ee99bd2af1 100644
>> --- a/drivers/watchdog/Kconfig
>> +++ b/drivers/watchdog/Kconfig
>> @@ -155,6 +155,13 @@ config WDT_ORION
>>   	  Select this to enable Orion watchdog timer, which can be found on some
>>   	  Marvell Armada chips.
>>   
>> +config WDT_K3_RTI
>> +	bool "Texas Instruments K3 RTI watchdog"
>> +	depends on WDT && ARCH_K3
>> +	help
>> +	  Say Y here if you want to include support for the K3 watchdog
>> +	  timer (RTI module) available in the K3 generation of processors.
>> +
>>   config WDT_SANDBOX
>>   	bool "Enable Watchdog Timer support for Sandbox"
>>   	depends on SANDBOX && WDT
>> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
>> index 519bbd3a40..16bdbf4970 100644
>> --- a/drivers/watchdog/Makefile
>> +++ b/drivers/watchdog/Makefile
>> @@ -27,6 +27,7 @@ obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
>>   obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
>>   obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
>>   obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
>> +obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
>>   obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
>>   obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
>>   obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
>> diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
>> new file mode 100644
>> index 0000000000..ebe29c7409
>> --- /dev/null
>> +++ b/drivers/watchdog/rti_wdt.c
>> @@ -0,0 +1,123 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright (c) Siemens AG, 2020
>> + *
>> + * Authors:
>> + *   Jan Kiszka <jan.kiszka at siemens.com>
>> + *
>> + * Derived from linux/drivers/watchdog/rti_wdt.c
>> + */
>> +
>> +#include <common.h>
>> +#include <clk.h>
>> +#include <dm.h>
>> +#include <power-domain.h>
>> +#include <wdt.h>
>> +#include <asm/io.h>
>> +
>> +/* Timer register set definition */
>> +#define RTIDWDCTRL		0x90
>> +#define RTIDWDPRLD		0x94
>> +#define RTIWDSTATUS		0x98
>> +#define RTIWDKEY		0x9c
>> +#define RTIDWDCNTR		0xa0
>> +#define RTIWWDRXCTRL		0xa4
>> +#define RTIWWDSIZECTRL		0xa8
>> +
>> +#define RTIWWDRX_NMI		0xa
>> +
>> +#define RTIWWDSIZE_50P		0x50
>> +
>> +#define WDENABLE_KEY		0xa98559da
>> +
>> +#define WDKEY_SEQ0		0xe51a
>> +#define WDKEY_SEQ1		0xa35c
>> +
>> +#define WDT_PRELOAD_SHIFT	13
>> +
>> +#define WDT_PRELOAD_MAX		0xfff
>> +
>> +struct rti_wdt_priv {
>> +	phys_addr_t regs;
>> +	unsigned int clk_khz;
>> +};
>> +
>> +static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
>> +{
>> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
>> +	u32 timer_margin;
>> +	int ret;
>> +
>> +	if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
>> +		return -EBUSY;
>> +
>> +	timer_margin = timeout_ms * priv->clk_khz / 1000;
>> +	timer_margin >>= WDT_PRELOAD_SHIFT;
>> +	if (timer_margin > WDT_PRELOAD_MAX)
>> +		timer_margin = WDT_PRELOAD_MAX;
>> +
>> +	writel(timer_margin, priv->regs + RTIDWDPRLD);
>> +	writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
>> +	writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
>> +
>> +	readl(priv->regs + RTIWWDSIZECTRL);
>> +
>> +	writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);
> 
> What happens if the watchdog timer expires in U-Boot?
> 

Then we reset. You can feed the watchdog also from U-Boot, just enable 
CONFIG_WATCHDOG.

> Is the watchdog started from U-Boot command line?
> 

U-Boot automatically starts the first watchdog it finds. I didn't find a 
switch to disable that. If you have multiple watchdogs, you can start 
others also via "wdt".

>> +
>> +	return 0;
>> +}
>> +
>> +static int rti_wdt_reset(struct udevice *dev)
>> +{
>> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
>> +	u32 prld;
>> +
>> +	/* Make sure we do not reset too early */
>> +	prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
>> +	if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
>> +		return -EPERM;
>> +
>> +	writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
>> +	writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rti_wdt_probe(struct udevice *dev)
>> +{
>> +	struct rti_wdt_priv *priv = dev_get_priv(dev);
>> +	struct clk clk;
>> +	int ret;
>> +
>> +	priv->regs = devfdt_get_addr(dev);
>> +	if (!priv->regs)
>> +		return -EINVAL;
>> +
>> +	ret = clk_get_by_index(dev, 0, &clk);
>> +	if (ret)
>> +		return ret;
>> +
>> +	priv->clk_khz = clk_get_rate(&clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct wdt_ops rti_wdt_ops = {
>> +	.start = rti_wdt_start,
>> +	.reset = rti_wdt_reset,
>> +};
>> +
>> +static const struct udevice_id rti_wdt_ids[] = {
>> +	{ .compatible = "ti,j7-rti-wdt" },
> 
> Is this compatible matching with Kernel's compatible?
> 

Yes.

Jan
diff mbox series

Patch

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index bf06180cdd..ee99bd2af1 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -155,6 +155,13 @@  config WDT_ORION
 	  Select this to enable Orion watchdog timer, which can be found on some
 	  Marvell Armada chips.
 
+config WDT_K3_RTI
+	bool "Texas Instruments K3 RTI watchdog"
+	depends on WDT && ARCH_K3
+	help
+	  Say Y here if you want to include support for the K3 watchdog
+	  timer (RTI module) available in the K3 generation of processors.
+
 config WDT_SANDBOX
 	bool "Enable Watchdog Timer support for Sandbox"
 	depends on SANDBOX && WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 519bbd3a40..16bdbf4970 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -27,6 +27,7 @@  obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o
 obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o
 obj-$(CONFIG_WDT_MTK) += mtk_wdt.o
 obj-$(CONFIG_WDT_OMAP3) += omap_wdt.o
+obj-$(CONFIG_WDT_K3_RTI) += rti_wdt.o
 obj-$(CONFIG_WDT_SP805) += sp805_wdt.o
 obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
 obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c
new file mode 100644
index 0000000000..ebe29c7409
--- /dev/null
+++ b/drivers/watchdog/rti_wdt.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) Siemens AG, 2020
+ *
+ * Authors:
+ *   Jan Kiszka <jan.kiszka at siemens.com>
+ *
+ * Derived from linux/drivers/watchdog/rti_wdt.c
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <power-domain.h>
+#include <wdt.h>
+#include <asm/io.h>
+
+/* Timer register set definition */
+#define RTIDWDCTRL		0x90
+#define RTIDWDPRLD		0x94
+#define RTIWDSTATUS		0x98
+#define RTIWDKEY		0x9c
+#define RTIDWDCNTR		0xa0
+#define RTIWWDRXCTRL		0xa4
+#define RTIWWDSIZECTRL		0xa8
+
+#define RTIWWDRX_NMI		0xa
+
+#define RTIWWDSIZE_50P		0x50
+
+#define WDENABLE_KEY		0xa98559da
+
+#define WDKEY_SEQ0		0xe51a
+#define WDKEY_SEQ1		0xa35c
+
+#define WDT_PRELOAD_SHIFT	13
+
+#define WDT_PRELOAD_MAX		0xfff
+
+struct rti_wdt_priv {
+	phys_addr_t regs;
+	unsigned int clk_khz;
+};
+
+static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
+{
+	struct rti_wdt_priv *priv = dev_get_priv(dev);
+	u32 timer_margin;
+	int ret;
+
+	if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
+		return -EBUSY;
+
+	timer_margin = timeout_ms * priv->clk_khz / 1000;
+	timer_margin >>= WDT_PRELOAD_SHIFT;
+	if (timer_margin > WDT_PRELOAD_MAX)
+		timer_margin = WDT_PRELOAD_MAX;
+
+	writel(timer_margin, priv->regs + RTIDWDPRLD);
+	writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
+	writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
+
+	readl(priv->regs + RTIWWDSIZECTRL);
+
+	writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);
+
+	return 0;
+}
+
+static int rti_wdt_reset(struct udevice *dev)
+{
+	struct rti_wdt_priv *priv = dev_get_priv(dev);
+	u32 prld;
+
+	/* Make sure we do not reset too early */
+	prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
+	if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
+		return -EPERM;
+
+	writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
+	writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
+
+	return 0;
+}
+
+static int rti_wdt_probe(struct udevice *dev)
+{
+	struct rti_wdt_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	int ret;
+
+	priv->regs = devfdt_get_addr(dev);
+	if (!priv->regs)
+		return -EINVAL;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+
+	priv->clk_khz = clk_get_rate(&clk);
+
+	return 0;
+}
+
+static const struct wdt_ops rti_wdt_ops = {
+	.start = rti_wdt_start,
+	.reset = rti_wdt_reset,
+};
+
+static const struct udevice_id rti_wdt_ids[] = {
+	{ .compatible = "ti,j7-rti-wdt" },
+	{ }
+};
+
+U_BOOT_DRIVER(rti_wdt) = {
+	.name = "rti_wdt",
+	.id = UCLASS_WDT,
+	.of_match = rti_wdt_ids,
+	.ops = &rti_wdt_ops,
+	.probe = rti_wdt_probe,
+	.priv_auto_alloc_size = sizeof(struct rti_wdt_priv),
+	.flags = DM_FLAG_REMOVE_WITH_PD_ON,
+};