mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 12:46:14 +02:00
watchdog: qcom: introduce qcom-wdt driver
Some Qualcomm device vendors decide to turn the watchdog on in the bootloader, resulting in the device being reset if it isn't petted every ~30 seconds. Introduce a driver to keep the watchdog happy and prevent this annoying behaviour. Signed-off-by: Paul Sajna <hello@paulsajna.com> Co-authored-by: Paul Sajna <hello@paulsajna.com> Tested-by: Paul Sajna <hello@paulsajna.com> Reviewed-by: Stefan Roese <sr@denx.de> Acked-by: Sumit Garg <sumit.garg@oss.qualcomm.com> Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org> Link: https://lore.kernel.org/r/20250422-b4-qcom-wdt-v3-1-730d4d5a858d@paulsajna.com Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
This commit is contained in:
parent
cb99e1257b
commit
530764de9f
@ -143,3 +143,5 @@ CONFIG_VIDEO_FONT_16X32=y
|
||||
CONFIG_SYS_WHITE_ON_BLACK=y
|
||||
CONFIG_NO_FB_CLEAR=y
|
||||
CONFIG_VIDEO_SIMPLE=y
|
||||
CONFIG_WDT_QCOM=y
|
||||
CONFIG_WDT=y
|
||||
|
||||
@ -335,6 +335,13 @@ config WDT_K3_RTI_FW_FILE
|
||||
|
||||
endif
|
||||
|
||||
config WDT_QCOM
|
||||
bool "Qualcomm watchdog timer support"
|
||||
depends on WDT && ARCH_SNAPDRAGON
|
||||
help
|
||||
Select this to enable Qualcomm watchdog timer, which can be found on
|
||||
some Qualcomm chips.
|
||||
|
||||
config WDT_RENESAS
|
||||
bool "Renesas watchdog timer support"
|
||||
depends on WDT && R8A779F0
|
||||
|
||||
@ -55,3 +55,4 @@ obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
|
||||
obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
|
||||
obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
|
||||
obj-$(CONFIG_WDT_ADI) += adi_wdt.o
|
||||
obj-$(CONFIG_WDT_QCOM) += qcom-wdt.o
|
||||
|
||||
117
drivers/watchdog/qcom-wdt.c
Normal file
117
drivers/watchdog/qcom-wdt.c
Normal file
@ -0,0 +1,117 @@
|
||||
// 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 <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;
|
||||
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);
|
||||
|
||||
writel(0, wdt_addr(wdt, WDT_EN));
|
||||
writel(1, wdt_addr(wdt, WDT_RST));
|
||||
writel(1, 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 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;
|
||||
|
||||
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),
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user