2021-11-08 03:49:06

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 00/12] watchdog: s3c2410: Add Exynos850 support

Exynos850 WDT IP-core differs a bit from existing platforms:
- Another set of PMU registers
- Separate WDT instance for each CPU cluster, having different PMU
registers/bits
- Source clock is different from PCLK

Implement all missing features above and enable Exynos850 WDT support.
While at it, do a bit of cleaning up.

Changes in v3:
- Renamed "samsung,index" property to more descriptive
"samsung,cluster-index"
- bindings: Disabled "samsung,cluster-index" property for SoCs other
than Exynos850
- Added quirks documentation
- Removed has_src_clk field: clk framework can handle NULL clk; added
s3c2410wdt_get_freq() function instead, to figure out which clock to
use for getting the rate
- Used pre-defined and completely set driver data for cluster0 and
cluster1
- Coding style changes
- Added R-b tags

Changes in v2:
- Added patch to fail probe if no valid timeout was found, as
suggested by Guenter Roeck (03/12)
- Extracted code for separating disable/mask functions into separate
patch (06/12)
- Added patch for inverting mask register value (07/12)
- Extracted PMU cleanup code to separate patch, to ease the review and
porting (09/12)
- Added patch for removing not needed 'err' label in probe function
(11/12)
- Addressed all outstanding review comments on mailing list

Sam Protsenko (12):
dt-bindings: watchdog: Require samsung,syscon-phandle for Exynos7
dt-bindings: watchdog: Document Exynos850 watchdog bindings
watchdog: s3c2410: Fail probe if can't find valid timeout
watchdog: s3c2410: Let kernel kick watchdog
watchdog: s3c2410: Make reset disable register optional
watchdog: s3c2410: Extract disable and mask code into separate
functions
watchdog: s3c2410: Implement a way to invert mask reg value
watchdog: s3c2410: Add support for WDT counter enable register
watchdog: s3c2410: Cleanup PMU related code
watchdog: s3c2410: Support separate source clock
watchdog: s3c2410: Remove superfluous err label
watchdog: s3c2410: Add Exynos850 support

.../bindings/watchdog/samsung-wdt.yaml | 48 ++-
drivers/watchdog/s3c2410_wdt.c | 322 +++++++++++++-----
2 files changed, 287 insertions(+), 83 deletions(-)

--
2.30.2


2021-11-08 03:50:16

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 02/12] dt-bindings: watchdog: Document Exynos850 watchdog bindings

Exynos850 SoC has two CPU clusters:
- cluster 0: contains CPUs #0, #1, #2, #3
- cluster 1: contains CPUs #4, #5, #6, #7

Each cluster has its own dedicated watchdog timer. Those WDT instances
are controlled using different bits in PMU registers, new
"samsung,index" property is added to tell the driver which bits to use
for defined watchdog node.

Also on Exynos850 the peripheral clock and the source clock are two
different clocks. Provide a way to specify two clocks in watchdog device
tree node.

Signed-off-by: Sam Protsenko <[email protected]>
---
Changes in v3:
- Renamed "samsung,index" property to more descriptive
"samsung,cluster-index"
- Disabled "samsung,cluster-index" property for SoCs other than
Exynos850

Changes in v2:
- Stated explicitly that Exynos850 driver requires 2 clocks
- Used single compatible for Exynos850
- Added "index" property to specify CPU cluster index
- Fixed a typo in commit message: dedicater -> dedicated

.../bindings/watchdog/samsung-wdt.yaml | 45 +++++++++++++++++--
1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
index 93cd77a6e92c..b08373336b16 100644
--- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
@@ -22,25 +22,32 @@ properties:
- samsung,exynos5250-wdt # for Exynos5250
- samsung,exynos5420-wdt # for Exynos5420
- samsung,exynos7-wdt # for Exynos7
+ - samsung,exynos850-wdt # for Exynos850

reg:
maxItems: 1

clocks:
- maxItems: 1
+ minItems: 1
+ maxItems: 2

clock-names:
- items:
- - const: watchdog
+ minItems: 1
+ maxItems: 2

interrupts:
maxItems: 1

+ samsung,cluster-index:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Index of CPU cluster on which watchdog is running (in case of Exynos850)
+
samsung,syscon-phandle:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the PMU system controller node (in case of Exynos5250,
- Exynos5420 and Exynos7).
+ Exynos5420, Exynos7 and Exynos850).

required:
- compatible
@@ -59,9 +66,39 @@ allOf:
- samsung,exynos5250-wdt
- samsung,exynos5420-wdt
- samsung,exynos7-wdt
+ - samsung,exynos850-wdt
then:
required:
- samsung,syscon-phandle
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - samsung,exynos850-wdt
+ then:
+ properties:
+ clocks:
+ items:
+ - description: Bus clock, used for register interface
+ - description: Source clock (driving watchdog counter)
+ clock-names:
+ items:
+ - const: watchdog
+ - const: watchdog_src
+ samsung,cluster-index:
+ enum: [0, 1]
+ required:
+ - samsung,cluster-index
+ else:
+ properties:
+ clocks:
+ items:
+ - description: Bus clock, which is also a source clock
+ clock-names:
+ items:
+ - const: watchdog
+ samsung,cluster-index: false

unevaluatedProperties: false

--
2.30.2

2021-11-08 03:55:47

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 04/12] watchdog: s3c2410: Let kernel kick watchdog

When "tmr_atboot" module param is set, the watchdog is started in
driver's probe. In that case, also set WDOG_HW_RUNNING bit to let
watchdog core driver know it's running. This way watchdog core can kick
the watchdog for us (if CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED option is
enabled), until user space takes control.

WDOG_HW_RUNNING bit must be set before registering the watchdog. So the
"tmr_atboot" handling code is moved before watchdog registration, to
avoid performing the same check twice. This is also logical because
WDOG_HW_RUNNING bit makes WDT core expect actually running watchdog.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- Added explanation on moving the code block to commit message
- [PATCH 03/12] handles the case when tmr_atboot is present but valid
timeout wasn't found

drivers/watchdog/s3c2410_wdt.c | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 00421cf22556..0845c05034a1 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -604,6 +604,21 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
wdt->wdt_device.parent = dev;

+ /*
+ * If "tmr_atboot" param is non-zero, start the watchdog right now. Also
+ * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
+ *
+ * If we're not enabling the watchdog, then ensure it is disabled if it
+ * has been left running from the bootloader or other source.
+ */
+ if (tmr_atboot) {
+ dev_info(dev, "starting watchdog timer\n");
+ s3c2410wdt_start(&wdt->wdt_device);
+ set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
+ } else {
+ s3c2410wdt_stop(&wdt->wdt_device);
+ }
+
ret = watchdog_register_device(&wdt->wdt_device);
if (ret)
goto err_cpufreq;
@@ -612,17 +627,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (ret < 0)
goto err_unregister;

- if (tmr_atboot) {
- dev_info(dev, "starting watchdog timer\n");
- s3c2410wdt_start(&wdt->wdt_device);
- } else {
- /* if we're not enabling the watchdog, then ensure it is
- * disabled if it has been left running from the bootloader
- * or other source */
-
- s3c2410wdt_stop(&wdt->wdt_device);
- }
-
platform_set_drvdata(pdev, wdt);

/* print out a statement of readiness */
--
2.30.2

2021-11-08 04:05:06

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 03/12] watchdog: s3c2410: Fail probe if can't find valid timeout

Driver can't work properly if there no valid timeout was found in
s3c2410wdt_set_heartbeat(). Ideally, that function should be reworked in
a way that it's always able to find some valid timeout. As a temporary
solution let's for now just fail the driver probe in case the valid
timeout can't be found in s3c2410wdt_set_heartbeat() function.

Signed-off-by: Sam Protsenko <[email protected]>
Reported-by: Guenter Roeck <[email protected]>
Suggested-by: Guenter Roeck <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- (none): it's a new patch

