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