blob: adbb5aacdc3b5cfc307cc4ff2dd6ee7c3131c7f2 [file] [log] [blame]
Casey Connolly8ea99b82025-04-23 02:19:39 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
4 * Copyright (c) Linaro Ltd. 2024
5 *
6 * Authors:
7 * Casey Connolly <casey.connolly@linaro.org>
8 * Paul Sajna <hello@paulsajna.com>
9 *
10 * Derived from linux/drivers/watchdog/qcom-wdt.c
11 */
12
13#include <dm.h>
14#include <dm/device_compat.h>
15#include <wdt.h>
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +053016#include <clk.h>
Casey Connolly8ea99b82025-04-23 02:19:39 +000017
18#include <asm/io.h>
19
20enum wdt_reg {
21 WDT_RST,
22 WDT_EN,
23 WDT_STS,
24 WDT_BARK_TIME,
25 WDT_BITE_TIME,
26};
27
28struct qcom_wdt_match_data {
29 const u32 *offset;
30};
31
32struct qcom_wdt {
33 void __iomem *base;
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +053034 ulong clk_rate;
Casey Connolly8ea99b82025-04-23 02:19:39 +000035 const u32 *layout;
36};
37
38static const u32 reg_offset_data_kpss[] = {
39 [WDT_RST] = 0x4,
40 [WDT_EN] = 0x8,
41 [WDT_STS] = 0xC,
42 [WDT_BARK_TIME] = 0x10,
43 [WDT_BITE_TIME] = 0x14,
44};
45
46static const struct qcom_wdt_match_data match_data_kpss = {
47 .offset = reg_offset_data_kpss,
48};
49
50static void __iomem *wdt_addr(struct qcom_wdt *wdt, enum wdt_reg reg)
51{
52 return wdt->base + wdt->layout[reg];
53}
54
55int qcom_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
56{
57 struct qcom_wdt *wdt = dev_get_priv(dev);
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +053058 ulong bark_timeout_s = ((timeout_ms - 1) * wdt->clk_rate) / 1000;
59 ulong bite_timeout_s = (timeout_ms * wdt->clk_rate) / 1000;
Casey Connolly8ea99b82025-04-23 02:19:39 +000060
61 writel(0, wdt_addr(wdt, WDT_EN));
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +053062 writel(BIT(0), wdt_addr(wdt, WDT_RST));
63 writel(bark_timeout_s, wdt_addr(wdt, WDT_BARK_TIME));
64 writel(bite_timeout_s, wdt_addr(wdt, WDT_BITE_TIME));
65 writel(BIT(0), wdt_addr(wdt, WDT_EN));
Casey Connolly8ea99b82025-04-23 02:19:39 +000066 if (readl(wdt_addr(wdt, WDT_EN)) != 1) {
67 dev_err(dev, "Failed to enable Qualcomm watchdog!\n");
68 return -EIO;
69 }
70 return 0;
71}
72
73int qcom_wdt_stop(struct udevice *dev)
74{
75 struct qcom_wdt *wdt = dev_get_priv(dev);
76
77 writel(0, wdt_addr(wdt, WDT_EN));
78 if (readl(wdt_addr(wdt, WDT_EN))) {
79 dev_err(dev, "Failed to disable Qualcomm watchdog!\n");
80 return -EIO;
81 }
82
83 return 0;
84}
85
86int qcom_wdt_reset(struct udevice *dev)
87{
88 struct qcom_wdt *wdt = dev_get_priv(dev);
89
90 writel(1, wdt_addr(wdt, WDT_RST));
91 return 0;
92}
93
94static int qcom_wdt_probe(struct udevice *dev)
95{
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +053096 struct clk clk;
97 long rate;
98 int ret;
99
Casey Connolly8ea99b82025-04-23 02:19:39 +0000100 struct qcom_wdt *wdt = dev_get_priv(dev);
101 struct qcom_wdt_match_data *data = (void *)dev_get_driver_data(dev);
102
103 wdt->base = dev_read_addr_ptr(dev);
104 wdt->layout = data->offset;
105
Balaji Selvanathan8682cbc2025-05-27 18:19:26 +0530106 ret = clk_get_by_index(dev, 0, &clk);
107 if (ret)
108 return ret;
109
110 rate = clk_get_rate(&clk);
111 if (rate <= 0)
112 return rate < 0 ? (int)rate : -EINVAL;
113
114 wdt->clk_rate = (ulong)rate;
115
Casey Connolly8ea99b82025-04-23 02:19:39 +0000116 return 0;
117}
118
119static const struct wdt_ops qcom_wdt_ops = {
120 .start = qcom_wdt_start,
121 .stop = qcom_wdt_stop,
122 .reset = qcom_wdt_reset,
123};
124
125static const struct udevice_id qcom_wdt_ids[] = {
126 { .compatible = "qcom,kpss-wdt", .data = (ulong)&match_data_kpss },
127 {}
128};
129
130U_BOOT_DRIVER(qcom_wdt) = {
131 .name = "qcom_wdt",
132 .id = UCLASS_WDT,
133 .of_match = qcom_wdt_ids,
134 .ops = &qcom_wdt_ops,
135 .probe = qcom_wdt_probe,
136 .priv_auto = sizeof(struct qcom_wdt),
137};