1 /* SPDX-License-Identifier: Apache-2.0 */
2 /*
3  * Copyright (c) 2023 Intel Corporation
4  *
5  * Author: Adrian Warecki <adrian.warecki@intel.com>
6  */
7 
8 /**
9  * @file
10  *
11  * @brief Intel ACE per-core watchdogs driver
12  *
13  * The ace platform has a set of designware watchdogs, one for each core. This driver is responsible
14  * for finding the base addresses of subordinate devices, controlling the pause signal and handling
15  * interrupts. The registers from the DSP Core Watch Dog Timer Control & Status block are used for
16  * this purpose. This block is shared by all per-core watchdogs.
17  *
18  * The base addresses of the subordinate watchdogs are read from the control registers. As a result,
19  * in the device tree we have only one base address for the intel watchdog.
20  *
21  * The designware watchdog only supports a hardware pause signal. It cannot be paused
22  * programmatically. On ace platform there are GPIO-like registers that allows control of a hardware
23  * pause signal for subordinate watchdogs.
24  *
25  * All subordinate watchdog devices share the same interrupt number. Each watchdog reports
26  * an interrupt to the core to which it has been assigned. The same interrupt number cannot
27  * be used by multiple devices in the device tree. This driver handles interrupts from all
28  * subordinate watchdogs and identifies which device signal it.
29  */
30 
31 #define DT_DRV_COMPAT intel_adsp_watchdog
32 
33 #include <zephyr/drivers/watchdog.h>
34 #include <zephyr/logging/log.h>
35 #include <zephyr/sys_clock.h>
36 #include <zephyr/math/ilog2.h>
37 
38 #include "wdt_dw.h"
39 #include "wdt_dw_common.h"
40 #include "wdt_intel_adsp.h"
41 
42 LOG_MODULE_REGISTER(wdt_intel_adsp, CONFIG_WDT_LOG_LEVEL);
43 
44 #define DEV_NODE				DT_INST(0, DT_DRV_COMPAT)
45 #define WDT_INTEL_ADSP_INTERRUPT_SUPPORT	DT_NODE_HAS_PROP(DEV_NODE, interrupts)
46 
47 /* Device run time data */
48 struct intel_adsp_wdt_dev_data {
49 	uint32_t core_wdt[CONFIG_MP_MAX_NUM_CPUS];
50 	uint32_t period_cfg;
51 	bool allow_reset;
52 #if WDT_INTEL_ADSP_INTERRUPT_SUPPORT
53 	wdt_callback_t callback;
54 #endif
55 };
56 
57 /* Device constant configuration parameters */
58 struct intel_adsp_wdt_dev_cfg {
59 	uint32_t base;
60 	uint32_t clk_freq;
61 };
62 
intel_adsp_wdt_setup(const struct device * dev,uint8_t options)63 static int intel_adsp_wdt_setup(const struct device *dev, uint8_t options)
64 {
65 	const struct intel_adsp_wdt_dev_cfg *const dev_config = dev->config;
66 	struct intel_adsp_wdt_dev_data *const dev_data = dev->data;
67 	unsigned int i;
68 	int ret;
69 
70 	ret = dw_wdt_check_options(options);
71 	if (ret) {
72 		return ret;
73 	}
74 
75 	for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) {
76 		ret = dw_wdt_configure(dev_data->core_wdt[i], dev_data->period_cfg);
77 		if (ret) {
78 			return ret;
79 		}
80 
81 #if WDT_INTEL_ADSP_INTERRUPT_SUPPORT
82 		dw_wdt_response_mode_set(dev_data->core_wdt[i], !!dev_data->callback);
83 #endif
84 		intel_adsp_wdt_reset_set(dev_config->base, i, dev_data->allow_reset);
85 	}
86 
87 	return 0;
88 }
89 
intel_adsp_wdt_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)90 static int intel_adsp_wdt_install_timeout(const struct device *dev,
91 					  const struct wdt_timeout_cfg *config)
92 {
93 	const struct intel_adsp_wdt_dev_cfg *const dev_config = dev->config;
94 	struct intel_adsp_wdt_dev_data *const dev_data = dev->data;
95 	int ret;
96 
97 #if WDT_INTEL_ADSP_INTERRUPT_SUPPORT
98 	dev_data->callback = config->callback;
99 #else
100 	if (config->callback) {
101 		LOG_ERR("Interrupt is not configured, can't set a callback.");
102 		return -ENOTSUP;
103 	}
104 #endif
105 
106 	ret = dw_wdt_calc_period(dev_data->core_wdt[0], dev_config->clk_freq, config,
107 				 &dev_data->period_cfg);
108 	if (ret) {
109 		return ret;
110 	}
111 
112 	if (config->flags & WDT_FLAG_RESET_CPU_CORE) {
113 		dev_data->allow_reset = true;
114 	}
115 
116 	return 0;
117 }
118 
intel_adsp_wdt_feed(const struct device * dev,int channel_id)119 static int intel_adsp_wdt_feed(const struct device *dev, int channel_id)
120 {
121 	struct intel_adsp_wdt_dev_data *const dev_data = dev->data;
122 
123 	if (channel_id >= CONFIG_MP_MAX_NUM_CPUS) {
124 		return -EINVAL;
125 	}
126 
127 	dw_wdt_counter_restart(dev_data->core_wdt[channel_id]);
128 	return 0;
129 }
130 
131 #if WDT_INTEL_ADSP_INTERRUPT_SUPPORT
intel_adsp_wdt_isr(const struct device * dev)132 static void intel_adsp_wdt_isr(const struct device *dev)
133 {
134 	struct intel_adsp_wdt_dev_data *const dev_data = dev->data;
135 	const uint32_t cpu = arch_proc_id();
136 	const uint32_t base = dev_data->core_wdt[cpu];
137 
138 	if (dw_wdt_interrupt_status_register_get(base)) {
139 		if (dev_data->callback) {
140 			dev_data->callback(dev, cpu);
141 		}
142 
143 		dw_wdt_clear_interrupt(base);
144 	}
145 }
146 #endif
147 
intel_adsp_wdt_init(const struct device * dev)148 static int intel_adsp_wdt_init(const struct device *dev)
149 {
150 	const unsigned int reset_pulse_length =
151 		ilog2(DT_PROP(DEV_NODE, reset_pulse_length)) - 1;
152 	const struct intel_adsp_wdt_dev_cfg *const dev_config = dev->config;
153 	struct intel_adsp_wdt_dev_data *const dev_data = dev->data;
154 	unsigned int i;
155 	int ret;
156 
157 	for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) {
158 		dev_data->core_wdt[i] = intel_adsp_wdt_pointer_get(dev_config->base, i);
159 		ret = dw_wdt_probe(dev_data->core_wdt[i], reset_pulse_length);
160 		if (ret) {
161 			return ret;
162 		}
163 	}
164 
165 #if WDT_INTEL_ADSP_INTERRUPT_SUPPORT
166 	IRQ_CONNECT(DT_IRQN(DEV_NODE), DT_IRQ(DEV_NODE, priority), intel_adsp_wdt_isr,
167 		    DEVICE_DT_GET(DEV_NODE), DT_IRQ(DEV_NODE, sense));
168 	irq_enable(DT_IRQN(DEV_NODE));
169 #endif
170 
171 	return 0;
172 }
173 
174 /**
175  * @brief Pause watchdog operation
176  *
177  * Sets the pause signal to stop the watchdog timing
178  *
179  * @param dev Pointer to the device structure
180  * @param channel_id Channel identifier
181  */
intel_adsp_watchdog_pause(const struct device * dev,const int channel_id)182 int intel_adsp_watchdog_pause(const struct device *dev, const int channel_id)
183 {
184 	const struct intel_adsp_wdt_dev_cfg *const dev_config = dev->config;
185 
186 	if (channel_id >= CONFIG_MP_MAX_NUM_CPUS) {
187 		return -EINVAL;
188 	}
189 
190 	intel_adsp_wdt_pause(dev_config->base, channel_id);
191 	return 0;
192 }
193 
194 /**
195  * @brief Resume watchdog operation
196  *
197  * Clears the pause signal to resume the watchdog timing
198  *
199  * @param dev Pointer to the device structure
200  * @param channel_id Channel identifier
201  */
intel_adsp_watchdog_resume(const struct device * dev,const int channel_id)202 int intel_adsp_watchdog_resume(const struct device *dev, const int channel_id)
203 {
204 	const struct intel_adsp_wdt_dev_cfg *const dev_config = dev->config;
205 
206 	if (channel_id >= CONFIG_MP_MAX_NUM_CPUS) {
207 		return -EINVAL;
208 	}
209 
210 	intel_adsp_wdt_resume(dev_config->base, channel_id);
211 	return 0;
212 }
213 
214 static const struct wdt_driver_api intel_adsp_wdt_api = {
215 	.setup = intel_adsp_wdt_setup,
216 	.disable = dw_wdt_disable,
217 	.install_timeout = intel_adsp_wdt_install_timeout,
218 	.feed = intel_adsp_wdt_feed,
219 };
220 
221 #if !(DT_NODE_HAS_PROP(DEV_NODE, clock_frequency) || DT_NODE_HAS_PROP(DEV_NODE, clocks))
222 #error Clock frequency not configured!
223 #endif
224 
225 static const struct intel_adsp_wdt_dev_cfg wdt_intel_adsp_config = {
226 	.base = DT_REG_ADDR(DEV_NODE),
227 	COND_CODE_1(DT_NODE_HAS_PROP(DEV_NODE, clock_frequency),
228 		    (.clk_freq = DT_PROP(DEV_NODE, clock_frequency)),
229 		    (.clk_freq = DT_PROP_BY_PHANDLE(DEV_NODE, clocks, clock_frequency))
230 	),
231 };
232 
233 static struct intel_adsp_wdt_dev_data wdt_intel_adsp_data;
234 
235 DEVICE_DT_DEFINE(DEV_NODE, &intel_adsp_wdt_init, NULL, &wdt_intel_adsp_data, &wdt_intel_adsp_config,
236 		 POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &intel_adsp_wdt_api);
237