drivers/watchdog/s3c2410_wdt.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 2395f353e52d..00421cf22556 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -515,7 +515,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
struct s3c2410_wdt *wdt;
struct resource *wdt_irq;
unsigned int wtcon;
- int started = 0;
int ret;

wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
@@ -581,15 +580,15 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
wdt->wdt_device.timeout);
if (ret) {
- started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
- S3C2410_WATCHDOG_DEFAULT_TIME);
-
- if (started == 0)
- dev_info(dev,
- "tmr_margin value out of range, default %d used\n",
+ ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
+ S3C2410_WATCHDOG_DEFAULT_TIME);
+ if (ret == 0) {
+ dev_warn(dev, "tmr_margin value out of range, default %d used\n",
S3C2410_WATCHDOG_DEFAULT_TIME);
- else
- dev_info(dev, "default timer value is out of range, cannot start\n");
+ } else {
+ dev_err(dev, "failed to use default timeout\n");
+ goto err_cpufreq;
+ }
}

ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
@@ -613,10 +612,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (ret < 0)
goto err_unregister;

- if (tmr_atboot && started == 0) {
+ if (tmr_atboot) {
dev_info(dev, "starting watchdog timer\n");
s3c2410wdt_start(&wdt->wdt_device);
- } else if (!tmr_atboot) {
+ } else {
/* if we're not enabling the watchdog, then ensure it is
* disabled if it has been left running from the bootloader
* or other source */
--
2.30.2

2021-11-08 04:05:51

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 05/12] watchdog: s3c2410: Make reset disable register optional

On new Exynos chips (e.g. Exynos850 and Exynos9) the
AUTOMATIC_WDT_RESET_DISABLE register was removed, and its value can be
thought of as "always 0x0". Add correspondig quirk bit, so that the
driver can omit accessing it if it's not present.

This commit doesn't bring any functional change to existing devices, but
merely provides an infrastructure for upcoming chips support.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Aligned arguments with opening parentheses
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- Used quirks instead of callbacks for all added PMU registers
- Used BIT() macro
- Extracted splitting the s3c2410wdt_mask_and_disable_reset() function
to separate patch
- Extracted cleanup code to separate patch to minimize changes and
ease the review and porting

drivers/watchdog/s3c2410_wdt.c | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 0845c05034a1..2cc4923a98a5 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -59,10 +59,12 @@
#define QUIRK_HAS_PMU_CONFIG (1 << 0)
#define QUIRK_HAS_RST_STAT (1 << 1)
#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
+#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)

/* These quirks require that we have a PMU register map */
#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
- QUIRK_HAS_RST_STAT)
+ QUIRK_HAS_RST_STAT | \
+ QUIRK_HAS_PMU_AUTO_DISABLE)

static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin;
@@ -137,7 +139,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 20,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG,
+ | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
@@ -147,7 +149,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 9,
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG,
+ | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct s3c2410_wdt_variant drv_data_exynos7 = {
@@ -157,7 +159,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 23, /* A57 WDTRESET */
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG,
+ | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct of_device_id s3c2410_wdt_match[] = {
@@ -213,11 +215,13 @@ static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
if (mask)
val = mask_val;

- ret = regmap_update_bits(wdt->pmureg,
- wdt->drv_data->disable_reg,
- mask_val, val);
- if (ret < 0)
- goto error;
+ if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
+ ret = regmap_update_bits(wdt->pmureg,
+ wdt->drv_data->disable_reg, mask_val,
+ val);
+ if (ret < 0)
+ goto error;
+ }

ret = regmap_update_bits(wdt->pmureg,
wdt->drv_data->mask_reset_reg,
--
2.30.2

2021-11-08 04:19:54

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 06/12] watchdog: s3c2410: Extract disable and mask code into separate functions

The s3c2410wdt_mask_and_disable_reset() function content is bound to be
changed further. Prepare it for upcoming changes by splitting into
separate "mask reset" and "disable reset" functions. But keep
s3c2410wdt_mask_and_disable_reset() function present as a facade.

This commit doesn't bring any functional change to existing devices, but
merely provides an infrastructure for upcoming chips support.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- (none): it's a new patch

drivers/watchdog/s3c2410_wdt.c | 54 ++++++++++++++++++++++------------
1 file changed, 35 insertions(+), 19 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 2cc4923a98a5..4ac0a30e835e 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -202,37 +202,53 @@ static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
return container_of(nb, struct s3c2410_wdt, freq_transition);
}

-static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
+static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
{
+ const u32 mask_val = BIT(wdt->drv_data->mask_bit);
+ const u32 val = mask ? mask_val : 0;
int ret;
- u32 mask_val = 1 << wdt->drv_data->mask_bit;
- u32 val = 0;

- /* No need to do anything if no PMU CONFIG needed */
- if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
- return 0;
+ ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
+ mask_val, val);
+ if (ret < 0)
+ dev_err(wdt->dev, "failed to update reg(%d)\n", ret);

- if (mask)
- val = mask_val;
+ return ret;
+}

- if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
- ret = regmap_update_bits(wdt->pmureg,
- wdt->drv_data->disable_reg, mask_val,
- val);
- if (ret < 0)
- goto error;
- }
+static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
+{
+ const u32 mask_val = BIT(wdt->drv_data->mask_bit);
+ const u32 val = mask ? mask_val : 0;
+ int ret;

- ret = regmap_update_bits(wdt->pmureg,
- wdt->drv_data->mask_reset_reg,
- mask_val, val);
- error:
+ ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
+ mask_val, val);
if (ret < 0)
dev_err(wdt->dev, "failed to update reg(%d)\n", ret);

return ret;
}

+static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
+{
+ int ret;
+
+ if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
+ ret = s3c2410wdt_disable_wdt_reset(wdt, mask);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG) {
+ ret = s3c2410wdt_mask_wdt_reset(wdt, mask);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
--
2.30.2

2021-11-08 04:19:54

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 08/12] watchdog: s3c2410: Add support for WDT counter enable register

On new Exynos chips (e.g. Exynos850) new CLUSTERx_NONCPU_OUT register is
introduced, where CNT_EN_WDT bit must be enabled to make watchdog
counter running. Add corresponding quirk and proper infrastructure to
handle that register if the quirk is set.

This commit doesn't bring any functional change to existing devices, but
merely provides an infrastructure for upcoming chips support.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- Used quirks instead of callbacks for all added PMU registers
- Used BIT() macro
- Extracted cleanup code to separate patch to minimize changes and
ease the review and porting

drivers/watchdog/s3c2410_wdt.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 2a61b6ea5602..ec341c876225 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -60,11 +60,13 @@
#define QUIRK_HAS_RST_STAT (1 << 1)
#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
+#define QUIRK_HAS_PMU_CNT_EN (1 << 4)

/* These quirks require that we have a PMU register map */
#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
QUIRK_HAS_RST_STAT | \
- QUIRK_HAS_PMU_AUTO_DISABLE)
+ QUIRK_HAS_PMU_AUTO_DISABLE | \
+ QUIRK_HAS_PMU_CNT_EN)

static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin;
@@ -98,6 +100,8 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
* @rst_stat_reg: Offset in pmureg for the register that has the reset status.
* @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
* reset.
+ * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
+ * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
* @quirks: A bitfield of quirks.
*/

@@ -108,6 +112,8 @@ struct s3c2410_wdt_variant {
int mask_bit;
int rst_stat_reg;
int rst_stat_bit;
+ int cnt_en_reg;
+ int cnt_en_bit;
u32 quirks;
};

@@ -233,6 +239,20 @@ static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
return ret;
}

+static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
+{
+ const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
+ const u32 val = en ? mask_val : 0;
+ int ret;
+
+ ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
+ mask_val, val);
+ if (ret < 0)
+ dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
+
+ return ret;
+}
+
static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
{
int ret;
@@ -249,6 +269,12 @@ static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
return ret;
}

+ if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
+ ret = s3c2410wdt_enable_counter(wdt, !mask);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}

--
2.30.2

2021-11-08 04:19:54

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 07/12] watchdog: s3c2410: Implement a way to invert mask reg value

