1 /*
2  * Copyright (c) 2023 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ambiq_watchdog
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/watchdog.h>
11 
12 #include <errno.h>
13 #include <am_mcu_apollo.h>
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(wdt_ambiq, CONFIG_WDT_LOG_LEVEL);
17 
18 typedef void (*ambiq_wdt_cfg_func_t)(void);
19 
20 struct wdt_ambiq_config {
21 	uint32_t base;
22 	uint32_t irq_num;
23 	uint8_t clk_freq;
24 	ambiq_wdt_cfg_func_t cfg_func;
25 };
26 
27 struct wdt_ambiq_data {
28 	wdt_callback_t callback;
29 	uint32_t timeout;
30 	bool reset;
31 };
32 
wdt_ambiq_isr(void * arg)33 static void wdt_ambiq_isr(void *arg)
34 {
35 	const struct device *dev = (const struct device *)arg;
36 	struct wdt_ambiq_data *data = dev->data;
37 
38 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
39 	am_hal_wdt_int_clear();
40 #else
41 	uint32_t status;
42 	am_hal_wdt_interrupt_status_get(AM_HAL_WDT_MCU, &status, false);
43 	am_hal_wdt_interrupt_clear(AM_HAL_WDT_MCU, status);
44 #endif
45 
46 	if (data->callback) {
47 		data->callback(dev, 0);
48 	}
49 }
50 
wdt_ambiq_setup(const struct device * dev,uint8_t options)51 static int wdt_ambiq_setup(const struct device *dev, uint8_t options)
52 {
53 	const struct wdt_ambiq_config *dev_cfg = dev->config;
54 	struct wdt_ambiq_data *data = dev->data;
55 	am_hal_wdt_config_t cfg;
56 
57 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
58 	uint32_t ui32ClockSource = AM_HAL_WDT_LFRC_CLK_DEFAULT;
59 
60 	if (dev_cfg->clk_freq == 128) {
61 		ui32ClockSource = AM_HAL_WDT_LFRC_CLK_128HZ;
62 	} else if (dev_cfg->clk_freq == 16) {
63 		ui32ClockSource = AM_HAL_WDT_LFRC_CLK_16HZ;
64 	} else if (dev_cfg->clk_freq == 1) {
65 		ui32ClockSource = AM_HAL_WDT_LFRC_CLK_1HZ;
66 	}
67 	cfg.ui32Config = ui32ClockSource | _VAL2FLD(WDT_CFG_RESEN, data->reset) |
68 			 AM_HAL_WDT_ENABLE_INTERRUPT;
69 	cfg.ui16InterruptCount = data->timeout;
70 	cfg.ui16ResetCount = data->timeout;
71 	am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_LFRC_START, 0);
72 	am_hal_wdt_init(&cfg);
73 	am_hal_wdt_int_enable();
74 	am_hal_wdt_start();
75 #else
76 	if (dev_cfg->clk_freq == 128) {
77 		cfg.eClockSource = AM_HAL_WDT_128HZ;
78 	} else if (dev_cfg->clk_freq == 16) {
79 		cfg.eClockSource = AM_HAL_WDT_16HZ;
80 	} else if (dev_cfg->clk_freq == 1) {
81 		cfg.eClockSource = AM_HAL_WDT_1HZ;
82 	}
83 
84 	cfg.bInterruptEnable = true;
85 	cfg.ui32InterruptValue = data->timeout;
86 	cfg.bResetEnable = data->reset;
87 	cfg.ui32ResetValue = data->timeout;
88 	cfg.bAlertOnDSPReset = false;
89 
90 	am_hal_wdt_config(AM_HAL_WDT_MCU, &cfg);
91 	am_hal_wdt_interrupt_enable(AM_HAL_WDT_MCU, AM_HAL_WDT_INTERRUPT_MCU);
92 	am_hal_wdt_start(AM_HAL_WDT_MCU, false);
93 #endif
94 	return 0;
95 }
96 
wdt_ambiq_disable(const struct device * dev)97 static int wdt_ambiq_disable(const struct device *dev)
98 {
99 	ARG_UNUSED(dev);
100 
101 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
102 	am_hal_wdt_halt();
103 #else
104 	am_hal_wdt_stop(AM_HAL_WDT_MCU);
105 #endif
106 	return 0;
107 }
108 
wdt_ambiq_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)109 static int wdt_ambiq_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
110 {
111 	const struct wdt_ambiq_config *dev_cfg = dev->config;
112 	struct wdt_ambiq_data *data = dev->data;
113 
114 	if (cfg->window.min != 0U || cfg->window.max == 0U) {
115 		return -EINVAL;
116 	}
117 
118 	data->timeout = cfg->window.max / 1000 * dev_cfg->clk_freq;
119 	data->callback = cfg->callback;
120 
121 	switch (cfg->flags) {
122 	case WDT_FLAG_RESET_CPU_CORE:
123 	case WDT_FLAG_RESET_SOC:
124 		data->reset = true;
125 		break;
126 	case WDT_FLAG_RESET_NONE:
127 		data->reset = false;
128 		break;
129 	default:
130 		LOG_ERR("Unsupported watchdog config flag");
131 		return -EINVAL;
132 	}
133 
134 	return 0;
135 }
136 
wdt_ambiq_feed(const struct device * dev,int channel_id)137 static int wdt_ambiq_feed(const struct device *dev, int channel_id)
138 {
139 	ARG_UNUSED(dev);
140 	ARG_UNUSED(channel_id);
141 
142 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
143 	am_hal_wdt_restart();
144 #else
145 	am_hal_wdt_restart(AM_HAL_WDT_MCU);
146 #endif
147 	LOG_DBG("Fed the watchdog");
148 
149 	return 0;
150 }
151 
wdt_ambiq_init(const struct device * dev)152 static int wdt_ambiq_init(const struct device *dev)
153 {
154 	const struct wdt_ambiq_config *dev_cfg = dev->config;
155 
156 	if (dev_cfg->clk_freq != 128 && dev_cfg->clk_freq != 16 && dev_cfg->clk_freq != 1) {
157 		return -ENOTSUP;
158 	}
159 
160 	NVIC_ClearPendingIRQ(dev_cfg->irq_num);
161 
162 	dev_cfg->cfg_func();
163 
164 	irq_enable(dev_cfg->irq_num);
165 
166 	return 0;
167 }
168 
169 static DEVICE_API(wdt, wdt_ambiq_driver_api) = {
170 	.setup = wdt_ambiq_setup,
171 	.disable = wdt_ambiq_disable,
172 	.install_timeout = wdt_ambiq_install_timeout,
173 	.feed = wdt_ambiq_feed,
174 };
175 
176 #define AMBIQ_WDT_INIT(n)                                                                          \
177 	static struct wdt_ambiq_data wdt_ambiq_data##n;                                            \
178 	static void ambiq_wdt_cfg_func_##n(void)                                                   \
179 	{                                                                                          \
180                                                                                                    \
181 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), wdt_ambiq_isr,              \
182 			    DEVICE_DT_INST_GET(n), 0);                                             \
183 	};                                                                                         \
184 	static const struct wdt_ambiq_config wdt_ambiq_config##n = {                               \
185 		.base = DT_INST_REG_ADDR(n),                                                       \
186 		.clk_freq = DT_INST_PROP(n, clock_frequency),                                      \
187 		.irq_num = DT_INST_IRQN(n),                                                        \
188 		.cfg_func = ambiq_wdt_cfg_func_##n};                                               \
189                                                                                                    \
190 	DEVICE_DT_INST_DEFINE(n, wdt_ambiq_init, NULL, &wdt_ambiq_data##n, &wdt_ambiq_config##n,   \
191 			      PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,                    \
192 			      &wdt_ambiq_driver_api);
193 
194 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_WDT_INIT)
195