u-boot/drivers/watchdog/qcom-wdt.c
Balaji Selvanathan 35811a7b0d drivers: watchdog: qcom: Add timeout configuration support in watchdog
This patch enhances the Qualcomm watchdog driver by introducing
support for dynamic timeout configuration. Specifically:

- Calculates and sets the bark and bite timeout values based on
  the clock rate and the requested timeout in milliseconds.
- Adds retrieval of the watchdog clock rate during probe using
  the common clock framework.
- Adds a default timeout value for ARCH_SNAPDRAGON
  in WATCHDOG_TIMEOUT_MSECS.

These changes improve the configurability and accuracy of the
watchdog timer on Qualcomm platforms.

This work builds upon the previous submission:
https://lore.kernel.org/all/20250422-b4-qcom-wdt-v3-1-730d4d5a858d@paulsajna.com/

Signed-off-by: Balaji Selvanathan <balaji.selvanathan@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20250527124926.128413-1-balaji.selvanathan@oss.qualcomm.com
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
2025-06-23 18:50:39 +02:00

138 lines
2.8 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Copyright (c) Linaro Ltd. 2024
*
* Authors:
* Casey Connolly <casey.connolly@linaro.org>
* Paul Sajna <hello@paulsajna.com>
*
* Derived from linux/drivers/watchdog/qcom-wdt.c
*/
#include <dm.h>
#include <dm/device_compat.h>
#include <wdt.h>
#include <clk.h>
#include <asm/io.h>
enum wdt_reg {
WDT_RST,
WDT_EN,
WDT_STS,
WDT_BARK_TIME,
WDT_BITE_TIME,
};
struct qcom_wdt_match_data {
const u32 *offset;
};
struct qcom_wdt {
void __iomem *base;
ulong clk_rate;
const u32 *layout;
};
static const u32 reg_offset_data_kpss[] = {
[WDT_RST] = 0x4,
[WDT_EN] = 0x8,
[WDT_STS] = 0xC,
[WDT_BARK_TIME] = 0x10,
[WDT_BITE_TIME] = 0x14,
};
static const struct qcom_wdt_match_data match_data_kpss = {
.offset = reg_offset_data_kpss,
};
static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg)
{
return wdt->base + wdt->layout[reg];
}
int qcom_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
struct qcom_wdt *wdt = dev_get_priv(dev);
ulong bark_timeout_s = ((timeout_ms - 1) * wdt->clk_rate) / 1000;
ulong bite_timeout_s = (timeout_ms * wdt->clk_rate) / 1000;
writel(0, wdt_addr(wdt, WDT_EN));
writel(BIT(0), wdt_addr(wdt, WDT_RST));
writel(bark_timeout_s, wdt_addr(wdt, WDT_BARK_TIME));
writel(bite_timeout_s, wdt_addr(wdt, WDT_BITE_TIME));
writel(BIT(0), wdt_addr(wdt, WDT_EN));
if (readl(wdt_addr(wdt, WDT_EN)) != 1) {
dev_err(dev, "Failed to enable Qualcomm watchdog!\n");
return -EIO;
}
return 0;
}
int qcom_wdt_stop(struct udevice *dev)
{
struct qcom_wdt *wdt = dev_get_priv(dev);
writel(0, wdt_addr(wdt, WDT_EN));
if (readl(wdt_addr(wdt, WDT_EN))) {
dev_err(dev, "Failed to disable Qualcomm watchdog!\n");
return -EIO;
}
return 0;
}
int qcom_wdt_reset(struct udevice *dev)
{
struct qcom_wdt *wdt = dev_get_priv(dev);
writel(1, wdt_addr(wdt, WDT_RST));
return 0;
}
static int qcom_wdt_probe(struct udevice *dev)
{
struct clk clk;
long rate;
int ret;
struct qcom_wdt *wdt = dev_get_priv(dev);
struct qcom_wdt_match_data *data = (void *)dev_get_driver_data(dev);
wdt->base = dev_read_addr_ptr(dev);
wdt->layout = data->offset;
ret = clk_get_by_index(dev, 0, &clk);
if (ret)
return ret;
rate = clk_get_rate(&clk);
if (rate <= 0)
return rate < 0 ? (int)rate : -EINVAL;
wdt->clk_rate = (ulong)rate;
return 0;
}
static const struct wdt_ops qcom_wdt_ops = {
.start = qcom_wdt_start,
.stop = qcom_wdt_stop,
.reset = qcom_wdt_reset,
};
static const struct udevice_id qcom_wdt_ids[] = {
{ .compatible = "qcom,kpss-wdt", .data = (ulong)&match_data_kpss },
{}
};
U_BOOT_DRIVER(qcom_wdt) = {
.name = "qcom_wdt",
.id = UCLASS_WDT,
.of_match = qcom_wdt_ids,
.ops = &qcom_wdt_ops,
.probe = qcom_wdt_probe,
.priv_auto = sizeof(struct qcom_wdt),
};