On new Exynos chips (like Exynos850) the MASK_WDT_RESET_REQUEST register
is replaced with CLUSTERx_NONCPU_INT_EN, and its mask bit value meaning
was reversed: for new register the bit value "1" means "Interrupt
enabled", while for MASK_WDT_RESET_REQUEST register "1" means "Mask the
interrupt" (i.e. "Interrupt disabled").

Introduce "mask_reset_inv" boolean field in driver data structure; when
that field is "true", mask register handling function will invert the
value before setting it to the register.

This commit doesn't bring any functional change to existing devices, but
merely provides an infrastructure for upcoming chips support.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- (none): it's a new patch

drivers/watchdog/s3c2410_wdt.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index 4ac0a30e835e..2a61b6ea5602 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -92,6 +92,7 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
* timer reset functionality.
* @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
* timer reset functionality.
+ * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
* @mask_bit: Bit number for the watchdog timer in the disable register and the
* mask reset register.
* @rst_stat_reg: Offset in pmureg for the register that has the reset status.
@@ -103,6 +104,7 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
struct s3c2410_wdt_variant {
int disable_reg;
int mask_reset_reg;
+ bool mask_reset_inv;
int mask_bit;
int rst_stat_reg;
int rst_stat_bit;
@@ -219,7 +221,8 @@ static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
{
const u32 mask_val = BIT(wdt->drv_data->mask_bit);
- const u32 val = mask ? mask_val : 0;
+ const bool val_inv = wdt->drv_data->mask_reset_inv;
+ const u32 val = (mask ^ val_inv) ? mask_val : 0;
int ret;

ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
--
2.30.2

2021-11-08 04:20:05

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 10/12] watchdog: s3c2410: Support separate source clock

Right now all devices supported in the driver have the single clock: it
acts simultaneously as a bus clock (providing register interface
clocking) and source clock (driving watchdog counter). Some newer Exynos
chips, like Exynos850, have two separate clocks for that. In that case
two clocks will be passed to the driver from the resource provider, e.g.
Device Tree. Provide necessary infrastructure to support that case:
- use source clock's rate for all timer related calculations
- use bus clock to gate/ungate the register interface

All devices that use the single clock are kept intact: if only one clock
is passed from Device Tree, it will be used for both purposes as before.

Signed-off-by: Sam Protsenko <[email protected]>
---
Changes in v3:
- Removed has_src_clk field: clk framework can handle NULL clk; added
s3c2410wdt_get_freq() function instead, to figure out which clock to
use for getting the rate

Changes in v2:
- Reworded commit message to be more formal
- Used separate "has_src_clk" trait to tell if source clock is present
- Renamed clock variables to match their purpose
- Removed caching source clock rate, obtaining it in place each time
instead
- Renamed err labels for more consistency

drivers/watchdog/s3c2410_wdt.c | 56 +++++++++++++++++++++++++---------
1 file changed, 41 insertions(+), 15 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f211be8bf976..f31bc765a8a5 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -153,7 +153,8 @@ struct s3c2410_wdt_variant {

struct s3c2410_wdt {
struct device *dev;
- struct clk *clock;
+ struct clk *bus_clk; /* for register interface (PCLK) */
+ struct clk *src_clk; /* for WDT counter */
void __iomem *reg_base;
unsigned int count;
spinlock_t lock;
@@ -231,9 +232,14 @@ MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);

/* functions */

-static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
+static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
{
- unsigned long freq = clk_get_rate(clock);
+ return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
+}
+
+static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
+{
+ const unsigned long freq = s3c2410wdt_get_freq(wdt);

return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
/ S3C2410_WTCON_MAXDIV);
@@ -383,7 +389,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
unsigned int timeout)
{
struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
- unsigned long freq = clk_get_rate(wdt->clock);
+ unsigned long freq = s3c2410wdt_get_freq(wdt);
unsigned int count;
unsigned int divisor = 1;
unsigned long wtcon;
@@ -632,26 +638,42 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
goto err;
}

- wdt->clock = devm_clk_get(dev, "watchdog");
- if (IS_ERR(wdt->clock)) {
- dev_err(dev, "failed to find watchdog clock source\n");
- ret = PTR_ERR(wdt->clock);
+ wdt->bus_clk = devm_clk_get(dev, "watchdog");
+ if (IS_ERR(wdt->bus_clk)) {
+ dev_err(dev, "failed to find bus clock\n");
+ ret = PTR_ERR(wdt->bus_clk);
goto err;
}

- ret = clk_prepare_enable(wdt->clock);
+ ret = clk_prepare_enable(wdt->bus_clk);
if (ret < 0) {
- dev_err(dev, "failed to enable clock\n");
+ dev_err(dev, "failed to enable bus clock\n");
return ret;
}

+ /*
+ * "watchdog_src" clock is optional; if it's not present -- just skip it
+ * and use "watchdog" clock as both bus and source clock.
+ */
+ wdt->src_clk = devm_clk_get(dev, "watchdog_src");
+ if (!IS_ERR(wdt->src_clk)) {
+ ret = clk_prepare_enable(wdt->src_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable source clock\n");
+ ret = PTR_ERR(wdt->src_clk);
+ goto err_bus_clk;
+ }
+ } else {
+ wdt->src_clk = NULL;
+ }
+
wdt->wdt_device.min_timeout = 1;
- wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
+ wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);

ret = s3c2410wdt_cpufreq_register(wdt);
if (ret < 0) {
dev_err(dev, "failed to register cpufreq\n");
- goto err_clk;
+ goto err_src_clk;
}

watchdog_set_drvdata(&wdt->wdt_device, wdt);
@@ -729,8 +751,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
err_cpufreq:
s3c2410wdt_cpufreq_deregister(wdt);

- err_clk:
- clk_disable_unprepare(wdt->clock);
+ err_src_clk:
+ clk_disable_unprepare(wdt->src_clk);
+
+ err_bus_clk:
+ clk_disable_unprepare(wdt->bus_clk);

err:
return ret;
@@ -749,7 +774,8 @@ static int s3c2410wdt_remove(struct platform_device *dev)

s3c2410wdt_cpufreq_deregister(wdt);

- clk_disable_unprepare(wdt->clock);
+ clk_disable_unprepare(wdt->src_clk);
+ clk_disable_unprepare(wdt->bus_clk);

return 0;
}
--
2.30.2

2021-11-08 04:20:05

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 11/12] watchdog: s3c2410: Remove superfluous err label

'err' label in probe function is not really need, it just returns.
Remove it and replace all 'goto' statements with actual returns in
place.

No functional change here, just a cleanup patch.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- (none): it's a new patch

drivers/watchdog/s3c2410_wdt.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index f31bc765a8a5..96aa5d9c6ed4 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -627,22 +627,18 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (wdt_irq == NULL) {
dev_err(dev, "no irq resource specified\n");
- ret = -ENOENT;
- goto err;
+ return -ENOENT;
}

/* get the memory region for the watchdog timer */
wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(wdt->reg_base)) {
- ret = PTR_ERR(wdt->reg_base);
- goto err;
- }
+ if (IS_ERR(wdt->reg_base))
+ return PTR_ERR(wdt->reg_base);

wdt->bus_clk = devm_clk_get(dev, "watchdog");
if (IS_ERR(wdt->bus_clk)) {
dev_err(dev, "failed to find bus clock\n");
- ret = PTR_ERR(wdt->bus_clk);
- goto err;
+ return PTR_ERR(wdt->bus_clk);
}

ret = clk_prepare_enable(wdt->bus_clk);
@@ -757,7 +753,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
err_bus_clk:
clk_disable_unprepare(wdt->bus_clk);

- err:
return ret;
}

--
2.30.2

2021-11-08 04:31:47

by Sam Protsenko

[permalink] [raw]
Subject: [PATCH v3 09/12] watchdog: s3c2410: Cleanup PMU related code

Now that PMU enablement code was extended for new Exynos SoCs, it
doesn't look very cohesive and consistent anymore. Do a bit of renaming,
grouping and style changes, to make it look good again. While at it, add
quirks documentation as well.

