2017-08-28 11:35:38

by Andreas Färber

[permalink] [raw]
Subject: [PATCH 0/3] arm64: Realtek RTD1295 watchdog

Hello,

This series adds the watchdog for the Realtek RTD1295 SoC.

There being no public source code for RTD1295, the implementation is based on
register offsets seen in the vendor DT, as well as older mach-rtk119x code
published by QNAP.

The DT node depends on my RTD1295 clk series.

More experimental patches at:
https://github.com/afaerber/linux/commits/rtd1295-next

Have a lot of fun!

Cheers,
Andreas

Cc: Wim Van Sebroeck <[email protected]>
Cc: Guenter Roeck <[email protected]>
Cc: [email protected]
Cc: Roc He <[email protected]>
Cc: 蒋丽琴 <[email protected]>
Cc: [email protected]

Andreas Färber (3):
dt-bindings: watchdog: Add Realtek RTD1295
watchdog: Add Realtek RTD1295
arm64: dts: realtek: Add watchdog node for RTD1295

.../bindings/watchdog/realtek,rtd119x.txt | 17 +++
arch/arm64/boot/dts/realtek/rtd1295.dtsi | 6 +
drivers/watchdog/Kconfig | 10 ++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/rtd119x_wdt.c | 152 +++++++++++++++++++++
5 files changed, 186 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt
create mode 100644 drivers/watchdog/rtd119x_wdt.c

--
2.12.3


2017-08-28 11:35:41

by Andreas Färber

[permalink] [raw]
Subject: [PATCH 1/3] dt-bindings: watchdog: Add Realtek RTD1295

Add a binding for the Realtek RTD1295 watchdog.

Signed-off-by: Andreas Färber <[email protected]>
---
.../devicetree/bindings/watchdog/realtek,rtd119x.txt | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt

diff --git a/Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt b/Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt
new file mode 100644
index 000000000000..05653054bd5b
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt
@@ -0,0 +1,17 @@
+Realtek RTD1295 Watchdog
+========================
+
+Required properties:
+
+- compatible : Should be "realtek,rtd1295-watchdog"
+- reg : Specifies the physical base address and size of registers
+- clocks : Specifies one clock input
+
+
+Example:
+
+ watchdog@98007680 {
+ compatible = "realtek,rtd1295-watchdog";
+ reg = <0x98007680 0x100>;
+ clocks = <&osc27M>;
+ };
--
2.12.3

2017-08-28 11:35:43

by Andreas Färber

[permalink] [raw]
Subject: [PATCH 3/3] arm64: dts: realtek: Add watchdog node for RTD1295

Add the watchdog node to the RTD1295 Device Tree.

Signed-off-by: Andreas Färber <[email protected]>
---
Depends on the pending clock nodes patch.

arch/arm64/boot/dts/realtek/rtd1295.dtsi | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/arch/arm64/boot/dts/realtek/rtd1295.dtsi b/arch/arm64/boot/dts/realtek/rtd1295.dtsi
index fbbd6456079e..f39ab63f879d 100644
--- a/arch/arm64/boot/dts/realtek/rtd1295.dtsi
+++ b/arch/arm64/boot/dts/realtek/rtd1295.dtsi
@@ -123,6 +123,12 @@
#interrupt-cells = <1>;
};

