1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <nrfx_dppi.h>
7 #include <hal/nrf_ipc.h>
8 #include <helpers/nrfx_gppi.h>
9 #include <zephyr/drivers/timer/nrf_rtc_timer.h>
10 #include <zephyr/drivers/mbox.h>
11 #include <zephyr/init.h>
12 #include <zephyr/logging/log_ctrl.h>
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(sync_rtc, CONFIG_SYNC_RTC_LOG_LEVEL);
15
16 /* Arbitrary delay is used needed to handle cases when offset between cores is
17 * small and rtc synchronization process might not handle events on time.
18 * Setting high value prolongs synchronization process but setting too low may
19 * lead synchronization failure if offset between cores is small and/or there
20 * are significant interrupt handling latencies.
21 */
22 #define RTC_SYNC_ARBITRARY_DELAY 100
23
24 static uint32_t sync_cc;
25 static int32_t nrf53_sync_offset = -EBUSY;
26
27 union rtc_sync_channels {
28 uint32_t raw;
29 struct {
30 uint8_t ppi;
31 uint8_t rtc;
32 uint8_t ipc_out;
33 uint8_t ipc_in;
34 } ch;
35 };
36
37 /* Algorithm for establishing RTC offset on the network side.
38 *
39 * Assumptions:
40 * APP starts first thus its RTC is ahead. Only network will need to adjust its
41 * time. Because APP will capture the offset but NET needs it, algorithm
42 * consists of two stages: Getting offset on APP side, passing this offset to
43 * NET core. To keep it simple and independent from IPM protocols, value is passed
44 * using just IPC, PPI and RTC.
45 *
46 * 1st stage:
47 * APP: setup PPI connection from IPC_RECEIVE to RTC CAPTURE, enable interrupt
48 * IPC received.
49 * NET: setup RTC CC for arbitrary offset from now, setup PPI from RTC_COMPARE to IPC_SEND
50 * Record value set to CC.
51 *
52 * When APP will capture the value it needs to be passed to NET since it will be
53 * capable of calculating the offset since it know what counter value corresponds
54 * to the value captured on APP side.
55 *
56 * 2nd stage:
57 * APP: Sets Compare event for value = 2 * captured value + arbitrary offset
58 * NET: setup PPI from IPC_RECEIVE to RTC CAPTURE
59 *
60 * When NET RTC captures IPC event it takes CC value and knowing CC value previously
61 * used by NET and arbitrary offset (which is the same on APP and NET) is able
62 * to calculate exact offset between RTC counters.
63 *
64 * Note, arbitrary delay is used to accommodate for the case when NET-APP offset
65 * is small enough that interrupt latency would impact it. NET-APP offset depends
66 * on when NET core is reset and time when RTC system clock is initialized.
67 */
68
69 /* Setup or clear connection from IPC_RECEIVE to RTC_CAPTURE
70 *
71 * @param channels Details about channels
72 * @param setup If true connection is setup, else it is cleared.
73 */
ppi_ipc_to_rtc(union rtc_sync_channels channels,bool setup)74 static void ppi_ipc_to_rtc(union rtc_sync_channels channels, bool setup)
75 {
76 nrf_ipc_event_t ipc_evt = nrf_ipc_receive_event_get(channels.ch.ipc_in);
77 uint32_t task_addr = z_nrf_rtc_timer_capture_task_address_get(channels.ch.rtc);
78
79 if (setup) {
80 nrfx_gppi_task_endpoint_setup(channels.ch.ppi, task_addr);
81 nrf_ipc_publish_set(NRF_IPC, ipc_evt, channels.ch.ppi);
82 } else {
83 nrfx_gppi_task_endpoint_clear(channels.ch.ppi, task_addr);
84 nrf_ipc_publish_clear(NRF_IPC, ipc_evt);
85 }
86 }
87
88 /* Setup or clear connection from RTC_COMPARE to IPC_SEND
89 *
90 * @param channels Details about channels
91 * @param setup If true connection is setup, else it is cleared.
92 */
ppi_rtc_to_ipc(union rtc_sync_channels channels,bool setup)93 static void ppi_rtc_to_ipc(union rtc_sync_channels channels, bool setup)
94 {
95 uint32_t evt_addr = z_nrf_rtc_timer_compare_evt_address_get(channels.ch.rtc);
96 nrf_ipc_task_t ipc_task = nrf_ipc_send_task_get(channels.ch.ipc_out);
97
98 if (setup) {
99 nrf_ipc_subscribe_set(NRF_IPC, ipc_task, channels.ch.ppi);
100 nrfx_gppi_event_endpoint_setup(channels.ch.ppi, evt_addr);
101 } else {
102 nrfx_gppi_event_endpoint_clear(channels.ch.ppi, evt_addr);
103 nrf_ipc_subscribe_clear(NRF_IPC, ipc_task);
104 }
105 }
106
107 /* Free DPPI and RTC channels */
free_resources(union rtc_sync_channels channels)108 static void free_resources(union rtc_sync_channels channels)
109 {
110 nrfx_dppi_t dppi = NRFX_DPPI_INSTANCE(0);
111 nrfx_err_t err;
112
113 nrfx_gppi_channels_disable(BIT(channels.ch.ppi));
114
115 z_nrf_rtc_timer_chan_free(channels.ch.rtc);
116
117 err = nrfx_dppi_channel_free(&dppi, channels.ch.ppi);
118 __ASSERT_NO_MSG(err == NRFX_SUCCESS);
119 }
120
z_nrf_rtc_timer_nrf53net_offset_get(void)121 int z_nrf_rtc_timer_nrf53net_offset_get(void)
122 {
123 if (!IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUNET)) {
124 return -ENOSYS;
125 }
126
127 return nrf53_sync_offset;
128 }
129
rtc_cb(int32_t id,uint64_t cc_value,void * user_data)130 static void rtc_cb(int32_t id, uint64_t cc_value, void *user_data)
131 {
132 ARG_UNUSED(id);
133 ARG_UNUSED(cc_value);
134
135 union rtc_sync_channels channels;
136
137 channels.raw = (uint32_t)user_data;
138 ppi_rtc_to_ipc(channels, false);
139 if (IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)) {
140 /* APP: Synchronized completed */
141 free_resources(channels);
142 } else {
143 /* Compare event generated, reconfigure PPI and wait for
144 * IPC event from APP.
145 */
146 ppi_ipc_to_rtc(channels, true);
147 }
148 }
149
sync_rtc_timestamp_get(void)150 static log_timestamp_t sync_rtc_timestamp_get(void)
151 {
152 return (log_timestamp_t)(sys_clock_tick_get() + nrf53_sync_offset);
153 }
154
remote_callback(void * user_data)155 static void remote_callback(void *user_data)
156 {
157 extern const struct log_link *log_link_ipc_get_link(void);
158
159 union rtc_sync_channels channels;
160 uint32_t cc;
161
162 channels.raw = (uint32_t)user_data;
163
164 cc = z_nrf_rtc_timer_compare_read(channels.ch.rtc);
165
166 /* Clear previous task,event */
167 ppi_ipc_to_rtc(channels, false);
168
169 if (IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)) {
170 /* Setup new connection from RTC to IPC and set RTC to a new
171 * interval that contains captured offset.
172 */
173 ppi_rtc_to_ipc(channels, true);
174
175 z_nrf_rtc_timer_set(channels.ch.rtc, cc + cc + RTC_SYNC_ARBITRARY_DELAY,
176 rtc_cb, (void *)channels.raw);
177 } else {
178 /* Synchronization completed */
179 free_resources(channels);
180 nrf53_sync_offset = cc - RTC_SYNC_ARBITRARY_DELAY - 2 * sync_cc;
181 if (IS_ENABLED(CONFIG_NRF53_SYNC_RTC_LOG_TIMESTAMP)) {
182 uint32_t offset_us =
183 (uint64_t)nrf53_sync_offset * 1000000 /
184 sys_clock_hw_cycles_per_sec();
185
186 log_set_timestamp_func(sync_rtc_timestamp_get,
187 sys_clock_hw_cycles_per_sec());
188 LOG_INF("Updated timestamp to synchronized RTC by %d ticks (%dus)",
189 nrf53_sync_offset, offset_us);
190 }
191 }
192 }
193
mbox_callback(const struct device * dev,mbox_channel_id_t channel_id,void * user_data,struct mbox_msg * data)194 static void mbox_callback(const struct device *dev, mbox_channel_id_t channel_id,
195 void *user_data, struct mbox_msg *data)
196 {
197 int err;
198
199 err = mbox_set_enabled(dev, channel_id, false);
200
201 (void)err;
202 __ASSERT_NO_MSG(err == 0);
203
204 remote_callback(user_data);
205 }
206
mbox_rx_init(void * user_data)207 static int mbox_rx_init(void *user_data)
208 {
209 const struct device *dev;
210 int err;
211
212 dev = COND_CODE_1(CONFIG_MBOX, (DEVICE_DT_GET(DT_NODELABEL(mbox))), (NULL));
213 if (dev == NULL) {
214 return -ENODEV;
215 }
216
217 err = mbox_register_callback(dev, CONFIG_NRF53_SYNC_RTC_IPM_IN, mbox_callback, user_data);
218 if (err < 0) {
219 return err;
220 }
221
222 return mbox_set_enabled(dev, CONFIG_NRF53_SYNC_RTC_IPM_IN, true);
223 }
224
225 /* Setup RTC synchronization. */
sync_rtc_setup(void)226 static int sync_rtc_setup(void)
227 {
228 nrfx_dppi_t dppi = NRFX_DPPI_INSTANCE(0);
229 nrfx_err_t err;
230 union rtc_sync_channels channels;
231 int32_t sync_rtc_ch;
232 int rv;
233
234 err = nrfx_dppi_channel_alloc(&dppi, &channels.ch.ppi);
235 if (err != NRFX_SUCCESS) {
236 rv = -ENODEV;
237 goto bail;
238 }
239
240 sync_rtc_ch = z_nrf_rtc_timer_chan_alloc();
241 if (sync_rtc_ch < 0) {
242 nrfx_dppi_channel_free(&dppi, channels.ch.ppi);
243 rv = sync_rtc_ch;
244 goto bail;
245 }
246
247 channels.ch.rtc = (uint8_t)sync_rtc_ch;
248 channels.ch.ipc_out = CONFIG_NRF53_SYNC_RTC_IPM_OUT;
249 channels.ch.ipc_in = CONFIG_NRF53_SYNC_RTC_IPM_IN;
250
251 rv = mbox_rx_init((void *)channels.raw);
252 if (rv < 0) {
253 goto bail;
254 }
255
256 nrfx_gppi_channels_enable(BIT(channels.ch.ppi));
257
258 if (IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP)) {
259 ppi_ipc_to_rtc(channels, true);
260 } else {
261 ppi_rtc_to_ipc(channels, true);
262
263 uint32_t key = irq_lock();
264
265 sync_cc = z_nrf_rtc_timer_read() + RTC_SYNC_ARBITRARY_DELAY;
266 z_nrf_rtc_timer_set(channels.ch.rtc, sync_cc, rtc_cb, (void *)channels.raw);
267 irq_unlock(key);
268 }
269
270 bail:
271 if (rv != 0) {
272 LOG_ERR("Failed synchronized RTC setup (err: %d)", rv);
273 }
274
275 return rv;
276 }
277
278 #if defined(CONFIG_MBOX_INIT_PRIORITY)
279 BUILD_ASSERT(CONFIG_NRF53_SYNC_RTC_INIT_PRIORITY > CONFIG_MBOX_INIT_PRIORITY,
280 "RTC Sync must be initialized after MBOX driver.");
281 #endif
282
283 SYS_INIT(sync_rtc_setup, POST_KERNEL, CONFIG_NRF53_SYNC_RTC_INIT_PRIORITY);
284