No functional change, just a refactoring commit.

Signed-off-by: Sam Protsenko <[email protected]>
Reviewed-by: Krzysztof Kozlowski <[email protected]>
---
Changes in v3:
- Added quirks documentation
- Added R-b tag by Krzysztof Kozlowski

Changes in v2:
- (none): it's a new patch

drivers/watchdog/s3c2410_wdt.c | 83 ++++++++++++++++++++++++----------
1 file changed, 58 insertions(+), 25 deletions(-)

diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index ec341c876225..f211be8bf976 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -56,17 +56,51 @@
#define EXYNOS5_RST_STAT_REG_OFFSET 0x0404
#define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
-#define QUIRK_HAS_PMU_CONFIG (1 << 0)
-#define QUIRK_HAS_RST_STAT (1 << 1)
-#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
+
+/**
+ * Quirk flags for different Samsung watchdog IP-cores.
+ *
+ * This driver supports multiple Samsung SoCs, each of which might have
+ * different set of registers and features supported. As watchdog block
+ * sometimes requires modifying PMU registers for proper functioning, register
+ * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
+ * flags described below serve the purpose of telling the driver about mentioned
+ * SoC traits, and can be specified in driver data for each particular supported
+ * device.
+ *
+ * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
+ * clear the interrupt once the interrupt service routine is complete. It's
+ * write-only, writing any values to this register clears the interrupt, but
+ * reading is not permitted.
+ *
+ * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
+ * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
+ * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
+ * inverted compared to the former one.
+ *
+ * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
+ * which contains bits indicating the reason for most recent CPU reset. If
+ * present, driver will use this register to check if previous reboot was due to
+ * watchdog timer reset.
+ *
+ * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
+ * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
+ * corresponding processor is in reset state.
+ *
+ * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
+ * with "watchdog counter enable" bit. That bit should be set to make watchdog
+ * counter running.
+ */
+#define QUIRK_HAS_WTCLRINT_REG (1 << 0)
+#define QUIRK_HAS_PMU_MASK_RESET (1 << 1)
+#define QUIRK_HAS_PMU_RST_STAT (1 << 2)
#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
#define QUIRK_HAS_PMU_CNT_EN (1 << 4)

/* These quirks require that we have a PMU register map */
-#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
- QUIRK_HAS_RST_STAT | \
- QUIRK_HAS_PMU_AUTO_DISABLE | \
- QUIRK_HAS_PMU_CNT_EN)
+#define QUIRKS_HAVE_PMUREG \
+ (QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
+ QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)

static bool nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin;
@@ -146,8 +180,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
.mask_bit = 20,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 20,
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
+ .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
@@ -156,8 +190,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
.mask_bit = 0,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 9,
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
+ .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct s3c2410_wdt_variant drv_data_exynos7 = {
@@ -166,8 +200,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
.mask_bit = 23,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 23, /* A57 WDTRESET */
- .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
- | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
+ .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
+ QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
};

static const struct of_device_id s3c2410_wdt_match[] = {
@@ -253,24 +287,24 @@ static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
return ret;
}

-static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
+static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
{
int ret;

if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
- ret = s3c2410wdt_disable_wdt_reset(wdt, mask);
+ ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
if (ret < 0)
return ret;
}

- if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG) {
- ret = s3c2410wdt_mask_wdt_reset(wdt, mask);
+ if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
+ ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
if (ret < 0)
return ret;
}

if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
- ret = s3c2410wdt_enable_counter(wdt, !mask);
+ ret = s3c2410wdt_enable_counter(wdt, en);
if (ret < 0)
return ret;
}
@@ -531,7 +565,7 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
unsigned int rst_stat;
int ret;

- if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
+ if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
return 0;

ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
@@ -672,7 +706,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
if (ret)
goto err_cpufreq;

- ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
+ ret = s3c2410wdt_enable(wdt, true);
if (ret < 0)
goto err_unregister;

@@ -707,7 +741,7 @@ static int s3c2410wdt_remove(struct platform_device *dev)
int ret;
struct s3c2410_wdt *wdt = platform_get_drvdata(dev);

- ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
+ ret = s3c2410wdt_enable(wdt, false);
if (ret < 0)
return ret;

@@ -724,8 +758,7 @@ static void s3c2410wdt_shutdown(struct platform_device *dev)
{
struct s3c2410_wdt *wdt = platform_get_drvdata(dev);

- s3c2410wdt_mask_and_disable_reset(wdt, true);
-
+ s3c2410wdt_enable(wdt, false);
s3c2410wdt_stop(&wdt->wdt_device);
}

@@ -740,7 +773,7 @@ static int s3c2410wdt_suspend(struct device *dev)
wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);

- ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
+ ret = s3c2410wdt_enable(wdt, false);
if (ret < 0)
return ret;

@@ -760,7 +793,7 @@ static int s3c2410wdt_resume(struct device *dev)
writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);

- ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
+ ret = s3c2410wdt_enable(wdt, true);
if (ret < 0)
return ret;

--
2.30.2

2021-11-08 16:25:55

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v3 02/12] dt-bindings: watchdog: Document Exynos850 watchdog bindings

On 07/11/2021 21:29, Sam Protsenko wrote:
> Exynos850 SoC has two CPU clusters:
> - cluster 0: contains CPUs #0, #1, #2, #3
> - cluster 1: contains CPUs #4, #5, #6, #7
>
> Each cluster has its own dedicated watchdog timer. Those WDT instances
> are controlled using different bits in PMU registers, new
> "samsung,index" property is added to tell the driver which bits to use
> for defined watchdog node.
>
> Also on Exynos850 the peripheral clock and the source clock are two
> different clocks. Provide a way to specify two clocks in watchdog device
> tree node.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> ---
> Changes in v3:
> - Renamed "samsung,index" property to more descriptive
> "samsung,cluster-index"
> - Disabled "samsung,cluster-index" property for SoCs other than
> Exynos850
>
> Changes in v2:
> - Stated explicitly that Exynos850 driver requires 2 clocks
> - Used single compatible for Exynos850
> - Added "index" property to specify CPU cluster index
> - Fixed a typo in commit message: dedicater -> dedicated
>
> .../bindings/watchdog/samsung-wdt.yaml | 45 +++++++++++++++++--
> 1 file changed, 41 insertions(+), 4 deletions(-)
>


Reviewed-by: Krzysztof Kozlowski <[email protected]>


Best regards,
Krzysztof

2021-11-08 19:10:09

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v3 10/12] watchdog: s3c2410: Support separate source clock

On 07/11/2021 21:29, Sam Protsenko wrote:
> Right now all devices supported in the driver have the single clock: it
> acts simultaneously as a bus clock (providing register interface
> clocking) and source clock (driving watchdog counter). Some newer Exynos
> chips, like Exynos850, have two separate clocks for that. In that case
> two clocks will be passed to the driver from the resource provider, e.g.
> Device Tree. Provide necessary infrastructure to support that case:
> - use source clock's rate for all timer related calculations
> - use bus clock to gate/ungate the register interface
>
> All devices that use the single clock are kept intact: if only one clock
> is passed from Device Tree, it will be used for both purposes as before.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> ---
> Changes in v3:
> - Removed has_src_clk field: clk framework can handle NULL clk; added
> s3c2410wdt_get_freq() function instead, to figure out which clock to
> use for getting the rate
>
> Changes in v2:
> - Reworded commit message to be more formal
> - Used separate "has_src_clk" trait to tell if source clock is present
> - Renamed clock variables to match their purpose
> - Removed caching source clock rate, obtaining it in place each time
> instead
> - Renamed err labels for more consistency
>
> drivers/watchdog/s3c2410_wdt.c | 56 +++++++++++++++++++++++++---------
> 1 file changed, 41 insertions(+), 15 deletions(-)
>


Reviewed-by: Krzysztof Kozlowski <[email protected]>


Best regards,
Krzysztof

2021-11-12 22:43:27

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v3 02/12] dt-bindings: watchdog: Document Exynos850 watchdog bindings