+ watchdog@98007680 {
+ compatible = "realtek,rtd1295-watchdog";
+ reg = <0x98007680 0x100>;
+ clocks = <&osc27M>;
+ };
+
uart0: serial@98007800 {
compatible = "snps,dw-apb-uart";
reg = <0x98007800 0x400>;
--
2.12.3

2017-08-28 11:36:18

by Andreas Färber

[permalink] [raw]
Subject: [PATCH 2/3] watchdog: Add Realtek RTD1295

Add a watchdog driver for the Realtek RTD1295 SoC.

Based on QNAP's arch/arm/mach-rtk119x/driver/rtk_watchdog.c code and
mach-rtk119x/driver/dc2vo/fpga/include/iso_reg.h register defines.

Signed-off-by: Andreas Färber <[email protected]>
---
drivers/watchdog/Kconfig | 10 +++
drivers/watchdog/Makefile | 1 +
drivers/watchdog/rtd119x_wdt.c | 152 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 163 insertions(+)
create mode 100644 drivers/watchdog/rtd119x_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c722cbfdc7e6..8fa81518c3fa 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -787,6 +787,16 @@ config UNIPHIER_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called uniphier_wdt.

+config RTD119X_WATCHDOG
+ bool "Realtek RTD119x/RTD129x watchdog support"
+ depends on ARCH_REALTEK || COMPILE_TEST
+ depends on OF
+ select WATCHDOG_CORE
+ default ARCH_REALTEK
+ help
+ Say Y here to include support for the watchdog timer in
+ Realtek RTD1295 SoCs.
+
# AVR32 Architecture

config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 56adf9fa67d0..63cb3ed8117d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
+obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o

# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c
new file mode 100644
index 000000000000..1bd2e86509a7
--- /dev/null
+++ b/drivers/watchdog/rtd119x_wdt.c
@@ -0,0 +1,152 @@
+/*
+ * Realtek RTD129x watchdog
+ *
+ * Copyright (c) 2017 Andreas Färber
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+
+#define RTD_TCWCR 0x0
+#define RTD_TCWTR 0x4
+#define RTD_TCWOV 0xc
+
+#define RTD_TCWCR_WDEN_DISABLED 0xa5
+#define RTD_TCWCR_WDEN_ENABLED 0xff
+#define RTD_TCWCR_WDEN_MASK 0xff
+
+#define RTD_TCWTR_WDCLR BIT(0)
+
+struct rtd119x_watchdog_device {
+ struct watchdog_device wdt_dev;
+ void __iomem *base;
+ struct clk *clk;
+};
+
+static int rtd119x_wdt_start(struct watchdog_device *wdev)
+{
+ struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+ u32 val;
+
+ val = readl_relaxed(data->base + RTD_TCWCR);
+ val &= ~RTD_TCWCR_WDEN_MASK;
+ val |= RTD_TCWCR_WDEN_ENABLED;
+ writel(val, data->base + RTD_TCWCR);
+
+ return 0;
+}
+
+static int rtd119x_wdt_stop(struct watchdog_device *wdev)
+{
+ struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+ u32 val;
+
+ val = readl_relaxed(data->base + RTD_TCWCR);
+ val &= ~RTD_TCWCR_WDEN_MASK;
+ val |= RTD_TCWCR_WDEN_DISABLED;
+ writel(val, data->base + RTD_TCWCR);
+
+ return 0;
+}
+
+static int rtd119x_wdt_ping(struct watchdog_device *wdev)
+{
+ struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+
+ writel_relaxed(RTD_TCWTR_WDCLR, data->base + RTD_TCWTR);
+
+ return rtd119x_wdt_start(wdev);
+}
+
+static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
+{
+ struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
+
+ writel(val * 27000000, data->base + RTD_TCWOV);
+
+ return 0;
+}
+
+static const struct watchdog_ops rtd119x_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = rtd119x_wdt_start,
+ .stop = rtd119x_wdt_stop,
+ .ping = rtd119x_wdt_ping,
+ .set_timeout = rtd119x_wdt_set_timeout,
+};
+
+static const struct watchdog_info rtd119x_wdt_info = {
+ .identity = "rtd119x-wdt",
+ .options = 0,
+};
+
+static const struct of_device_id rtd_wdt_dt_ids[] = {
+ { .compatible = "realtek,rtd1295-watchdog" },
+ { }
+};
+
+static int rtd_wdt_probe(struct platform_device *pdev)
+{
+ struct rtd119x_watchdog_device *data;
+ struct resource *res;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ data->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (IS_ERR(data->clk))
+ return PTR_ERR(data->clk);
+
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ clk_put(data->clk);
+ return ret;
+ }
+
+ data->wdt_dev.info = &rtd119x_wdt_info;
+ data->wdt_dev.ops = &rtd119x_wdt_ops;
+ data->wdt_dev.timeout = 120;
+ data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
+ data->wdt_dev.min_timeout = 1;
+ data->wdt_dev.parent = &pdev->dev;
+
+ watchdog_stop_on_reboot(&data->wdt_dev);
+ watchdog_set_drvdata(&data->wdt_dev, data);
+ platform_set_drvdata(pdev, data);
+
+ writel_relaxed(RTD_TCWTR_WDCLR, data->base + RTD_TCWTR);
+ rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
+ rtd119x_wdt_stop(&data->wdt_dev);
+
+ ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev);
+ if (ret) {
+ clk_disable_unprepare(data->clk);
+ clk_put(data->clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver rtd_wdt_driver = {
+ .probe = rtd_wdt_probe,
+ .driver = {
+ .name = "rtd1295-watchdog",
+ .of_match_table = rtd_wdt_dt_ids,
+ },
+};
+builtin_platform_driver(rtd_wdt_driver);
--
2.12.3

2017-09-01 14:30:56

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 1/3] dt-bindings: watchdog: Add Realtek RTD1295

On Mon, Aug 28, 2017 at 01:35:29PM +0200, Andreas F?rber wrote:
> Add a binding for the Realtek RTD1295 watchdog.
>
> Signed-off-by: Andreas F?rber <[email protected]>
> ---
> .../devicetree/bindings/watchdog/realtek,rtd119x.txt | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/watchdog/realtek,rtd119x.txt

Acked-by: Rob Herring <[email protected]>

2017-09-01 14:31:32

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 3/3] arm64: dts: realtek: Add watchdog node for RTD1295

On Mon, Aug 28, 2017 at 01:35:31PM +0200, Andreas F?rber wrote:
> Add the watchdog node to the RTD1295 Device Tree.
>
> Signed-off-by: Andreas F?rber <[email protected]>
> ---
> Depends on the pending clock nodes patch.
>
> arch/arm64/boot/dts/realtek/rtd1295.dtsi | 6 ++++++
> 1 file changed, 6 insertions(+)

Acked-by: Rob Herring <[email protected]>

2017-09-01 16:22:07

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH 2/3] watchdog: Add Realtek RTD1295

On 08/28/2017 04:35 AM, Andreas Färber wrote:
> Add a watchdog driver for the Realtek RTD1295 SoC.
>
> Based on QNAP's arch/arm/mach-rtk119x/driver/rtk_watchdog.c code and
> mach-rtk119x/driver/dc2vo/fpga/include/iso_reg.h register defines.
>
> Signed-off-by: Andreas Färber <[email protected]>
> ---
> drivers/watchdog/Kconfig | 10 +++
> drivers/watchdog/Makefile | 1 +
> drivers/watchdog/rtd119x_wdt.c | 152 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 163 insertions(+)
> create mode 100644 drivers/watchdog/rtd119x_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index c722cbfdc7e6..8fa81518c3fa 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -787,6 +787,16 @@ config UNIPHIER_WATCHDOG
> To compile this driver as a module, choose M here: the
> module will be called uniphier_wdt.
>
> +config RTD119X_WATCHDOG
> + bool "Realtek RTD119x/RTD129x watchdog support"
> + depends on ARCH_REALTEK || COMPILE_TEST
> + depends on OF
> + select WATCHDOG_CORE
> + default ARCH_REALTEK
> + help
> + Say Y here to include support for the watchdog timer in
> + Realtek RTD1295 SoCs.
> +
> # AVR32 Architecture
>
> config AT32AP700X_WDT
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 56adf9fa67d0..63cb3ed8117d 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -87,6 +87,7 @@ obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o
> obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o
> obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o
> obj-$(CONFIG_UNIPHIER_WATCHDOG) += uniphier_wdt.o
> +obj-$(CONFIG_RTD119X_WATCHDOG) += rtd119x_wdt.o
>
> # AVR32 Architecture
> obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
> diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c
> new file mode 100644
> index 000000000000..1bd2e86509a7
> --- /dev/null
> +++ b/drivers/watchdog/rtd119x_wdt.c
> @@ -0,0 +1,152 @@
> +/*
> + * Realtek RTD129x watchdog
> + *
> + * Copyright (c) 2017 Andreas Färber
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/watchdog.h>
> +
> +#define RTD_TCWCR 0x0
> +#define RTD_TCWTR 0x4
> +#define RTD_TCWOV 0xc
> +
> +#define RTD_TCWCR_WDEN_DISABLED 0xa5
> +#define RTD_TCWCR_WDEN_ENABLED 0xff
> +#define RTD_TCWCR_WDEN_MASK 0xff
> +
> +#define RTD_TCWTR_WDCLR BIT(0)
> +

need to include bitops.h.

> +struct rtd119x_watchdog_device {
> + struct watchdog_device wdt_dev;
> + void __iomem *base;
> + struct clk *clk;
> +};
> +
> +static int rtd119x_wdt_start(struct watchdog_device *wdev)
> +{
> + struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
> + u32 val;
> +
> + val = readl_relaxed(data->base + RTD_TCWCR);
> + val &= ~RTD_TCWCR_WDEN_MASK;
> + val |= RTD_TCWCR_WDEN_ENABLED;
> + writel(val, data->base + RTD_TCWCR);
> +
> + return 0;
> +}
> +
> +static int rtd119x_wdt_stop(struct watchdog_device *wdev)
> +{
> + struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
> + u32 val;
> +
> + val = readl_relaxed(data->base + RTD_TCWCR);
> + val &= ~RTD_TCWCR_WDEN_MASK;
> + val |= RTD_TCWCR_WDEN_DISABLED;
> + writel(val, data->base + RTD_TCWCR);
> +
> + return 0;
> +}
> +
> +static int rtd119x_wdt_ping(struct watchdog_device *wdev)
> +{
> + struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
> +
> + writel_relaxed(RTD_TCWTR_WDCLR, data->base + RTD_TCWTR);
> +
> + return rtd119x_wdt_start(wdev);
> +}
> +
> +static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
> +{
> + struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
> +
> + writel(val * 27000000, data->base + RTD_TCWOV);
> +

This also needs to set wdev->timeout.

> + return 0;
> +}
> +
> +static const struct watchdog_ops rtd119x_wdt_ops = {
> + .owner = THIS_MODULE,
> + .start = rtd119x_wdt_start,
> + .stop = rtd119x_wdt_stop,
> + .ping = rtd119x_wdt_ping,
> + .set_timeout = rtd119x_wdt_set_timeout,
> +};
> +
> +static const struct watchdog_info rtd119x_wdt_info = {
> + .identity = "rtd119x-wdt",
> + .options = 0,
> +};
> +
> +static const struct of_device_id rtd_wdt_dt_ids[] = {
> + { .compatible = "realtek,rtd1295-watchdog" },
> + { }
> +};
> +
> +static int rtd_wdt_probe(struct platform_device *pdev)
> +{
> + struct rtd119x_watchdog_device *data;
> + struct resource *res;
> + int ret;
> +
> + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + data->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(data->base))
> + return PTR_ERR(data->base);
> +
> + data->clk = of_clk_get(pdev->dev.of_node, 0);
> + if (IS_ERR(data->clk))
> + return PTR_ERR(data->clk);
> +
> + ret = clk_prepare_enable(data->clk);
> + if (ret) {
> + clk_put(data->clk);
> + return ret;
> + }
> +
> + data->wdt_dev.info = &rtd119x_wdt_info;
> + data->wdt_dev.ops = &rtd119x_wdt_ops;
> + data->wdt_dev.timeout = 120;
> + data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
> + data->wdt_dev.min_timeout = 1;
> + data->wdt_dev.parent = &pdev->dev;
> +
> + watchdog_stop_on_reboot(&data->wdt_dev);
> + watchdog_set_drvdata(&data->wdt_dev, data);
> + platform_set_drvdata(pdev, data);
> +
Not needed since there is no remove function.

> + writel_relaxed(RTD_TCWTR_WDCLR, data->base + RTD_TCWTR);
> + rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
> + rtd119x_wdt_stop(&data->wdt_dev);
> +
> + ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev);
> + if (ret) {
> + clk_disable_unprepare(data->clk);
> + clk_put(data->clk);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static struct platform_driver rtd_wdt_driver = {
> + .probe = rtd_wdt_probe,
> + .driver = {
> + .name = "rtd1295-watchdog",
> + .of_match_table = rtd_wdt_dt_ids,
> + },
> +};
> +builtin_platform_driver(rtd_wdt_driver);
>