xref: /rk3399_ARM-atf/plat/brcm/board/common/bcm_elog.c (revision 926cd70a0cc3a0cbf209a87765a8dc0b869798e3)
1*f29d1e0cSSheetal Tigadoli /*
2*f29d1e0cSSheetal Tigadoli  * Copyright (c) 2018 - 2020, Broadcom
3*f29d1e0cSSheetal Tigadoli  *
4*f29d1e0cSSheetal Tigadoli  * SPDX-License-Identifier: BSD-3-Clause
5*f29d1e0cSSheetal Tigadoli  */
6*f29d1e0cSSheetal Tigadoli 
7*f29d1e0cSSheetal Tigadoli #include <stdarg.h>
8*f29d1e0cSSheetal Tigadoli #include <stdint.h>
9*f29d1e0cSSheetal Tigadoli #include <string.h>
10*f29d1e0cSSheetal Tigadoli 
11*f29d1e0cSSheetal Tigadoli #include <arch_helpers.h>
12*f29d1e0cSSheetal Tigadoli #include <common/debug.h>
13*f29d1e0cSSheetal Tigadoli #include <plat/common/platform.h>
14*f29d1e0cSSheetal Tigadoli 
15*f29d1e0cSSheetal Tigadoli #include <bcm_elog.h>
16*f29d1e0cSSheetal Tigadoli 
17*f29d1e0cSSheetal Tigadoli /* error logging signature */
18*f29d1e0cSSheetal Tigadoli #define BCM_ELOG_SIG_OFFSET      0x0000
19*f29d1e0cSSheetal Tigadoli #define BCM_ELOG_SIG_VAL         0x75767971
20*f29d1e0cSSheetal Tigadoli 
21*f29d1e0cSSheetal Tigadoli /* current logging offset that points to where new logs should be added */
22*f29d1e0cSSheetal Tigadoli #define BCM_ELOG_OFF_OFFSET      0x0004
23*f29d1e0cSSheetal Tigadoli 
24*f29d1e0cSSheetal Tigadoli /* current logging length (excluding header) */
25*f29d1e0cSSheetal Tigadoli #define BCM_ELOG_LEN_OFFSET      0x0008
26*f29d1e0cSSheetal Tigadoli 
27*f29d1e0cSSheetal Tigadoli #define BCM_ELOG_HEADER_LEN      12
28*f29d1e0cSSheetal Tigadoli 
29*f29d1e0cSSheetal Tigadoli /*
30*f29d1e0cSSheetal Tigadoli  * @base: base address of memory where log is saved
31*f29d1e0cSSheetal Tigadoli  * @max_size: max size of memory reserved for logging
32*f29d1e0cSSheetal Tigadoli  * @is_active: indicates logging is currently active
33*f29d1e0cSSheetal Tigadoli  * @level: current logging level
34*f29d1e0cSSheetal Tigadoli  */
35*f29d1e0cSSheetal Tigadoli struct bcm_elog {
36*f29d1e0cSSheetal Tigadoli 	uintptr_t base;
37*f29d1e0cSSheetal Tigadoli 	uint32_t max_size;
38*f29d1e0cSSheetal Tigadoli 	unsigned int is_active;
39*f29d1e0cSSheetal Tigadoli 	unsigned int level;
40*f29d1e0cSSheetal Tigadoli };
41*f29d1e0cSSheetal Tigadoli 
42*f29d1e0cSSheetal Tigadoli static struct bcm_elog global_elog;
43*f29d1e0cSSheetal Tigadoli 
44*f29d1e0cSSheetal Tigadoli extern void memcpy16(void *dst, const void *src, unsigned int len);
45*f29d1e0cSSheetal Tigadoli 
46*f29d1e0cSSheetal Tigadoli /*
47*f29d1e0cSSheetal Tigadoli  * Log one character
48*f29d1e0cSSheetal Tigadoli  */
elog_putchar(struct bcm_elog * elog,unsigned char c)49*f29d1e0cSSheetal Tigadoli static void elog_putchar(struct bcm_elog *elog, unsigned char c)
50*f29d1e0cSSheetal Tigadoli {
51*f29d1e0cSSheetal Tigadoli 	uint32_t offset, len;
52*f29d1e0cSSheetal Tigadoli 
53*f29d1e0cSSheetal Tigadoli 	offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
54*f29d1e0cSSheetal Tigadoli 	len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
55*f29d1e0cSSheetal Tigadoli 	mmio_write_8(elog->base + offset, c);
56*f29d1e0cSSheetal Tigadoli 	offset++;
57*f29d1e0cSSheetal Tigadoli 
58*f29d1e0cSSheetal Tigadoli 	/* log buffer is now full and need to wrap around */
59*f29d1e0cSSheetal Tigadoli 	if (offset >= elog->max_size)
60*f29d1e0cSSheetal Tigadoli 		offset = BCM_ELOG_HEADER_LEN;
61*f29d1e0cSSheetal Tigadoli 
62*f29d1e0cSSheetal Tigadoli 	/* only increment length when log buffer is not full */
63*f29d1e0cSSheetal Tigadoli 	if (len < elog->max_size - BCM_ELOG_HEADER_LEN)
64*f29d1e0cSSheetal Tigadoli 		len++;
65*f29d1e0cSSheetal Tigadoli 
66*f29d1e0cSSheetal Tigadoli 	mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
67*f29d1e0cSSheetal Tigadoli 	mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
68*f29d1e0cSSheetal Tigadoli }
69*f29d1e0cSSheetal Tigadoli 
elog_unsigned_num(struct bcm_elog * elog,unsigned long unum,unsigned int radix)70*f29d1e0cSSheetal Tigadoli static void elog_unsigned_num(struct bcm_elog *elog, unsigned long unum,
71*f29d1e0cSSheetal Tigadoli 			      unsigned int radix)
72*f29d1e0cSSheetal Tigadoli {
73*f29d1e0cSSheetal Tigadoli 	/* Just need enough space to store 64 bit decimal integer */
74*f29d1e0cSSheetal Tigadoli 	unsigned char num_buf[20];
75*f29d1e0cSSheetal Tigadoli 	int i = 0, rem;
76*f29d1e0cSSheetal Tigadoli 
77*f29d1e0cSSheetal Tigadoli 	do {
78*f29d1e0cSSheetal Tigadoli 		rem = unum % radix;
79*f29d1e0cSSheetal Tigadoli 		if (rem < 0xa)
80*f29d1e0cSSheetal Tigadoli 			num_buf[i++] = '0' + rem;
81*f29d1e0cSSheetal Tigadoli 		else
82*f29d1e0cSSheetal Tigadoli 			num_buf[i++] = 'a' + (rem - 0xa);
83*f29d1e0cSSheetal Tigadoli 	} while (unum /= radix);
84*f29d1e0cSSheetal Tigadoli 
85*f29d1e0cSSheetal Tigadoli 	while (--i >= 0)
86*f29d1e0cSSheetal Tigadoli 		elog_putchar(elog, num_buf[i]);
87*f29d1e0cSSheetal Tigadoli }
88*f29d1e0cSSheetal Tigadoli 
elog_string(struct bcm_elog * elog,const char * str)89*f29d1e0cSSheetal Tigadoli static void elog_string(struct bcm_elog *elog, const char *str)
90*f29d1e0cSSheetal Tigadoli {
91*f29d1e0cSSheetal Tigadoli 	while (*str)
92*f29d1e0cSSheetal Tigadoli 		elog_putchar(elog, *str++);
93*f29d1e0cSSheetal Tigadoli }
94*f29d1e0cSSheetal Tigadoli 
95*f29d1e0cSSheetal Tigadoli /*
96*f29d1e0cSSheetal Tigadoli  * Routine to initialize error logging
97*f29d1e0cSSheetal Tigadoli  */
bcm_elog_init(void * base,uint32_t size,unsigned int level)98*f29d1e0cSSheetal Tigadoli int bcm_elog_init(void *base, uint32_t size, unsigned int level)
99*f29d1e0cSSheetal Tigadoli {
100*f29d1e0cSSheetal Tigadoli 	struct bcm_elog *elog = &global_elog;
101*f29d1e0cSSheetal Tigadoli 	uint32_t val;
102*f29d1e0cSSheetal Tigadoli 
103*f29d1e0cSSheetal Tigadoli 	elog->base = (uintptr_t)base;
104*f29d1e0cSSheetal Tigadoli 	elog->max_size = size;
105*f29d1e0cSSheetal Tigadoli 	elog->is_active = 1;
106*f29d1e0cSSheetal Tigadoli 	elog->level = level / 10;
107*f29d1e0cSSheetal Tigadoli 
108*f29d1e0cSSheetal Tigadoli 	/*
109*f29d1e0cSSheetal Tigadoli 	 * If a valid signature can be found, it means logs have been copied
110*f29d1e0cSSheetal Tigadoli 	 * into designated memory by another software. In this case, we should
111*f29d1e0cSSheetal Tigadoli 	 * not re-initialize the entry header in the designated memory
112*f29d1e0cSSheetal Tigadoli 	 */
113*f29d1e0cSSheetal Tigadoli 	val = mmio_read_32(elog->base + BCM_ELOG_SIG_OFFSET);
114*f29d1e0cSSheetal Tigadoli 	if (val != BCM_ELOG_SIG_VAL) {
115*f29d1e0cSSheetal Tigadoli 		mmio_write_32(elog->base + BCM_ELOG_SIG_OFFSET,
116*f29d1e0cSSheetal Tigadoli 			      BCM_ELOG_SIG_VAL);
117*f29d1e0cSSheetal Tigadoli 		mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET,
118*f29d1e0cSSheetal Tigadoli 			      BCM_ELOG_HEADER_LEN);
119*f29d1e0cSSheetal Tigadoli 		mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, 0);
120*f29d1e0cSSheetal Tigadoli 	}
121*f29d1e0cSSheetal Tigadoli 
122*f29d1e0cSSheetal Tigadoli 	return 0;
123*f29d1e0cSSheetal Tigadoli }
124*f29d1e0cSSheetal Tigadoli 
125*f29d1e0cSSheetal Tigadoli /*
126*f29d1e0cSSheetal Tigadoli  * Routine to disable error logging
127*f29d1e0cSSheetal Tigadoli  */
bcm_elog_exit(void)128*f29d1e0cSSheetal Tigadoli void bcm_elog_exit(void)
129*f29d1e0cSSheetal Tigadoli {
130*f29d1e0cSSheetal Tigadoli 	struct bcm_elog *elog = &global_elog;
131*f29d1e0cSSheetal Tigadoli 
132*f29d1e0cSSheetal Tigadoli 	if (!elog->is_active)
133*f29d1e0cSSheetal Tigadoli 		return;
134*f29d1e0cSSheetal Tigadoli 
135*f29d1e0cSSheetal Tigadoli 	elog->is_active = 0;
136*f29d1e0cSSheetal Tigadoli 
137*f29d1e0cSSheetal Tigadoli 	flush_dcache_range(elog->base, elog->max_size);
138*f29d1e0cSSheetal Tigadoli }
139*f29d1e0cSSheetal Tigadoli 
140*f29d1e0cSSheetal Tigadoli /*
141*f29d1e0cSSheetal Tigadoli  * Routine to copy error logs from current memory to 'dst' memory and continue
142*f29d1e0cSSheetal Tigadoli  * logging from the new 'dst' memory.
143*f29d1e0cSSheetal Tigadoli  * dst and base addresses must be 16-bytes aligned.
144*f29d1e0cSSheetal Tigadoli  */
bcm_elog_copy_log(void * dst,uint32_t max_size)145*f29d1e0cSSheetal Tigadoli int bcm_elog_copy_log(void *dst, uint32_t max_size)
146*f29d1e0cSSheetal Tigadoli {
147*f29d1e0cSSheetal Tigadoli 	struct bcm_elog *elog = &global_elog;
148*f29d1e0cSSheetal Tigadoli 	uint32_t offset, len;
149*f29d1e0cSSheetal Tigadoli 
150*f29d1e0cSSheetal Tigadoli 	if (!elog->is_active || ((uintptr_t)dst == elog->base))
151*f29d1e0cSSheetal Tigadoli 		return -1;
152*f29d1e0cSSheetal Tigadoli 
153*f29d1e0cSSheetal Tigadoli 	/* flush cache before copying logs */
154*f29d1e0cSSheetal Tigadoli 	flush_dcache_range(elog->base, max_size);
155*f29d1e0cSSheetal Tigadoli 
156*f29d1e0cSSheetal Tigadoli 	/*
157*f29d1e0cSSheetal Tigadoli 	 * If current offset exceeds the new max size, then that is considered
158*f29d1e0cSSheetal Tigadoli 	 * as a buffer overflow situation. In this case, we reset the offset
159*f29d1e0cSSheetal Tigadoli 	 * back to the beginning
160*f29d1e0cSSheetal Tigadoli 	 */
161*f29d1e0cSSheetal Tigadoli 	offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
162*f29d1e0cSSheetal Tigadoli 	if (offset >= max_size) {
163*f29d1e0cSSheetal Tigadoli 		offset = BCM_ELOG_HEADER_LEN;
164*f29d1e0cSSheetal Tigadoli 		mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
165*f29d1e0cSSheetal Tigadoli 	}
166*f29d1e0cSSheetal Tigadoli 
167*f29d1e0cSSheetal Tigadoli 	/* note payload length does not include header */
168*f29d1e0cSSheetal Tigadoli 	len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
169*f29d1e0cSSheetal Tigadoli 	if (len > max_size - BCM_ELOG_HEADER_LEN) {
170*f29d1e0cSSheetal Tigadoli 		len = max_size - BCM_ELOG_HEADER_LEN;
171*f29d1e0cSSheetal Tigadoli 		mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
172*f29d1e0cSSheetal Tigadoli 	}
173*f29d1e0cSSheetal Tigadoli 
174*f29d1e0cSSheetal Tigadoli 	/* Need to copy everything including the header. */
175*f29d1e0cSSheetal Tigadoli 	memcpy16(dst, (const void *)elog->base, len + BCM_ELOG_HEADER_LEN);
176*f29d1e0cSSheetal Tigadoli 	elog->base = (uintptr_t)dst;
177*f29d1e0cSSheetal Tigadoli 	elog->max_size = max_size;
178*f29d1e0cSSheetal Tigadoli 
179*f29d1e0cSSheetal Tigadoli 	return 0;
180*f29d1e0cSSheetal Tigadoli }
181*f29d1e0cSSheetal Tigadoli 
182*f29d1e0cSSheetal Tigadoli /*
183*f29d1e0cSSheetal Tigadoli  * Main routine to save logs into memory
184*f29d1e0cSSheetal Tigadoli  */
bcm_elog(const char * fmt,...)185*f29d1e0cSSheetal Tigadoli void bcm_elog(const char *fmt, ...)
186*f29d1e0cSSheetal Tigadoli {
187*f29d1e0cSSheetal Tigadoli 	va_list args;
188*f29d1e0cSSheetal Tigadoli 	const char *prefix_str;
189*f29d1e0cSSheetal Tigadoli 	int bit64;
190*f29d1e0cSSheetal Tigadoli 	int64_t num;
191*f29d1e0cSSheetal Tigadoli 	uint64_t unum;
192*f29d1e0cSSheetal Tigadoli 	char *str;
193*f29d1e0cSSheetal Tigadoli 	struct bcm_elog *elog = &global_elog;
194*f29d1e0cSSheetal Tigadoli 
195*f29d1e0cSSheetal Tigadoli 	/* We expect the LOG_MARKER_* macro as the first character */
196*f29d1e0cSSheetal Tigadoli 	unsigned int level = fmt[0];
197*f29d1e0cSSheetal Tigadoli 
198*f29d1e0cSSheetal Tigadoli 	if (!elog->is_active || level > elog->level)
199*f29d1e0cSSheetal Tigadoli 		return;
200*f29d1e0cSSheetal Tigadoli 
201*f29d1e0cSSheetal Tigadoli 	prefix_str = plat_log_get_prefix(level);
202*f29d1e0cSSheetal Tigadoli 
203*f29d1e0cSSheetal Tigadoli 	while (*prefix_str != '\0') {
204*f29d1e0cSSheetal Tigadoli 		elog_putchar(elog, *prefix_str);
205*f29d1e0cSSheetal Tigadoli 		prefix_str++;
206*f29d1e0cSSheetal Tigadoli 	}
207*f29d1e0cSSheetal Tigadoli 
208*f29d1e0cSSheetal Tigadoli 	va_start(args, fmt);
209*f29d1e0cSSheetal Tigadoli 	fmt++;
210*f29d1e0cSSheetal Tigadoli 	while (*fmt) {
211*f29d1e0cSSheetal Tigadoli 		bit64 = 0;
212*f29d1e0cSSheetal Tigadoli 
213*f29d1e0cSSheetal Tigadoli 		if (*fmt == '%') {
214*f29d1e0cSSheetal Tigadoli 			fmt++;
215*f29d1e0cSSheetal Tigadoli 			/* Check the format specifier */
216*f29d1e0cSSheetal Tigadoli loop:
217*f29d1e0cSSheetal Tigadoli 			switch (*fmt) {
218*f29d1e0cSSheetal Tigadoli 			case 'i': /* Fall through to next one */
219*f29d1e0cSSheetal Tigadoli 			case 'd':
220*f29d1e0cSSheetal Tigadoli 				if (bit64)
221*f29d1e0cSSheetal Tigadoli 					num = va_arg(args, int64_t);
222*f29d1e0cSSheetal Tigadoli 				else
223*f29d1e0cSSheetal Tigadoli 					num = va_arg(args, int32_t);
224*f29d1e0cSSheetal Tigadoli 
225*f29d1e0cSSheetal Tigadoli 				if (num < 0) {
226*f29d1e0cSSheetal Tigadoli 					elog_putchar(elog, '-');
227*f29d1e0cSSheetal Tigadoli 					unum = (unsigned long)-num;
228*f29d1e0cSSheetal Tigadoli 				} else
229*f29d1e0cSSheetal Tigadoli 					unum = (unsigned long)num;
230*f29d1e0cSSheetal Tigadoli 
231*f29d1e0cSSheetal Tigadoli 				elog_unsigned_num(elog, unum, 10);
232*f29d1e0cSSheetal Tigadoli 				break;
233*f29d1e0cSSheetal Tigadoli 			case 's':
234*f29d1e0cSSheetal Tigadoli 				str = va_arg(args, char *);
235*f29d1e0cSSheetal Tigadoli 				elog_string(elog, str);
236*f29d1e0cSSheetal Tigadoli 				break;
237*f29d1e0cSSheetal Tigadoli 			case 'x':
238*f29d1e0cSSheetal Tigadoli 				if (bit64)
239*f29d1e0cSSheetal Tigadoli 					unum = va_arg(args, uint64_t);
240*f29d1e0cSSheetal Tigadoli 				else
241*f29d1e0cSSheetal Tigadoli 					unum = va_arg(args, uint32_t);
242*f29d1e0cSSheetal Tigadoli 
243*f29d1e0cSSheetal Tigadoli 				elog_unsigned_num(elog, unum, 16);
244*f29d1e0cSSheetal Tigadoli 				break;
245*f29d1e0cSSheetal Tigadoli 			case 'l':
246*f29d1e0cSSheetal Tigadoli 				bit64 = 1;
247*f29d1e0cSSheetal Tigadoli 				fmt++;
248*f29d1e0cSSheetal Tigadoli 				goto loop;
249*f29d1e0cSSheetal Tigadoli 			case 'u':
250*f29d1e0cSSheetal Tigadoli 				if (bit64)
251*f29d1e0cSSheetal Tigadoli 					unum = va_arg(args, uint64_t);
252*f29d1e0cSSheetal Tigadoli 				else
253*f29d1e0cSSheetal Tigadoli 					unum = va_arg(args, uint32_t);
254*f29d1e0cSSheetal Tigadoli 
255*f29d1e0cSSheetal Tigadoli 				elog_unsigned_num(elog, unum, 10);
256*f29d1e0cSSheetal Tigadoli 				break;
257*f29d1e0cSSheetal Tigadoli 			default:
258*f29d1e0cSSheetal Tigadoli 				/* Exit on any other format specifier */
259*f29d1e0cSSheetal Tigadoli 				goto exit;
260*f29d1e0cSSheetal Tigadoli 			}
261*f29d1e0cSSheetal Tigadoli 			fmt++;
262*f29d1e0cSSheetal Tigadoli 			continue;
263*f29d1e0cSSheetal Tigadoli 		}
264*f29d1e0cSSheetal Tigadoli 		elog_putchar(elog, *fmt++);
265*f29d1e0cSSheetal Tigadoli 	}
266*f29d1e0cSSheetal Tigadoli exit:
267*f29d1e0cSSheetal Tigadoli 	va_end(args);
268*f29d1e0cSSheetal Tigadoli }
269