On Sun, 07 Nov 2021 22:29:33 +0200, Sam Protsenko wrote:
> Exynos850 SoC has two CPU clusters:
> - cluster 0: contains CPUs #0, #1, #2, #3
> - cluster 1: contains CPUs #4, #5, #6, #7
>
> Each cluster has its own dedicated watchdog timer. Those WDT instances
> are controlled using different bits in PMU registers, new
> "samsung,index" property is added to tell the driver which bits to use
> for defined watchdog node.
>
> Also on Exynos850 the peripheral clock and the source clock are two
> different clocks. Provide a way to specify two clocks in watchdog device
> tree node.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> ---
> Changes in v3:
> - Renamed "samsung,index" property to more descriptive
> "samsung,cluster-index"
> - Disabled "samsung,cluster-index" property for SoCs other than
> Exynos850
>
> Changes in v2:
> - Stated explicitly that Exynos850 driver requires 2 clocks
> - Used single compatible for Exynos850
> - Added "index" property to specify CPU cluster index
> - Fixed a typo in commit message: dedicater -> dedicated
>
> .../bindings/watchdog/samsung-wdt.yaml | 45 +++++++++++++++++--
> 1 file changed, 41 insertions(+), 4 deletions(-)
>

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

2021-11-17 13:34:13

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 02/12] dt-bindings: watchdog: Document Exynos850 watchdog bindings

On Sun, Nov 07, 2021 at 10:29:33PM +0200, Sam Protsenko wrote:
> Exynos850 SoC has two CPU clusters:
> - cluster 0: contains CPUs #0, #1, #2, #3
> - cluster 1: contains CPUs #4, #5, #6, #7
>
> Each cluster has its own dedicated watchdog timer. Those WDT instances
> are controlled using different bits in PMU registers, new
> "samsung,index" property is added to tell the driver which bits to use
> for defined watchdog node.
>
> Also on Exynos850 the peripheral clock and the source clock are two
> different clocks. Provide a way to specify two clocks in watchdog device
> tree node.
>
> Signed-off-by: Sam Protsenko <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Renamed "samsung,index" property to more descriptive
> "samsung,cluster-index"
> - Disabled "samsung,cluster-index" property for SoCs other than
> Exynos850
>
> Changes in v2:
> - Stated explicitly that Exynos850 driver requires 2 clocks
> - Used single compatible for Exynos850
> - Added "index" property to specify CPU cluster index
> - Fixed a typo in commit message: dedicater -> dedicated
>
> .../bindings/watchdog/samsung-wdt.yaml | 45 +++++++++++++++++--
> 1 file changed, 41 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
> index 93cd77a6e92c..b08373336b16 100644
> --- a/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
> +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.yaml
> @@ -22,25 +22,32 @@ properties:
> - samsung,exynos5250-wdt # for Exynos5250
> - samsung,exynos5420-wdt # for Exynos5420
> - samsung,exynos7-wdt # for Exynos7
> + - samsung,exynos850-wdt # for Exynos850
>
> reg:
> maxItems: 1
>
> clocks:
> - maxItems: 1
> + minItems: 1
> + maxItems: 2
>
> clock-names:
> - items:
> - - const: watchdog
> + minItems: 1
> + maxItems: 2
>
> interrupts:
> maxItems: 1
>
> + samsung,cluster-index:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description:
> + Index of CPU cluster on which watchdog is running (in case of Exynos850)
> +
> samsung,syscon-phandle:
> $ref: /schemas/types.yaml#/definitions/phandle
> description:
> Phandle to the PMU system controller node (in case of Exynos5250,
> - Exynos5420 and Exynos7).
> + Exynos5420, Exynos7 and Exynos850).
>
> required:
> - compatible
> @@ -59,9 +66,39 @@ allOf:
> - samsung,exynos5250-wdt
> - samsung,exynos5420-wdt
> - samsung,exynos7-wdt
> + - samsung,exynos850-wdt
> then:
> required:
> - samsung,syscon-phandle
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - samsung,exynos850-wdt
> + then:
> + properties:
> + clocks:
> + items:
> + - description: Bus clock, used for register interface
> + - description: Source clock (driving watchdog counter)
> + clock-names:
> + items:
> + - const: watchdog
> + - const: watchdog_src
> + samsung,cluster-index:
> + enum: [0, 1]
> + required:
> + - samsung,cluster-index
> + else:
> + properties:
> + clocks:
> + items:
> + - description: Bus clock, which is also a source clock
> + clock-names:
> + items:
> + - const: watchdog
> + samsung,cluster-index: false
>
> unevaluatedProperties: false
>
> --
> 2.30.2
>

2021-11-17 13:34:46

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 03/12] watchdog: s3c2410: Fail probe if can't find valid timeout

On Sun, Nov 07, 2021 at 10:29:34PM +0200, Sam Protsenko wrote:
> Driver can't work properly if there no valid timeout was found in
> s3c2410wdt_set_heartbeat(). Ideally, that function should be reworked in
> a way that it's always able to find some valid timeout. As a temporary
> solution let's for now just fail the driver probe in case the valid
> timeout can't be found in s3c2410wdt_set_heartbeat() function.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reported-by: Guenter Roeck <[email protected]>
> Suggested-by: Guenter Roeck <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - (none): it's a new patch
>
> drivers/watchdog/s3c2410_wdt.c | 21 ++++++++++-----------
> 1 file changed, 10 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 2395f353e52d..00421cf22556 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -515,7 +515,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> struct s3c2410_wdt *wdt;
> struct resource *wdt_irq;
> unsigned int wtcon;
> - int started = 0;
> int ret;
>
> wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
> @@ -581,15 +580,15 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
> wdt->wdt_device.timeout);
> if (ret) {
> - started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
> - S3C2410_WATCHDOG_DEFAULT_TIME);
> -
> - if (started == 0)
> - dev_info(dev,
> - "tmr_margin value out of range, default %d used\n",
> + ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
> + S3C2410_WATCHDOG_DEFAULT_TIME);
> + if (ret == 0) {
> + dev_warn(dev, "tmr_margin value out of range, default %d used\n",
> S3C2410_WATCHDOG_DEFAULT_TIME);
> - else
> - dev_info(dev, "default timer value is out of range, cannot start\n");
> + } else {
> + dev_err(dev, "failed to use default timeout\n");
> + goto err_cpufreq;
> + }
> }
>
> ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
> @@ -613,10 +612,10 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> if (ret < 0)
> goto err_unregister;
>
> - if (tmr_atboot && started == 0) {
> + if (tmr_atboot) {
> dev_info(dev, "starting watchdog timer\n");
> s3c2410wdt_start(&wdt->wdt_device);
> - } else if (!tmr_atboot) {
> + } else {
> /* if we're not enabling the watchdog, then ensure it is
> * disabled if it has been left running from the bootloader
> * or other source */
> --
> 2.30.2
>

2021-11-17 13:35:25

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 04/12] watchdog: s3c2410: Let kernel kick watchdog

On Sun, Nov 07, 2021 at 10:29:35PM +0200, Sam Protsenko wrote:
> When "tmr_atboot" module param is set, the watchdog is started in
> driver's probe. In that case, also set WDOG_HW_RUNNING bit to let
> watchdog core driver know it's running. This way watchdog core can kick
> the watchdog for us (if CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED option is
> enabled), until user space takes control.
>
> WDOG_HW_RUNNING bit must be set before registering the watchdog. So the
> "tmr_atboot" handling code is moved before watchdog registration, to
> avoid performing the same check twice. This is also logical because
> WDOG_HW_RUNNING bit makes WDT core expect actually running watchdog.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - Added explanation on moving the code block to commit message
> - [PATCH 03/12] handles the case when tmr_atboot is present but valid
> timeout wasn't found
>
> drivers/watchdog/s3c2410_wdt.c | 26 +++++++++++++++-----------
> 1 file changed, 15 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 00421cf22556..0845c05034a1 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -604,6 +604,21 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
> wdt->wdt_device.parent = dev;
>
> + /*
> + * If "tmr_atboot" param is non-zero, start the watchdog right now. Also
> + * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
> + *
> + * If we're not enabling the watchdog, then ensure it is disabled if it
> + * has been left running from the bootloader or other source.
> + */
> + if (tmr_atboot) {
> + dev_info(dev, "starting watchdog timer\n");
> + s3c2410wdt_start(&wdt->wdt_device);
> + set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
> + } else {
> + s3c2410wdt_stop(&wdt->wdt_device);
> + }
> +
> ret = watchdog_register_device(&wdt->wdt_device);
> if (ret)
> goto err_cpufreq;
> @@ -612,17 +627,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> if (ret < 0)
> goto err_unregister;
>
> - if (tmr_atboot) {
> - dev_info(dev, "starting watchdog timer\n");
> - s3c2410wdt_start(&wdt->wdt_device);
> - } else {
> - /* if we're not enabling the watchdog, then ensure it is
> - * disabled if it has been left running from the bootloader
> - * or other source */
> -
> - s3c2410wdt_stop(&wdt->wdt_device);
> - }
> -
> platform_set_drvdata(pdev, wdt);
>
> /* print out a statement of readiness */
> --
> 2.30.2
>

2021-11-17 13:35:35

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 05/12] watchdog: s3c2410: Make reset disable register optional

