1 /*
2  * SPDX-License-Identifier: Apache-2.0
3  *
4  * Copyright (c) 2024 Ambiq Micro
5  */
6 
7 
8 #include <zephyr/drivers/rtc.h>
9 #include <zephyr/logging/log.h>
10 #include <zephyr/pm/device.h>
11 #include <zephyr/sys/util.h>
12 #include "rtc_utils.h"
13 
14 #define DT_DRV_COMPAT ambiq_rtc
15 
16 LOG_MODULE_REGISTER(ambiq_rtc, CONFIG_RTC_LOG_LEVEL);
17 
18 #include <am_mcu_apollo.h>
19 
20 #define AMBIQ_RTC_ALARM_TIME_MASK                                                              \
21 	(RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR |      \
22 	 RTC_ALARM_TIME_MASK_WEEKDAY | RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY)
23 
24 /* struct tm start time:   1st, Jan, 1900 */
25 #define TM_YEAR_REF 1900
26 #define AMBIQ_RTC_YEAR_MAX 2199
27 
28 struct ambiq_rtc_config {
29 	uint8_t clk_src;
30 };
31 
32 struct ambiq_rtc_data {
33 	struct k_spinlock lock;
34 #ifdef CONFIG_RTC_ALARM
35 	struct rtc_time alarm_time;
36 	uint16_t alarm_set_mask;
37 	rtc_alarm_callback alarm_user_callback;
38 	void *alarm_user_data;
39 	bool alarm_pending;
40 #endif
41 };
42 
rtc_time_to_ambiq_time_set(const struct rtc_time * tm,am_hal_rtc_time_t * atm)43 static void rtc_time_to_ambiq_time_set(const struct rtc_time *tm, am_hal_rtc_time_t *atm)
44 {
45 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
46 	atm->ui32Century = ((tm->tm_year <= 99) || (tm->tm_year >= 200));
47 #else
48 	atm->ui32CenturyBit = ((tm->tm_year > 99) && (tm->tm_year < 200));
49 #endif
50 	atm->ui32Year = tm->tm_year;
51 	if (tm->tm_year > 99) {
52 		atm->ui32Year = tm->tm_year % 100;
53 	}
54 	atm->ui32Weekday = tm->tm_wday;
55 	atm->ui32Month = tm->tm_mon + 1;
56 	atm->ui32DayOfMonth = tm->tm_mday;
57 	atm->ui32Hour = tm->tm_hour;
58 	atm->ui32Minute = tm->tm_min;
59 	atm->ui32Second = tm->tm_sec;
60 
61 	/* Nanoseconds times 10mil is hundredths */
62 	atm->ui32Hundredths = tm->tm_nsec / 10000000;
63 	if (atm->ui32Hundredths > 99) {
64 		uint16_t value = atm->ui32Hundredths / 100;
65 
66 		atm->ui32Second += value;
67 		atm->ui32Hundredths -= value*100;
68 	}
69 }
70 
71 /* To set the timer registers */
ambiq_time_to_rtc_time_set(const am_hal_rtc_time_t * atm,struct rtc_time * tm)72 static void ambiq_time_to_rtc_time_set(const am_hal_rtc_time_t *atm, struct rtc_time *tm)
73 {
74 	tm->tm_year = atm->ui32Year;
75 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
76 	if (atm->ui32Century == 0) {
77 		tm->tm_year += 100;
78 	} else {
79 		tm->tm_year += 200;
80 	}
81 #else
82 	if (atm->ui32CenturyBit == 0) {
83 		tm->tm_year += 200;
84 	} else {
85 		tm->tm_year += 100;
86 	}
87 #endif
88 	tm->tm_wday = atm->ui32Weekday;
89 	tm->tm_mon = atm->ui32Month - 1;
90 	tm->tm_mday = atm->ui32DayOfMonth;
91 	tm->tm_hour = atm->ui32Hour;
92 	tm->tm_min = atm->ui32Minute;
93 	tm->tm_sec = atm->ui32Second;
94 
95 	/* Nanoseconds times 10mil is hundredths */
96 	tm->tm_nsec = atm->ui32Hundredths * 10000000;
97 }
98 
test_for_rollover(am_hal_rtc_time_t * atm)99 static int test_for_rollover(am_hal_rtc_time_t *atm)
100 {
101 	if ((atm->ui32Year == 99) &&
102 		(atm->ui32Month == 12) &&
103 		(atm->ui32DayOfMonth == 31)) {
104 		return -EINVAL;
105 	}
106 
107 	return 0;
108 }
109 
110 /* To set the timer registers */
ambiq_rtc_set_time(const struct device * dev,const struct rtc_time * timeptr)111 static int ambiq_rtc_set_time(const struct device *dev, const struct rtc_time *timeptr)
112 {
113 	int err = 0;
114 	am_hal_rtc_time_t ambiq_time = {0};
115 
116 	struct ambiq_rtc_data *data = dev->data;
117 
118 	if (timeptr->tm_year + TM_YEAR_REF > AMBIQ_RTC_YEAR_MAX) {
119 		return -EINVAL;
120 	}
121 
122 	k_spinlock_key_t key = k_spin_lock(&data->lock);
123 
124 	LOG_DBG("set time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
125 			"min = %d, sec = %d",
126 			timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
127 			timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
128 
129 	/* Convertn to Ambiq Time */
130 	rtc_time_to_ambiq_time_set(timeptr, &ambiq_time);
131 
132 	if (test_for_rollover(&ambiq_time)) {
133 		return -EINVAL;
134 	}
135 
136 	err = am_hal_rtc_time_set(&ambiq_time);
137 	if (err) {
138 		LOG_WRN("Set Timer returned an error - %d!", err);
139 	}
140 
141 	k_spin_unlock(&data->lock, key);
142 
143 	return err;
144 }
145 
146 /* To get from the timer registers */
ambiq_rtc_get_time(const struct device * dev,struct rtc_time * timeptr)147 static int ambiq_rtc_get_time(const struct device *dev, struct rtc_time *timeptr)
148 {
149 	int err = 0;
150 	am_hal_rtc_time_t ambiq_time = {0};
151 	struct ambiq_rtc_data *data = dev->data;
152 
153 	k_spinlock_key_t key = k_spin_lock(&data->lock);
154 
155 	err = am_hal_rtc_time_get(&ambiq_time);
156 	if (err != 0) {
157 		LOG_WRN("Get Timer returned an error - %d!", err);
158 		goto unlock;
159 	}
160 
161 	ambiq_time_to_rtc_time_set(&ambiq_time, timeptr);
162 
163 	LOG_DBG("get time: year = %d, mon = %d, mday = %d, wday = %d, hour = %d, "
164 			"min = %d, sec = %d",
165 			timeptr->tm_year, timeptr->tm_mon, timeptr->tm_mday, timeptr->tm_wday,
166 			timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec);
167 
168 unlock:
169 	k_spin_unlock(&data->lock, key);
170 
171 	return err;
172 }
173 
174 #ifdef CONFIG_RTC_ALARM
175 
ambiq_rtc_alarm_get_supported_fields(const struct device * dev,uint16_t id,uint16_t * mask)176 static int ambiq_rtc_alarm_get_supported_fields(const struct device *dev,
177 								uint16_t id, uint16_t *mask)
178 {
179 	ARG_UNUSED(dev);
180 
181 	if (id != 0U) {
182 		LOG_ERR("Invalid ID %d", id);
183 		return -EINVAL;
184 	}
185 
186 	*mask = AMBIQ_RTC_ALARM_TIME_MASK;
187 
188 	return 0;
189 }
190 
191 /* To get from the alarm registers */
ambiq_rtc_alarm_get_time(const struct device * dev,uint16_t id,uint16_t * mask,struct rtc_time * timeptr)192 static int ambiq_rtc_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
193 		struct rtc_time *timeptr)
194 {
195 	am_hal_rtc_time_t ambiq_time = {0};
196 	struct ambiq_rtc_data *data = dev->data;
197 
198 	if (id != 0U) {
199 		LOG_ERR("Invalid ID %d", id);
200 		return -EINVAL;
201 	}
202 
203 	k_spinlock_key_t key = k_spin_lock(&data->lock);
204 
205 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
206 	am_hal_rtc_alarm_get(&ambiq_time);
207 #else
208 	am_hal_rtc_alarm_get(&ambiq_time, NULL);
209 #endif
210 
211 	ambiq_time_to_rtc_time_set(&ambiq_time, timeptr);
212 
213 	*mask = data->alarm_set_mask;
214 
215 	LOG_DBG("get alarm: wday = %d, mon = %d, mday = %d, hour = %d, min = %d, sec = %d, "
216 		"mask = 0x%04x", timeptr->tm_wday, timeptr->tm_mon, timeptr->tm_mday,
217 		timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, *mask);
218 
219 	k_spin_unlock(&data->lock, key);
220 
221 	return 0;
222 }
223 
ambiq_rtc_alarm_set_time(const struct device * dev,uint16_t id,uint16_t mask,const struct rtc_time * timeptr)224 static int ambiq_rtc_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
225 		const struct rtc_time *timeptr)
226 {
227 	struct ambiq_rtc_data *data = dev->data;
228 	am_hal_rtc_time_t ambiq_time = {0};
229 	uint16_t mask_available;
230 
231 	if (id != 0U) {
232 		LOG_ERR("Invalid ID %d", id);
233 		return -EINVAL;
234 	}
235 
236 	if (rtc_utils_validate_rtc_time(timeptr, mask) == false) {
237 		LOG_DBG("Invalid Input Value");
238 		return -EINVAL;
239 	}
240 
241 	(void)ambiq_rtc_alarm_get_supported_fields(NULL, 0, &mask_available);
242 
243 	if (mask & ~mask_available) {
244 		return -EINVAL;
245 	}
246 
247 	data->alarm_set_mask = mask;
248 
249 	k_spinlock_key_t key = k_spin_lock(&data->lock);
250 
251 	/* Disable and clear the alarm */
252 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
253 	am_hal_rtc_int_disable(AM_HAL_RTC_INT_ALM);
254 	am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);
255 #else
256 	am_hal_rtc_interrupt_disable(AM_HAL_RTC_INT_ALM);
257 	am_hal_rtc_interrupt_clear(AM_HAL_RTC_INT_ALM);
258 #endif
259 
260 	/* When mask is 0 */
261 	if (mask == 0) {
262 		LOG_DBG("The alarm is disabled");
263 		goto unlock;
264 	}
265 
266 	LOG_DBG("set alarm: second = %d, min = %d, hour = %d, mday = %d, month = %d,"
267 			"wday = %d,  mask = 0x%04x",
268 			timeptr->tm_sec, timeptr->tm_min, timeptr->tm_hour, timeptr->tm_mday,
269 			timeptr->tm_mon, timeptr->tm_wday, mask);
270 
271 	/* Convertn to Ambiq Time */
272 	rtc_time_to_ambiq_time_set(timeptr, &ambiq_time);
273 
274 	/*  Set RTC ALARM, Ambiq must have interval != AM_HAL_RTC_ALM_RPT_DIS */
275 	am_hal_rtc_alarm_set(&ambiq_time, AM_HAL_RTC_ALM_RPT_YR);
276 
277 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
278 	am_hal_rtc_int_enable(AM_HAL_RTC_INT_ALM);
279 #else
280 	am_hal_rtc_interrupt_enable(AM_HAL_RTC_INT_ALM);
281 #endif
282 
283 unlock:
284 	k_spin_unlock(&data->lock, key);
285 
286 	return 0;
287 }
288 
ambiq_rtc_alarm_is_pending(const struct device * dev,uint16_t id)289 static int ambiq_rtc_alarm_is_pending(const struct device *dev, uint16_t id)
290 {
291 	struct ambiq_rtc_data *data = dev->data;
292 	int ret = 0;
293 
294 	if (id != 0) {
295 		return -EINVAL;
296 	}
297 
298 	K_SPINLOCK(&data->lock) {
299 		ret = data->alarm_pending ? 1 : 0;
300 		data->alarm_pending = false;
301 	}
302 
303 	return ret;
304 }
305 
ambiq_rtc_isr(const struct device * dev)306 static void ambiq_rtc_isr(const struct device *dev)
307 {
308 	/* Clear the RTC alarm interrupt. 8*/
309 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
310 	am_hal_rtc_int_clear(AM_HAL_RTC_INT_ALM);
311 #else
312 	am_hal_rtc_interrupt_clear(AM_HAL_RTC_INT_ALM);
313 #endif
314 
315 #if defined(CONFIG_RTC_ALARM)
316 	struct ambiq_rtc_data *data = dev->data;
317 
318 	if (data->alarm_user_callback) {
319 		data->alarm_user_callback(dev, 0, data->alarm_user_data);
320 		data->alarm_pending = false;
321 	} else {
322 		data->alarm_pending = true;
323 	}
324 #endif
325 }
326 
ambiq_rtc_alarm_set_callback(const struct device * dev,uint16_t id,rtc_alarm_callback callback,void * user_data)327 static int ambiq_rtc_alarm_set_callback(const struct device *dev, uint16_t id,
328 		rtc_alarm_callback callback, void *user_data)
329 {
330 
331 	struct ambiq_rtc_data *data = dev->data;
332 
333 	if (id != 0U) {
334 		LOG_ERR("Invalid ID %d", id);
335 		return -EINVAL;
336 	}
337 
338 	K_SPINLOCK(&data->lock) {
339 		data->alarm_user_callback = callback;
340 		data->alarm_user_data = user_data;
341 		if ((callback == NULL) && (user_data == NULL)) {
342 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
343 			am_hal_rtc_int_disable(AM_HAL_RTC_INT_ALM);
344 #else
345 			am_hal_rtc_interrupt_disable(AM_HAL_RTC_INT_ALM);
346 #endif
347 		}
348 	}
349 
350 	return 0;
351 }
352 #endif
353 
ambiq_rtc_init(const struct device * dev)354 static int ambiq_rtc_init(const struct device *dev)
355 {
356 	const struct ambiq_rtc_config *config = dev->config;
357 
358 #ifdef CONFIG_RTC_ALARM
359 	struct ambiq_rtc_data *data = dev->data;
360 #endif
361 
362 /* Enable the clock for RTC. */
363 #if defined(CONFIG_SOC_SERIES_APOLLO3X)
364 	am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_START + config->clk_src, NULL);
365 #endif
366 	am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_RTC_SEL_XTAL + config->clk_src, NULL);
367 	/* Enable the RTC. */
368 	am_hal_rtc_osc_enable();
369 
370 #ifdef CONFIG_RTC_ALARM
371 	data->alarm_user_callback = NULL;
372 	data->alarm_pending = false;
373 
374 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), ambiq_rtc_isr, DEVICE_DT_INST_GET(0),
375 			0);
376 	irq_enable(DT_INST_IRQN(0));
377 #endif
378 	return 0;
379 }
380 
381 static DEVICE_API(rtc, ambiq_rtc_driver_api) = {
382 	.set_time = ambiq_rtc_set_time,
383 	.get_time = ambiq_rtc_get_time,
384 	/* RTC_UPDATE not supported */
385 #ifdef CONFIG_RTC_ALARM
386 	.alarm_get_supported_fields = ambiq_rtc_alarm_get_supported_fields,
387 	.alarm_set_time = ambiq_rtc_alarm_set_time,
388 	.alarm_get_time = ambiq_rtc_alarm_get_time,
389 	.alarm_is_pending = ambiq_rtc_alarm_is_pending,
390 	.alarm_set_callback = ambiq_rtc_alarm_set_callback,
391 #endif
392 };
393 
394 #define AMBIQ_RTC_INIT(inst)									\
395 static const struct ambiq_rtc_config ambiq_rtc_config_##inst = {	\
396 	.clk_src = DT_INST_ENUM_IDX(inst, clock)};		\
397 												\
398 static struct ambiq_rtc_data ambiq_rtc_data##inst;	\
399 												\
400 DEVICE_DT_INST_DEFINE(inst, &ambiq_rtc_init, NULL, &ambiq_rtc_data##inst,    \
401 				&ambiq_rtc_config_##inst, POST_KERNEL, \
402 				CONFIG_RTC_INIT_PRIORITY, &ambiq_rtc_driver_api);
403 
404 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_RTC_INIT)
405