1 /*
2  * Copyright (c) 2018 Piotr Mienkowski
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /** @file
8  * @brief Serial Wire Output (SWO) backend implementation.
9  *
10  * SWO/SWV has been developed by ARM. The following code works only on ARM
11  * architecture.
12  *
13  * An SWO viewer program will typically set-up the SWO port including its
14  * frequency when connected to the debug probe. Such configuration can persist
15  * only until the MCU reset. The SWO backend initialization function will
16  * re-configure the SWO port upon boot and set the frequency as specified by
17  * the LOG_BACKEND_SWO_FREQ_HZ Kconfig option. To ensure flawless operation
18  * this frequency should much the one set by the SWO viewer program.
19  *
20  * The initialization code assumes that SWO core frequency is equal to HCLK
21  * as defined by the clock-frequency property in the CPU node. This may require
22  * additional, vendor specific configuration.
23  */
24 
25 #include <logging/log_backend.h>
26 #include <logging/log_core.h>
27 #include <logging/log_msg.h>
28 #include <logging/log_output.h>
29 #include <logging/log_backend_std.h>
30 #include <soc.h>
31 
32 /** The stimulus port from which SWO data is received and displayed */
33 #define ITM_PORT_LOGGER       0
34 
35 /* Set TPIU prescaler for the current debug trace clock frequency. */
36 #if CONFIG_LOG_BACKEND_SWO_FREQ_HZ == 0
37 #define SWO_FREQ_DIV  1
38 #else
39 
40 /* Set reference frequency which can be custom or cpu frequency. */
41 #if DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
42 #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), swo_ref_frequency)
43 #elif DT_NODE_HAS_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
44 #define SWO_REF_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
45 #else
46 #error "Missing DT 'clock-frequency' property on cpu@0 node"
47 #endif
48 
49 #define SWO_FREQ_DIV \
50 	((SWO_REF_FREQ + (CONFIG_LOG_BACKEND_SWO_FREQ_HZ / 2)) / \
51 		CONFIG_LOG_BACKEND_SWO_FREQ_HZ)
52 
53 #if SWO_FREQ_DIV > 0xFFFF
54 #error CONFIG_LOG_BACKEND_SWO_FREQ_HZ is too low. SWO clock divider is 16-bit. \
55 	Minimum supported SWO clock frequency is \
56 	[Reference Clock Frequency]/2^16.
57 #endif
58 
59 #endif
60 
61 static uint8_t buf[1];
62 
char_out(uint8_t * data,size_t length,void * ctx)63 static int char_out(uint8_t *data, size_t length, void *ctx)
64 {
65 	ARG_UNUSED(ctx);
66 
67 	for (size_t i = 0; i < length; i++) {
68 		ITM_SendChar(data[i]);
69 	}
70 
71 	return length;
72 }
73 
74 LOG_OUTPUT_DEFINE(log_output_swo, char_out, buf, sizeof(buf));
75 
log_backend_swo_put(const struct log_backend * const backend,struct log_msg * msg)76 static void log_backend_swo_put(const struct log_backend *const backend,
77 		struct log_msg *msg)
78 {
79 	uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
80 		LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
81 
82 	log_backend_std_put(&log_output_swo, flag, msg);
83 }
84 
log_backend_swo_process(const struct log_backend * const backend,union log_msg2_generic * msg)85 static void log_backend_swo_process(const struct log_backend *const backend,
86 				    union log_msg2_generic *msg)
87 {
88 	uint32_t flags = log_backend_std_get_flags();
89 
90 	log_output_msg2_process(&log_output_swo, &msg->log, flags);
91 }
92 
log_backend_swo_init(struct log_backend const * const backend)93 static void log_backend_swo_init(struct log_backend const *const backend)
94 {
95 	/* Enable DWT and ITM units */
96 	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
97 	/* Enable access to ITM registers */
98 	ITM->LAR  = 0xC5ACCE55;
99 	/* Disable stimulus ports ITM_STIM0-ITM_STIM31 */
100 	ITM->TER  = 0x0;
101 	/* Disable ITM */
102 	ITM->TCR  = 0x0;
103 	/* Select NRZ (UART) encoding protocol */
104 	TPI->SPPR = 2;
105 	/* Set SWO baud rate prescaler value: SWO_clk = ref_clock/(ACPR + 1) */
106 	TPI->ACPR = SWO_FREQ_DIV - 1;
107 	/* Enable unprivileged access to ITM stimulus ports */
108 	ITM->TPR  = 0x0;
109 	/* Configure Debug Watchpoint and Trace */
110 	DWT->CTRL &= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk | DWT_CTRL_CYCCNTENA_Msk);
111 	DWT->CTRL |= (DWT_CTRL_POSTPRESET_Msk | DWT_CTRL_POSTINIT_Msk);
112 	/* Configure Formatter and Flush Control Register */
113 	TPI->FFCR = 0x00000100;
114 	/* Enable ITM, set TraceBusID=1, no local timestamp generation */
115 	ITM->TCR  = 0x0001000D;
116 	/* Enable stimulus port used by the logger */
117 	ITM->TER  = 1 << ITM_PORT_LOGGER;
118 }
119 
log_backend_swo_panic(struct log_backend const * const backend)120 static void log_backend_swo_panic(struct log_backend const *const backend)
121 {
122 }
123 
dropped(const struct log_backend * const backend,uint32_t cnt)124 static void dropped(const struct log_backend *const backend, uint32_t cnt)
125 {
126 	ARG_UNUSED(backend);
127 
128 	log_backend_std_dropped(&log_output_swo, cnt);
129 }
130 
log_backend_swo_sync_string(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * fmt,va_list ap)131 static void log_backend_swo_sync_string(const struct log_backend *const backend,
132 		struct log_msg_ids src_level, uint32_t timestamp,
133 		const char *fmt, va_list ap)
134 {
135 	uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
136 		LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
137 
138 	log_backend_std_sync_string(&log_output_swo, flag, src_level,
139 				    timestamp, fmt, ap);
140 }
141 
log_backend_swo_sync_hexdump(const struct log_backend * const backend,struct log_msg_ids src_level,uint32_t timestamp,const char * metadata,const uint8_t * data,uint32_t length)142 static void log_backend_swo_sync_hexdump(
143 		const struct log_backend *const backend,
144 		struct log_msg_ids src_level, uint32_t timestamp,
145 		const char *metadata, const uint8_t *data, uint32_t length)
146 {
147 	uint32_t flag = IS_ENABLED(CONFIG_LOG_BACKEND_SWO_SYST_ENABLE) ?
148 		LOG_OUTPUT_FLAG_FORMAT_SYST : 0;
149 
150 	log_backend_std_sync_hexdump(&log_output_swo, flag, src_level,
151 				     timestamp, metadata, data, length);
152 }
153 
154 const struct log_backend_api log_backend_swo_api = {
155 	.process = IS_ENABLED(CONFIG_LOG2) ? log_backend_swo_process : NULL,
156 	.put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? log_backend_swo_put : NULL,
157 	.put_sync_string = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
158 			log_backend_swo_sync_string : NULL,
159 	.put_sync_hexdump = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ?
160 			log_backend_swo_sync_hexdump : NULL,
161 	.panic = log_backend_swo_panic,
162 	.init = log_backend_swo_init,
163 	.dropped = IS_ENABLED(CONFIG_LOG_IMMEDIATE) ? NULL : dropped,
164 };
165 
166 LOG_BACKEND_DEFINE(log_backend_swo, log_backend_swo_api, true);
167