On Sun, Nov 07, 2021 at 10:29:36PM +0200, Sam Protsenko wrote:
> On new Exynos chips (e.g. Exynos850 and Exynos9) the
> AUTOMATIC_WDT_RESET_DISABLE register was removed, and its value can be
> thought of as "always 0x0". Add correspondig quirk bit, so that the
> driver can omit accessing it if it's not present.
>
> This commit doesn't bring any functional change to existing devices, but
> merely provides an infrastructure for upcoming chips support.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Aligned arguments with opening parentheses
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - Used quirks instead of callbacks for all added PMU registers
> - Used BIT() macro
> - Extracted splitting the s3c2410wdt_mask_and_disable_reset() function
> to separate patch
> - Extracted cleanup code to separate patch to minimize changes and
> ease the review and porting
>
> drivers/watchdog/s3c2410_wdt.c | 22 +++++++++++++---------
> 1 file changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 0845c05034a1..2cc4923a98a5 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -59,10 +59,12 @@
> #define QUIRK_HAS_PMU_CONFIG (1 << 0)
> #define QUIRK_HAS_RST_STAT (1 << 1)
> #define QUIRK_HAS_WTCLRINT_REG (1 << 2)
> +#define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
>
> /* These quirks require that we have a PMU register map */
> #define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
> - QUIRK_HAS_RST_STAT)
> + QUIRK_HAS_RST_STAT | \
> + QUIRK_HAS_PMU_AUTO_DISABLE)
>
> static bool nowayout = WATCHDOG_NOWAYOUT;
> static int tmr_margin;
> @@ -137,7 +139,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 20,
> .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG,
> + | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
> @@ -147,7 +149,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 9,
> .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG,
> + | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct s3c2410_wdt_variant drv_data_exynos7 = {
> @@ -157,7 +159,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 23, /* A57 WDTRESET */
> .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG,
> + | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct of_device_id s3c2410_wdt_match[] = {
> @@ -213,11 +215,13 @@ static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> if (mask)
> val = mask_val;
>
> - ret = regmap_update_bits(wdt->pmureg,
> - wdt->drv_data->disable_reg,
> - mask_val, val);
> - if (ret < 0)
> - goto error;
> + if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
> + ret = regmap_update_bits(wdt->pmureg,
> + wdt->drv_data->disable_reg, mask_val,
> + val);
> + if (ret < 0)
> + goto error;
> + }
>
> ret = regmap_update_bits(wdt->pmureg,
> wdt->drv_data->mask_reset_reg,
> --
> 2.30.2
>

2021-11-17 13:35:46

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 06/12] watchdog: s3c2410: Extract disable and mask code into separate functions

On Sun, Nov 07, 2021 at 10:29:37PM +0200, Sam Protsenko wrote:
> The s3c2410wdt_mask_and_disable_reset() function content is bound to be
> changed further. Prepare it for upcoming changes by splitting into
> separate "mask reset" and "disable reset" functions. But keep
> s3c2410wdt_mask_and_disable_reset() function present as a facade.
>
> This commit doesn't bring any functional change to existing devices, but
> merely provides an infrastructure for upcoming chips support.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - (none): it's a new patch
>
> drivers/watchdog/s3c2410_wdt.c | 54 ++++++++++++++++++++++------------
> 1 file changed, 35 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 2cc4923a98a5..4ac0a30e835e 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -202,37 +202,53 @@ static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
> return container_of(nb, struct s3c2410_wdt, freq_transition);
> }
>
> -static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> +static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
> {
> + const u32 mask_val = BIT(wdt->drv_data->mask_bit);
> + const u32 val = mask ? mask_val : 0;
> int ret;
> - u32 mask_val = 1 << wdt->drv_data->mask_bit;
> - u32 val = 0;
>
> - /* No need to do anything if no PMU CONFIG needed */
> - if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
> - return 0;
> + ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
> + mask_val, val);
> + if (ret < 0)
> + dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
>
> - if (mask)
> - val = mask_val;
> + return ret;
> +}
>
> - if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
> - ret = regmap_update_bits(wdt->pmureg,
> - wdt->drv_data->disable_reg, mask_val,
> - val);
> - if (ret < 0)
> - goto error;
> - }
> +static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
> +{
> + const u32 mask_val = BIT(wdt->drv_data->mask_bit);
> + const u32 val = mask ? mask_val : 0;
> + int ret;
>
> - ret = regmap_update_bits(wdt->pmureg,
> - wdt->drv_data->mask_reset_reg,
> - mask_val, val);
> - error:
> + ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
> + mask_val, val);
> if (ret < 0)
> dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
>
> return ret;
> }
>
> +static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> +{
> + int ret;
> +
> + if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
> + ret = s3c2410wdt_disable_wdt_reset(wdt, mask);
> + if (ret < 0)
> + return ret;
> + }
> +
> + if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG) {
> + ret = s3c2410wdt_mask_wdt_reset(wdt, mask);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
> {
> struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
> --
> 2.30.2
>

2021-11-17 13:35:59

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 07/12] watchdog: s3c2410: Implement a way to invert mask reg value

On Sun, Nov 07, 2021 at 10:29:38PM +0200, Sam Protsenko wrote:
> On new Exynos chips (like Exynos850) the MASK_WDT_RESET_REQUEST register
> is replaced with CLUSTERx_NONCPU_INT_EN, and its mask bit value meaning
> was reversed: for new register the bit value "1" means "Interrupt
> enabled", while for MASK_WDT_RESET_REQUEST register "1" means "Mask the
> interrupt" (i.e. "Interrupt disabled").
>
> Introduce "mask_reset_inv" boolean field in driver data structure; when
> that field is "true", mask register handling function will invert the
> value before setting it to the register.
>
> This commit doesn't bring any functional change to existing devices, but
> merely provides an infrastructure for upcoming chips support.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - (none): it's a new patch
>
> drivers/watchdog/s3c2410_wdt.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 4ac0a30e835e..2a61b6ea5602 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -92,6 +92,7 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
> * timer reset functionality.
> * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
> * timer reset functionality.
> + * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
> * @mask_bit: Bit number for the watchdog timer in the disable register and the
> * mask reset register.
> * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
> @@ -103,6 +104,7 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
> struct s3c2410_wdt_variant {
> int disable_reg;
> int mask_reset_reg;
> + bool mask_reset_inv;
> int mask_bit;
> int rst_stat_reg;
> int rst_stat_bit;
> @@ -219,7 +221,8 @@ static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
> static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
> {
> const u32 mask_val = BIT(wdt->drv_data->mask_bit);
> - const u32 val = mask ? mask_val : 0;
> + const bool val_inv = wdt->drv_data->mask_reset_inv;
> + const u32 val = (mask ^ val_inv) ? mask_val : 0;
> int ret;
>
> ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
> --
> 2.30.2
>

2021-11-17 13:36:20

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 08/12] watchdog: s3c2410: Add support for WDT counter enable register

On Sun, Nov 07, 2021 at 10:29:39PM +0200, Sam Protsenko wrote:
> On new Exynos chips (e.g. Exynos850) new CLUSTERx_NONCPU_OUT register is
> introduced, where CNT_EN_WDT bit must be enabled to make watchdog
> counter running. Add corresponding quirk and proper infrastructure to
> handle that register if the quirk is set.
>
> This commit doesn't bring any functional change to existing devices, but
> merely provides an infrastructure for upcoming chips support.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - Used quirks instead of callbacks for all added PMU registers
> - Used BIT() macro
> - Extracted cleanup code to separate patch to minimize changes and
> ease the review and porting
>
> drivers/watchdog/s3c2410_wdt.c | 28 +++++++++++++++++++++++++++-
> 1 file changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index 2a61b6ea5602..ec341c876225 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -60,11 +60,13 @@
> #define QUIRK_HAS_RST_STAT (1 << 1)
> #define QUIRK_HAS_WTCLRINT_REG (1 << 2)
> #define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
> +#define QUIRK_HAS_PMU_CNT_EN (1 << 4)
>
> /* These quirks require that we have a PMU register map */
> #define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
> QUIRK_HAS_RST_STAT | \
> - QUIRK_HAS_PMU_AUTO_DISABLE)
> + QUIRK_HAS_PMU_AUTO_DISABLE | \
> + QUIRK_HAS_PMU_CNT_EN)
>
> static bool nowayout = WATCHDOG_NOWAYOUT;
> static int tmr_margin;
> @@ -98,6 +100,8 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to
> * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
> * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
> * reset.
> + * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
> + * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
> * @quirks: A bitfield of quirks.
> */
>
> @@ -108,6 +112,8 @@ struct s3c2410_wdt_variant {
> int mask_bit;
> int rst_stat_reg;
> int rst_stat_bit;
> + int cnt_en_reg;
> + int cnt_en_bit;
> u32 quirks;
> };
>
> @@ -233,6 +239,20 @@ static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
> return ret;
> }
>
> +static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
> +{
> + const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
> + const u32 val = en ? mask_val : 0;
> + int ret;
> +
> + ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
> + mask_val, val);
> + if (ret < 0)
> + dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
> +
> + return ret;
> +}
> +
> static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> {
> int ret;
> @@ -249,6 +269,12 @@ static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> return ret;
> }
>
> + if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
> + ret = s3c2410wdt_enable_counter(wdt, !mask);
> + if (ret < 0)
> + return ret;
> + }
> +
> return 0;
> }
>
> --
> 2.30.2
>

