1 /*
2  * Copyright (c) 2023 by Rivos Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT lowrisc_opentitan_aontimer
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/util.h>
11 #include <zephyr/drivers/watchdog.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(wdt_opentitan, CONFIG_WDT_LOG_LEVEL);
15 
16 #define OT_REG_WDOG_REGWEN_OFFSET 0x10
17 #define OT_REG_WDOG_CTRL_OFFSET 0x14
18 #define OT_REG_WDOG_BARK_THOLD_OFFSET 0x18
19 #define OT_REG_WDOG_BITE_THOLD_OFFSET 0x1C
20 #define OT_REG_WDOG_COUNT_OFFSET 0x20
21 #define OT_REG_INTR_STATE_OFFSET 0x24
22 
23 struct wdt_ot_aontimer_cfg {
24 	uintptr_t regs;
25 	uint32_t clk_freq;
26 	bool wdog_lock;
27 };
28 
29 struct wdt_ot_aontimer_data {
30 	wdt_callback_t bark;
31 };
32 
ot_aontimer_setup(const struct device * dev,uint8_t options)33 static int ot_aontimer_setup(const struct device *dev, uint8_t options)
34 {
35 	const struct wdt_ot_aontimer_cfg *const cfg = dev->config;
36 	volatile uintptr_t regs = cfg->regs;
37 
38 	sys_write32(0, regs + OT_REG_WDOG_COUNT_OFFSET);
39 	sys_write32(1, regs + OT_REG_WDOG_CTRL_OFFSET);
40 	if (cfg->wdog_lock) {
41 		/* Force a read to ensure the timer was enabled. */
42 		(void) sys_read32(regs + OT_REG_WDOG_CTRL_OFFSET);
43 		sys_write32(0, regs + OT_REG_WDOG_REGWEN_OFFSET);
44 	}
45 	return 0;
46 }
47 
ot_aontimer_disable(const struct device * dev)48 static int ot_aontimer_disable(const struct device *dev)
49 {
50 	const struct wdt_ot_aontimer_cfg *const cfg = dev->config;
51 	volatile uintptr_t regs = cfg->regs;
52 
53 	if (!sys_read32(regs + OT_REG_WDOG_REGWEN_OFFSET)) {
54 		LOG_ERR("Cannot disable - watchdog settings locked.");
55 		return -EPERM;
56 	}
57 
58 	uint32_t ctrl_val = sys_read32(regs + OT_REG_WDOG_CTRL_OFFSET);
59 
60 	if (!(ctrl_val & BIT(0))) {
61 		return -EFAULT;
62 	}
63 	sys_write32(ctrl_val & ~BIT(0), regs + OT_REG_WDOG_CTRL_OFFSET);
64 
65 	return 0;
66 }
67 
68 /*
69  * The OpenTitan AON Timer includes a multi-level watchdog timer.
70  * While the first stage supports an interrupt callback, the second does not.
71  * The second stage is mandatory to adjust the "bite" time window.
72  *
73  * Some boundaries are enforced to prevent behavior that is technically correct
74  * but is not likely intended.
75  * Bark:
76  * Minimum must be 0. Maximum must be > min.
77  * The bark interrupt occurs at max (or if the timeout is too long to be
78  * supported, the value x s.t. min < x < max and x is the largest valid timeout)
79  * Bite:
80  * Minimum must be >= bark.min, and maximum >= bark.max. If the timeout is too
81  * long to fit, it tries to find the value x s.t. min < x < max where x is the
82  * largest valid timeout.
83  * The bite action occurs max.
84  */
ot_aontimer_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)85 static int ot_aontimer_install_timeout(const struct device *dev,
86 					const struct wdt_timeout_cfg *cfg)
87 {
88 	struct wdt_ot_aontimer_data *data = dev->data;
89 	const struct wdt_ot_aontimer_cfg *const dev_cfg = dev->config;
90 	volatile uintptr_t reg_base = dev_cfg->regs;
91 	const uint64_t max_window = (uint64_t) UINT32_MAX * 1000 / dev_cfg->clk_freq;
92 	uint64_t bite_thold;
93 #ifdef CONFIG_WDT_MULTISTAGE
94 	/* When multistage is selected, add an intermediate bark stage */
95 	uint64_t bark_thold;
96 	struct wdt_timeout_cfg *bite = cfg->next;
97 
98 	if (bite == NULL || bite->window.max < cfg->window.max ||
99 			(uint64_t) bite->window.min > max_window) {
100 		return -EINVAL;
101 	}
102 	/*
103 	 * Flag must be clear for stage 1, and reset SOC for stage 2.
104 	 * CPU not supported
105 	 */
106 	if (cfg->flags != WDT_FLAG_RESET_NONE || bite->flags != WDT_FLAG_RESET_SOC) {
107 		return -ENOTSUP;
108 	}
109 #endif
110 
111 	if (cfg->window.min > cfg->window.max || (uint64_t) cfg->window.min > max_window) {
112 		return -EINVAL;
113 	}
114 
115 	if (!sys_read32(reg_base + OT_REG_WDOG_REGWEN_OFFSET)) {
116 		LOG_ERR("Cannot install timeout - watchdog settings locked.");
117 		return -ENOMEM;
118 	}
119 
120 	/* Watchdog is already enabled! */
121 	if (sys_read32(reg_base + OT_REG_WDOG_CTRL_OFFSET) & BIT(0)) {
122 		return -EBUSY;
123 	}
124 
125 #ifdef CONFIG_WDT_MULTISTAGE
126 	/* Force 64-bit ops to ensure thresholds fits in the timer reg. */
127 	bark_thold = ((uint64_t) cfg->window.max * dev_cfg->clk_freq / 1000);
128 	bite_thold = ((uint64_t) bite->window.max * dev_cfg->clk_freq / 1000);
129 	/* Saturate these config values; min is verified to be < max_window */
130 	if (bark_thold > UINT32_MAX) {
131 		bark_thold = UINT32_MAX;
132 	}
133 	if (bite_thold > UINT32_MAX) {
134 		bite_thold = UINT32_MAX;
135 	}
136 	data->bark = cfg->callback;
137 	sys_write32((uint32_t) bark_thold, reg_base + OT_REG_WDOG_BARK_THOLD_OFFSET);
138 	sys_write32((uint32_t) bite_thold, reg_base + OT_REG_WDOG_BITE_THOLD_OFFSET);
139 #else
140 	bite_thold = ((uint64_t) cfg->window.max * dev_cfg->clk_freq / 1000);
141 	/* Saturate this config value; min is verified to be < max_window */
142 	if (bite_thold > UINT32_MAX) {
143 		bite_thold = UINT32_MAX;
144 	}
145 	if (cfg->flags == WDT_FLAG_RESET_NONE) {
146 		/* Set bite -> bark, so we generate an interrupt instead of resetting */
147 		sys_write32((uint32_t) bite_thold, reg_base + OT_REG_WDOG_BARK_THOLD_OFFSET);
148 		/* Disable bite by writing it to max. Edge case is the bark = max. */
149 		sys_write32(UINT32_MAX, reg_base + OT_REG_WDOG_BITE_THOLD_OFFSET);
150 		data->bark = cfg->callback;
151 	} else {
152 		data->bark = NULL;
153 		/* Effectively disable bark by setting it to max */
154 		sys_write32(UINT32_MAX, reg_base + OT_REG_WDOG_BARK_THOLD_OFFSET);
155 		sys_write32((uint32_t) bite_thold, reg_base + OT_REG_WDOG_BITE_THOLD_OFFSET);
156 	}
157 #endif
158 
159 	return 0;
160 }
161 
ot_aontimer_feed(const struct device * dev,int channel_id)162 static int ot_aontimer_feed(const struct device *dev, int channel_id)
163 {
164 	ARG_UNUSED(channel_id);
165 	const struct wdt_ot_aontimer_cfg *const cfg = dev->config;
166 	volatile uintptr_t regs = cfg->regs;
167 
168 	sys_write32(0, regs + OT_REG_WDOG_COUNT_OFFSET);
169 
170 	/* Deassert the interrupt line */
171 	sys_write32(BIT(1), regs + OT_REG_INTR_STATE_OFFSET);
172 	return 0;
173 }
174 
wdt_ot_isr(const struct device * dev)175 static void wdt_ot_isr(const struct device *dev)
176 {
177 	const struct wdt_ot_aontimer_cfg *const cfg = dev->config;
178 	struct wdt_ot_aontimer_data *data = dev->data;
179 	volatile uintptr_t regs = cfg->regs;
180 
181 	if (data->bark != NULL) {
182 		data->bark(dev, 0);
183 	}
184 
185 	/* Deassert the interrupt line */
186 	sys_write32(BIT(1), regs + OT_REG_INTR_STATE_OFFSET);
187 }
188 
ot_aontimer_init(const struct device * dev)189 static int ot_aontimer_init(const struct device *dev)
190 {
191 	IRQ_CONNECT(
192 		DT_INST_IRQ_BY_IDX(0, 0, irq),
193 		DT_INST_IRQ_BY_IDX(0, 0, priority), wdt_ot_isr,
194 		DEVICE_DT_INST_GET(0), 0);
195 	irq_enable(DT_INST_IRQ_BY_IDX(0, 0, irq));
196 
197 	return 0;
198 }
199 
200 static struct wdt_ot_aontimer_data ot_aontimer_data;
201 
202 static struct wdt_ot_aontimer_cfg ot_aontimer_cfg = {
203 	.regs = (volatile uintptr_t) DT_INST_REG_ADDR(0),
204 	.clk_freq = DT_INST_PROP(0, clock_frequency),
205 	.wdog_lock = DT_INST_PROP(0, wdog_lock),
206 };
207 
208 static DEVICE_API(wdt, ot_aontimer_api) = {
209 	.setup = ot_aontimer_setup,
210 	.disable = ot_aontimer_disable,
211 	.install_timeout = ot_aontimer_install_timeout,
212 	.feed = ot_aontimer_feed,
213 };
214 
215 DEVICE_DT_INST_DEFINE(0, ot_aontimer_init, NULL,
216 	&ot_aontimer_data, &ot_aontimer_cfg, PRE_KERNEL_1,
217 	CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
218 	&ot_aontimer_api);
219