blob: be9250f6e5aec13c4a762fa0e29f51f337c0c3be [file] [log] [blame]
Austin Schuh208337d2022-01-01 14:29:11 -08001/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "pico.h"
8
9#include "hardware/irq.h"
10#include "hardware/rtc.h"
11#include "hardware/resets.h"
12#include "hardware/clocks.h"
13
14// Set this when setting an alarm
15static rtc_callback_t _callback = NULL;
16static bool _alarm_repeats = false;
17
18bool rtc_running(void) {
19 return (rtc_hw->ctrl & RTC_CTRL_RTC_ACTIVE_BITS);
20}
21
22void rtc_init(void) {
23 // Get clk_rtc freq and make sure it is running
24 uint rtc_freq = clock_get_hz(clk_rtc);
25 assert(rtc_freq != 0);
26
27 // Take rtc out of reset now that we know clk_rtc is running
28 reset_block(RESETS_RESET_RTC_BITS);
29 unreset_block_wait(RESETS_RESET_RTC_BITS);
30
31 // Set up the 1 second divider.
32 // If rtc_freq is 400 then clkdiv_m1 should be 399
33 rtc_freq -= 1;
34
35 // Check the freq is not too big to divide
36 assert(rtc_freq <= RTC_CLKDIV_M1_BITS);
37
38 // Write divide value
39 rtc_hw->clkdiv_m1 = rtc_freq;
40}
41
42static bool valid_datetime(datetime_t *t) {
43 // Valid ranges taken from RTC doc. Note when setting an RTC alarm
44 // these values are allowed to be -1 to say "don't match this value"
45 if (!(t->year >= 0 && t->year <= 4095)) return false;
46 if (!(t->month >= 1 && t->month <= 12)) return false;
47 if (!(t->day >= 1 && t->day <= 31)) return false;
48 if (!(t->dotw >= 0 && t->dotw <= 6)) return false;
49 if (!(t->hour >= 0 && t->hour <= 23)) return false;
50 if (!(t->min >= 0 && t->min <= 59)) return false;
51 if (!(t->sec >= 0 && t->sec <= 59)) return false;
52 return true;
53}
54
55bool rtc_set_datetime(datetime_t *t) {
56 if (!valid_datetime(t)) {
57 return false;
58 }
59
60 // Disable RTC
61 rtc_hw->ctrl = 0;
62 // Wait while it is still active
63 while (rtc_running()) {
64 tight_loop_contents();
65 }
66
67 // Write to setup registers
Ravago Jonesd208ae72023-02-13 02:24:07 -080068 rtc_hw->setup_0 = (((uint32_t)t->year) << RTC_SETUP_0_YEAR_LSB ) |
69 (((uint32_t)t->month) << RTC_SETUP_0_MONTH_LSB) |
70 (((uint32_t)t->day) << RTC_SETUP_0_DAY_LSB);
71 rtc_hw->setup_1 = (((uint32_t)t->dotw) << RTC_SETUP_1_DOTW_LSB) |
72 (((uint32_t)t->hour) << RTC_SETUP_1_HOUR_LSB) |
73 (((uint32_t)t->min) << RTC_SETUP_1_MIN_LSB) |
74 (((uint32_t)t->sec) << RTC_SETUP_1_SEC_LSB);
Austin Schuh208337d2022-01-01 14:29:11 -080075
76 // Load setup values into rtc clock domain
77 rtc_hw->ctrl = RTC_CTRL_LOAD_BITS;
78
79 // Enable RTC and wait for it to be running
80 rtc_hw->ctrl = RTC_CTRL_RTC_ENABLE_BITS;
81 while (!rtc_running()) {
82 tight_loop_contents();
83 }
84
85 return true;
86}
87
88bool rtc_get_datetime(datetime_t *t) {
89 // Make sure RTC is running
90 if (!rtc_running()) {
91 return false;
92 }
93
94 // Note: RTC_0 should be read before RTC_1
95 uint32_t rtc_0 = rtc_hw->rtc_0;
96 uint32_t rtc_1 = rtc_hw->rtc_1;
97
Ravago Jonesd208ae72023-02-13 02:24:07 -080098 t->dotw = (int8_t) ((rtc_0 & RTC_RTC_0_DOTW_BITS ) >> RTC_RTC_0_DOTW_LSB);
99 t->hour = (int8_t) ((rtc_0 & RTC_RTC_0_HOUR_BITS ) >> RTC_RTC_0_HOUR_LSB);
100 t->min = (int8_t) ((rtc_0 & RTC_RTC_0_MIN_BITS ) >> RTC_RTC_0_MIN_LSB);
101 t->sec = (int8_t) ((rtc_0 & RTC_RTC_0_SEC_BITS ) >> RTC_RTC_0_SEC_LSB);
102 t->year = (int16_t) ((rtc_1 & RTC_RTC_1_YEAR_BITS ) >> RTC_RTC_1_YEAR_LSB);
103 t->month = (int8_t) ((rtc_1 & RTC_RTC_1_MONTH_BITS) >> RTC_RTC_1_MONTH_LSB);
104 t->day = (int8_t) ((rtc_1 & RTC_RTC_1_DAY_BITS ) >> RTC_RTC_1_DAY_LSB);
Austin Schuh208337d2022-01-01 14:29:11 -0800105
106 return true;
107}
108
109void rtc_enable_alarm(void) {
110 // Set matching and wait for it to be enabled
111 hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
112 while(!(rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS)) {
113 tight_loop_contents();
114 }
115}
116
117static void rtc_irq_handler(void) {
118 // Always disable the alarm to clear the current IRQ.
119 // Even if it is a repeatable alarm, we don't want it to keep firing.
120 // If it matches on a second it can keep firing for that second.
121 rtc_disable_alarm();
122
123 if (_alarm_repeats) {
124 // If it is a repeatable alarm, re enable the alarm.
125 rtc_enable_alarm();
126 }
127
128 // Call user callback function
129 if (_callback) {
130 _callback();
131 }
132}
133
134static bool rtc_alarm_repeats(datetime_t *t) {
135 // If any value is set to -1 then we don't match on that value
136 // hence the alarm will eventually repeat
137 if (t->year < 0) return true;
138 if (t->month < 0) return true;
139 if (t->day < 0) return true;
140 if (t->dotw < 0) return true;
141 if (t->hour < 0) return true;
142 if (t->min < 0) return true;
143 if (t->sec < 0) return true;
144 return false;
145}
146
147void rtc_set_alarm(datetime_t *t, rtc_callback_t user_callback) {
148 rtc_disable_alarm();
149
150 // Only add to setup if it isn't -1
Ravago Jonesd208ae72023-02-13 02:24:07 -0800151 rtc_hw->irq_setup_0 = ((t->year < 0) ? 0 : (((uint32_t)t->year) << RTC_IRQ_SETUP_0_YEAR_LSB )) |
152 ((t->month < 0) ? 0 : (((uint32_t)t->month) << RTC_IRQ_SETUP_0_MONTH_LSB)) |
153 ((t->day < 0) ? 0 : (((uint32_t)t->day) << RTC_IRQ_SETUP_0_DAY_LSB ));
154 rtc_hw->irq_setup_1 = ((t->dotw < 0) ? 0 : (((uint32_t)t->dotw) << RTC_IRQ_SETUP_1_DOTW_LSB)) |
155 ((t->hour < 0) ? 0 : (((uint32_t)t->hour) << RTC_IRQ_SETUP_1_HOUR_LSB)) |
156 ((t->min < 0) ? 0 : (((uint32_t)t->min) << RTC_IRQ_SETUP_1_MIN_LSB )) |
157 ((t->sec < 0) ? 0 : (((uint32_t)t->sec) << RTC_IRQ_SETUP_1_SEC_LSB ));
Austin Schuh208337d2022-01-01 14:29:11 -0800158
159 // Set the match enable bits for things we care about
160 if (t->year >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_YEAR_ENA_BITS);
161 if (t->month >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MONTH_ENA_BITS);
162 if (t->day >= 0) hw_set_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_DAY_ENA_BITS);
163 if (t->dotw >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_DOTW_ENA_BITS);
164 if (t->hour >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_HOUR_ENA_BITS);
165 if (t->min >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_MIN_ENA_BITS);
166 if (t->sec >= 0) hw_set_bits(&rtc_hw->irq_setup_1, RTC_IRQ_SETUP_1_SEC_ENA_BITS);
167
168 // Does it repeat? I.e. do we not match on any of the bits
169 _alarm_repeats = rtc_alarm_repeats(t);
170
171 // Store function pointer we can call later
172 _callback = user_callback;
173
174 irq_set_exclusive_handler(RTC_IRQ, rtc_irq_handler);
175
176 // Enable the IRQ at the peri
177 rtc_hw->inte = RTC_INTE_RTC_BITS;
178
179 // Enable the IRQ at the proc
180 irq_set_enabled(RTC_IRQ, true);
181
182 rtc_enable_alarm();
183}
184
185void rtc_disable_alarm(void) {
186 // Disable matching and wait for it to stop being active
187 hw_clear_bits(&rtc_hw->irq_setup_0, RTC_IRQ_SETUP_0_MATCH_ENA_BITS);
188 while(rtc_hw->irq_setup_0 & RTC_IRQ_SETUP_0_MATCH_ACTIVE_BITS) {
189 tight_loop_contents();
190 }
191}