2021-11-17 13:36:57

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 09/12] watchdog: s3c2410: Cleanup PMU related code

On Sun, Nov 07, 2021 at 10:29:40PM +0200, Sam Protsenko wrote:
> Now that PMU enablement code was extended for new Exynos SoCs, it
> doesn't look very cohesive and consistent anymore. Do a bit of renaming,
> grouping and style changes, to make it look good again. While at it, add
> quirks documentation as well.
>
> No functional change, just a refactoring commit.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added quirks documentation
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - (none): it's a new patch
>
> drivers/watchdog/s3c2410_wdt.c | 83 ++++++++++++++++++++++++----------
> 1 file changed, 58 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index ec341c876225..f211be8bf976 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -56,17 +56,51 @@
> #define EXYNOS5_RST_STAT_REG_OFFSET 0x0404
> #define EXYNOS5_WDT_DISABLE_REG_OFFSET 0x0408
> #define EXYNOS5_WDT_MASK_RESET_REG_OFFSET 0x040c
> -#define QUIRK_HAS_PMU_CONFIG (1 << 0)
> -#define QUIRK_HAS_RST_STAT (1 << 1)
> -#define QUIRK_HAS_WTCLRINT_REG (1 << 2)
> +
> +/**
> + * Quirk flags for different Samsung watchdog IP-cores.
> + *
> + * This driver supports multiple Samsung SoCs, each of which might have
> + * different set of registers and features supported. As watchdog block
> + * sometimes requires modifying PMU registers for proper functioning, register
> + * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
> + * flags described below serve the purpose of telling the driver about mentioned
> + * SoC traits, and can be specified in driver data for each particular supported
> + * device.
> + *
> + * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
> + * clear the interrupt once the interrupt service routine is complete. It's
> + * write-only, writing any values to this register clears the interrupt, but
> + * reading is not permitted.
> + *
> + * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
> + * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
> + * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
> + * inverted compared to the former one.
> + *
> + * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
> + * which contains bits indicating the reason for most recent CPU reset. If
> + * present, driver will use this register to check if previous reboot was due to
> + * watchdog timer reset.
> + *
> + * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
> + * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
> + * corresponding processor is in reset state.
> + *
> + * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
> + * with "watchdog counter enable" bit. That bit should be set to make watchdog
> + * counter running.
> + */
> +#define QUIRK_HAS_WTCLRINT_REG (1 << 0)
> +#define QUIRK_HAS_PMU_MASK_RESET (1 << 1)
> +#define QUIRK_HAS_PMU_RST_STAT (1 << 2)
> #define QUIRK_HAS_PMU_AUTO_DISABLE (1 << 3)
> #define QUIRK_HAS_PMU_CNT_EN (1 << 4)
>
> /* These quirks require that we have a PMU register map */
> -#define QUIRKS_HAVE_PMUREG (QUIRK_HAS_PMU_CONFIG | \
> - QUIRK_HAS_RST_STAT | \
> - QUIRK_HAS_PMU_AUTO_DISABLE | \
> - QUIRK_HAS_PMU_CNT_EN)
> +#define QUIRKS_HAVE_PMUREG \
> + (QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
> + QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
>
> static bool nowayout = WATCHDOG_NOWAYOUT;
> static int tmr_margin;
> @@ -146,8 +180,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5250 = {
> .mask_bit = 20,
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 20,
> - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
> + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
> @@ -156,8 +190,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
> .mask_bit = 0,
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 9,
> - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
> + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct s3c2410_wdt_variant drv_data_exynos7 = {
> @@ -166,8 +200,8 @@ static const struct s3c2410_wdt_variant drv_data_exynos7 = {
> .mask_bit = 23,
> .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
> .rst_stat_bit = 23, /* A57 WDTRESET */
> - .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
> - | QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_AUTO_DISABLE,
> + .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
> + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
> };
>
> static const struct of_device_id s3c2410_wdt_match[] = {
> @@ -253,24 +287,24 @@ static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
> return ret;
> }
>
> -static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
> +static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
> {
> int ret;
>
> if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
> - ret = s3c2410wdt_disable_wdt_reset(wdt, mask);
> + ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
> if (ret < 0)
> return ret;
> }
>
> - if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG) {
> - ret = s3c2410wdt_mask_wdt_reset(wdt, mask);
> + if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
> + ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
> if (ret < 0)
> return ret;
> }
>
> if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
> - ret = s3c2410wdt_enable_counter(wdt, !mask);
> + ret = s3c2410wdt_enable_counter(wdt, en);
> if (ret < 0)
> return ret;
> }
> @@ -531,7 +565,7 @@ static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
> unsigned int rst_stat;
> int ret;
>
> - if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
> + if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
> return 0;
>
> ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
> @@ -672,7 +706,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> if (ret)
> goto err_cpufreq;
>
> - ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
> + ret = s3c2410wdt_enable(wdt, true);
> if (ret < 0)
> goto err_unregister;
>
> @@ -707,7 +741,7 @@ static int s3c2410wdt_remove(struct platform_device *dev)
> int ret;
> struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
>
> - ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
> + ret = s3c2410wdt_enable(wdt, false);
> if (ret < 0)
> return ret;
>
> @@ -724,8 +758,7 @@ static void s3c2410wdt_shutdown(struct platform_device *dev)
> {
> struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
>
> - s3c2410wdt_mask_and_disable_reset(wdt, true);
> -
> + s3c2410wdt_enable(wdt, false);
> s3c2410wdt_stop(&wdt->wdt_device);
> }
>
> @@ -740,7 +773,7 @@ static int s3c2410wdt_suspend(struct device *dev)
> wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
> wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
>
> - ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
> + ret = s3c2410wdt_enable(wdt, false);
> if (ret < 0)
> return ret;
>
> @@ -760,7 +793,7 @@ static int s3c2410wdt_resume(struct device *dev)
> writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
> writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
>
> - ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
> + ret = s3c2410wdt_enable(wdt, true);
> if (ret < 0)
> return ret;
>
> --
> 2.30.2
>

2021-11-17 13:37:09

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 10/12] watchdog: s3c2410: Support separate source clock

