diff mbox series

[1/3] dt-bindings: watchdog: sunxi: Add compatible for R329

Message ID 20210726035143.53132-1-samuel@sholland.org
State Changes Requested, archived
Headers show
Series [1/3] dt-bindings: watchdog: sunxi: Add compatible for R329 | expand

Checks

Context Check Description
robh/checkpatch success
robh/dt-meta-schema success
robh/dtbs-check fail build log

Commit Message

Samuel Holland July 26, 2021, 3:51 a.m. UTC
On existing SoCs, the watchdog has a single clock input: HOSC (OSC24M)
divided by 750.  However, starting with R329, LOSC (OSC32k) is added as
an alternative clock source, with a bit to switch between them.

Since 24 MHz / 750 == 32 kHz, not 32.768 kHz, the hardware adjusts the
cycle counts to keep the timeouts independent of the clock source. This
keeps the programming interface backward-compatible.

R329 also adds two new registers, to allow software to immediately drive
the SoC reset signal.

Signed-off-by: Samuel Holland <samuel@sholland.org>
---
 .../watchdog/allwinner,sun4i-a10-wdt.yaml     | 30 ++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

Comments

Guenter Roeck July 29, 2021, 4:11 a.m. UTC | #1
On Sun, Jul 25, 2021 at 10:51:43PM -0500, Samuel Holland wrote:
> D1 adds a key field to the "CFG" and "MODE" registers, that must be set
> to change the other bits. Add logic to set the key when updating those
> registers.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>

Reviewed-by: Guenter Roeck <linux@roeck-us.net>

> ---
>  drivers/watchdog/sunxi_wdt.c | 20 +++++++++++++++++++-
>  1 file changed, 19 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c
> index b50757882a98..6cf82922d3fb 100644
> --- a/drivers/watchdog/sunxi_wdt.c
> +++ b/drivers/watchdog/sunxi_wdt.c
> @@ -48,6 +48,7 @@ struct sunxi_wdt_reg {
>  	u8 wdt_timeout_shift;
>  	u8 wdt_reset_mask;
>  	u8 wdt_reset_val;
> +	u32 wdt_key_val;
>  };
>  
>  struct sunxi_wdt_dev {
> @@ -91,12 +92,14 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
>  	val = readl(wdt_base + regs->wdt_cfg);
>  	val &= ~(regs->wdt_reset_mask);
>  	val |= regs->wdt_reset_val;
> +	val |= regs->wdt_key_val;
>  	writel(val, wdt_base + regs->wdt_cfg);
>  
>  	/* Set lowest timeout and enable watchdog */
>  	val = readl(wdt_base + regs->wdt_mode);
>  	val &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
>  	val |= WDT_MODE_EN;
> +	val |= regs->wdt_key_val;
>  	writel(val, wdt_base + regs->wdt_mode);
>  
>  	/*
> @@ -109,6 +112,7 @@ static int sunxi_wdt_restart(struct watchdog_device *wdt_dev,
>  		mdelay(5);
>  		val = readl(wdt_base + regs->wdt_mode);
>  		val |= WDT_MODE_EN;
> +		val |= regs->wdt_key_val;
>  		writel(val, wdt_base + regs->wdt_mode);
>  	}
>  	return 0;
> @@ -141,6 +145,7 @@ static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev,
>  	reg = readl(wdt_base + regs->wdt_mode);
>  	reg &= ~(WDT_TIMEOUT_MASK << regs->wdt_timeout_shift);
>  	reg |= wdt_timeout_map[timeout] << regs->wdt_timeout_shift;
> +	reg |= regs->wdt_key_val;
>  	writel(reg, wdt_base + regs->wdt_mode);
>  
>  	sunxi_wdt_ping(wdt_dev);
> @@ -154,7 +159,7 @@ static int sunxi_wdt_stop(struct watchdog_device *wdt_dev)
>  	void __iomem *wdt_base = sunxi_wdt->wdt_base;
>  	const struct sunxi_wdt_reg *regs = sunxi_wdt->wdt_regs;
>  
> -	writel(0, wdt_base + regs->wdt_mode);
> +	writel(regs->wdt_key_val, wdt_base + regs->wdt_mode);
>  
>  	return 0;
>  }
> @@ -176,11 +181,13 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
>  	reg = readl(wdt_base + regs->wdt_cfg);
>  	reg &= ~(regs->wdt_reset_mask);
>  	reg |= regs->wdt_reset_val;
> +	reg |= regs->wdt_key_val;
>  	writel(reg, wdt_base + regs->wdt_cfg);
>  
>  	/* Enable watchdog */
>  	reg = readl(wdt_base + regs->wdt_mode);
>  	reg |= WDT_MODE_EN;
> +	reg |= regs->wdt_key_val;
>  	writel(reg, wdt_base + regs->wdt_mode);
>  
>  	return 0;
> @@ -220,9 +227,20 @@ static const struct sunxi_wdt_reg sun6i_wdt_reg = {
>  	.wdt_reset_val = 0x01,
>  };
>  
> +static const struct sunxi_wdt_reg sun20i_wdt_reg = {
> +	.wdt_ctrl = 0x10,
> +	.wdt_cfg = 0x14,
> +	.wdt_mode = 0x18,
> +	.wdt_timeout_shift = 4,
> +	.wdt_reset_mask = 0x03,
> +	.wdt_reset_val = 0x01,
> +	.wdt_key_val = 0x16aa0000,
> +};
> +
>  static const struct of_device_id sunxi_wdt_dt_ids[] = {
>  	{ .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_reg },
>  	{ .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_reg },
> +	{ .compatible = "allwinner,sun20i-d1-wdt", .data = &sun20i_wdt_reg },
>  	{ /* sentinel */ }
>  };
>  MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids);
Maxime Ripard July 29, 2021, 12:18 p.m. UTC | #2
Hi,