On Sun, Nov 07, 2021 at 10:29:41PM +0200, Sam Protsenko wrote:
> Right now all devices supported in the driver have the single clock: it
> acts simultaneously as a bus clock (providing register interface
> clocking) and source clock (driving watchdog counter). Some newer Exynos
> chips, like Exynos850, have two separate clocks for that. In that case
> two clocks will be passed to the driver from the resource provider, e.g.
> Device Tree. Provide necessary infrastructure to support that case:
> - use source clock's rate for all timer related calculations
> - use bus clock to gate/ungate the register interface
>
> All devices that use the single clock are kept intact: if only one clock
> is passed from Device Tree, it will be used for both purposes as before.
>
> Signed-off-by: Sam Protsenko <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Removed has_src_clk field: clk framework can handle NULL clk; added
> s3c2410wdt_get_freq() function instead, to figure out which clock to
> use for getting the rate
>
> Changes in v2:
> - Reworded commit message to be more formal
> - Used separate "has_src_clk" trait to tell if source clock is present
> - Renamed clock variables to match their purpose
> - Removed caching source clock rate, obtaining it in place each time
> instead
> - Renamed err labels for more consistency
>
> drivers/watchdog/s3c2410_wdt.c | 56 +++++++++++++++++++++++++---------
> 1 file changed, 41 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index f211be8bf976..f31bc765a8a5 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -153,7 +153,8 @@ struct s3c2410_wdt_variant {
>
> struct s3c2410_wdt {
> struct device *dev;
> - struct clk *clock;
> + struct clk *bus_clk; /* for register interface (PCLK) */
> + struct clk *src_clk; /* for WDT counter */
> void __iomem *reg_base;
> unsigned int count;
> spinlock_t lock;
> @@ -231,9 +232,14 @@ MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
>
> /* functions */
>
> -static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
> +static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
> {
> - unsigned long freq = clk_get_rate(clock);
> + return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
> +}
> +
> +static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
> +{
> + const unsigned long freq = s3c2410wdt_get_freq(wdt);
>
> return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
> / S3C2410_WTCON_MAXDIV);
> @@ -383,7 +389,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
> unsigned int timeout)
> {
> struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
> - unsigned long freq = clk_get_rate(wdt->clock);
> + unsigned long freq = s3c2410wdt_get_freq(wdt);
> unsigned int count;
> unsigned int divisor = 1;
> unsigned long wtcon;
> @@ -632,26 +638,42 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> goto err;
> }
>
> - wdt->clock = devm_clk_get(dev, "watchdog");
> - if (IS_ERR(wdt->clock)) {
> - dev_err(dev, "failed to find watchdog clock source\n");
> - ret = PTR_ERR(wdt->clock);
> + wdt->bus_clk = devm_clk_get(dev, "watchdog");
> + if (IS_ERR(wdt->bus_clk)) {
> + dev_err(dev, "failed to find bus clock\n");
> + ret = PTR_ERR(wdt->bus_clk);
> goto err;
> }
>
> - ret = clk_prepare_enable(wdt->clock);
> + ret = clk_prepare_enable(wdt->bus_clk);
> if (ret < 0) {
> - dev_err(dev, "failed to enable clock\n");
> + dev_err(dev, "failed to enable bus clock\n");
> return ret;
> }
>
> + /*
> + * "watchdog_src" clock is optional; if it's not present -- just skip it
> + * and use "watchdog" clock as both bus and source clock.
> + */
> + wdt->src_clk = devm_clk_get(dev, "watchdog_src");
> + if (!IS_ERR(wdt->src_clk)) {
> + ret = clk_prepare_enable(wdt->src_clk);
> + if (ret < 0) {
> + dev_err(dev, "failed to enable source clock\n");
> + ret = PTR_ERR(wdt->src_clk);
> + goto err_bus_clk;
> + }
> + } else {
> + wdt->src_clk = NULL;
> + }
> +
> wdt->wdt_device.min_timeout = 1;
> - wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
> + wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
>
> ret = s3c2410wdt_cpufreq_register(wdt);
> if (ret < 0) {
> dev_err(dev, "failed to register cpufreq\n");
> - goto err_clk;
> + goto err_src_clk;
> }
>
> watchdog_set_drvdata(&wdt->wdt_device, wdt);
> @@ -729,8 +751,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> err_cpufreq:
> s3c2410wdt_cpufreq_deregister(wdt);
>
> - err_clk:
> - clk_disable_unprepare(wdt->clock);
> + err_src_clk:
> + clk_disable_unprepare(wdt->src_clk);
> +
> + err_bus_clk:
> + clk_disable_unprepare(wdt->bus_clk);
>
> err:
> return ret;
> @@ -749,7 +774,8 @@ static int s3c2410wdt_remove(struct platform_device *dev)
>
> s3c2410wdt_cpufreq_deregister(wdt);
>
> - clk_disable_unprepare(wdt->clock);
> + clk_disable_unprepare(wdt->src_clk);
> + clk_disable_unprepare(wdt->bus_clk);
>
> return 0;
> }
> --
> 2.30.2
>

2021-11-17 13:37:36

by Guenter Roeck

[permalink] [raw]
Subject: Re: [PATCH v3 11/12] watchdog: s3c2410: Remove superfluous err label

On Sun, Nov 07, 2021 at 10:29:42PM +0200, Sam Protsenko wrote:
> 'err' label in probe function is not really need, it just returns.
> Remove it and replace all 'goto' statements with actual returns in
> place.
>
> No functional change here, just a cleanup patch.
>
> Signed-off-by: Sam Protsenko <[email protected]>
> Reviewed-by: Krzysztof Kozlowski <[email protected]>

Reviewed-by: Guenter Roeck <[email protected]>

> ---
> Changes in v3:
> - Added R-b tag by Krzysztof Kozlowski
>
> Changes in v2:
> - (none): it's a new patch
>
> drivers/watchdog/s3c2410_wdt.c | 13 ++++---------
> 1 file changed, 4 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index f31bc765a8a5..96aa5d9c6ed4 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -627,22 +627,18 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> if (wdt_irq == NULL) {
> dev_err(dev, "no irq resource specified\n");
> - ret = -ENOENT;
> - goto err;
> + return -ENOENT;
> }
>
> /* get the memory region for the watchdog timer */
> wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
> - if (IS_ERR(wdt->reg_base)) {
> - ret = PTR_ERR(wdt->reg_base);
> - goto err;
> - }
> + if (IS_ERR(wdt->reg_base))
> + return PTR_ERR(wdt->reg_base);
>
> wdt->bus_clk = devm_clk_get(dev, "watchdog");
> if (IS_ERR(wdt->bus_clk)) {
> dev_err(dev, "failed to find bus clock\n");
> - ret = PTR_ERR(wdt->bus_clk);
> - goto err;
> + return PTR_ERR(wdt->bus_clk);
> }
>
> ret = clk_prepare_enable(wdt->bus_clk);
> @@ -757,7 +753,6 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> err_bus_clk:
> clk_disable_unprepare(wdt->bus_clk);
>
> - err:
> return ret;
> }
>
> --
> 2.30.2
>