On Sun, Jul 25, 2021 at 10:51:41PM -0500, Samuel Holland wrote:
> On existing SoCs, the watchdog has a single clock input: HOSC (OSC24M)
> divided by 750.  However, starting with R329, LOSC (OSC32k) is added as
> an alternative clock source, with a bit to switch between them.
> 
> Since 24 MHz / 750 == 32 kHz, not 32.768 kHz, the hardware adjusts the
> cycle counts to keep the timeouts independent of the clock source. This
> keeps the programming interface backward-compatible.
> 
> R329 also adds two new registers, to allow software to immediately drive
> the SoC reset signal.
> 
> Signed-off-by: Samuel Holland <samuel@sholland.org>
> ---
>  .../watchdog/allwinner,sun4i-a10-wdt.yaml     | 30 ++++++++++++++++++-
>  1 file changed, 29 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
> index 9aa3c313c49f..853ceb1b7c0f 100644
> --- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
> +++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
> @@ -24,6 +24,7 @@ properties:
>                - allwinner,sun50i-a100-wdt
>                - allwinner,sun50i-h6-wdt
>                - allwinner,sun50i-h616-wdt
> +              - allwinner,sun50i-r329-wdt
>            - const: allwinner,sun6i-a31-wdt
>        - items:
>            - const: allwinner,suniv-f1c100s-wdt
> @@ -33,7 +34,16 @@ properties:
>      maxItems: 1
>  
>    clocks:
> -    maxItems: 1
> +    minItems: 1
> +    maxItems: 2
> +
> +  clock-names:
> +    minItems: 1
> +    maxItems: 2
> +    items:
> +      enum:
> +        - hosc
> +        - losc

So I assume that the expectations are that hosc is first (or the only
valid choice with older SoCs), and losc second?

If so, we should make it clear using two const instead of an enum. Here
you state that on older SoCs we could use either the hosc or losc clocks
as source, which is incorrect.

I guess adding descriptions for clocks would be great too.

Maxime
diff mbox series

Patch

diff --git a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
index 9aa3c313c49f..853ceb1b7c0f 100644
--- a/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/allwinner,sun4i-a10-wdt.yaml
@@ -24,6 +24,7 @@  properties:
               - allwinner,sun50i-a100-wdt
               - allwinner,sun50i-h6-wdt
               - allwinner,sun50i-h616-wdt
+              - allwinner,sun50i-r329-wdt
           - const: allwinner,sun6i-a31-wdt
       - items:
           - const: allwinner,suniv-f1c100s-wdt
@@ -33,7 +34,16 @@  properties:
     maxItems: 1
 
   clocks:
-    maxItems: 1
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      enum:
+        - hosc
+        - losc
 
   interrupts:
     maxItems: 1
@@ -44,6 +54,24 @@  required:
   - clocks
   - interrupts
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - allwinner,sun50i-r329-wdt
+
+then:
+  properties:
+    clocks:
+      minItems: 2
+
+    clock-names:
+      minItems: 2
+
+  required:
+    - clock-names
+
 unevaluatedProperties: false
 
 examples: