1 /*
2 * Copyright (c) 2015 Brian Swetland
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <lk/debug.h>
10 #include <lk/reg.h>
11 #include <kernel/thread.h>
12 #include <lib/cbuf.h>
13
14 #include <arch/arm/cm.h>
15 #include <platform/lpc43xx-uart.h>
16 #include <platform/lpc43xx-clocks.h>
17
18 static cbuf_t console_rx_buf;
19
20 #ifndef TARGET_DEBUG_BAUDRATE
21 #define TARGET_DEBUG_BAUDRATE 115200
22 #endif
23
24 #if TARGET_DEBUG_UART == 1
25 #define UART_BASE UART0_BASE
26 #define UART_IRQ lpc43xx_USART0_IRQ
27 #define UART_IRQn USART0_IRQn
28 #elif TARGET_DEBUG_UART == 2
29 #define UART_BASE UART1_BASE
30 #define UART_IRQ lpc43xx_UART1_IRQ
31 #define UART_IRQn UART1_IRQn
32 #elif TARGET_DEBUG_UART == 3
33 #define UART_BASE UART2_BASE
34 #define UART_IRQ lpc43xx_USART2_IRQ
35 #define UART_IRQn USART2_IRQn
36 #elif TARGET_DEBUG_UART == 4
37 #define UART_BASE UART3_BASE
38 #define UART_IRQ lpc43xx_USART3_IRQ
39 #define UART_IRQn USART3_IRQn
40 #else
41 #warning TARGET_DEBUG_UART unspecified
42 #endif
43
44 static u32 base_uart_clk[4] = {
45 BASE_UART0_CLK,
46 BASE_UART1_CLK,
47 BASE_UART2_CLK,
48 BASE_UART3_CLK
49 };
50
51 extern uint8_t __lpc43xx_main_clock_sel;
52 extern uint32_t __lpc43xx_main_clock_mhz;
53
54 #define ITM_STIM0 0xE0000000
55 #define ITM_TER 0xE0000E00
56 #define ITM_TCR 0xE0000E80
57 #define ITM_LAR 0xE0000FB0
58
59 #define TPI_ACPR 0xE0040010
60 #define TPI_SPPR 0xE00400F0
61 #define TPI_FFCR 0xE0040304
62
63 #define DEMCR 0xE000EDFC
64 #define DEMCR_TRCENA (1 << 24)
65
lpc43xx_debug_early_init(void)66 void lpc43xx_debug_early_init(void) {
67 // ensure ITM and DWT are enabled
68 writel(readl(DEMCR) | DEMCR_TRCENA, DEMCR);
69
70 writel((1 << 9) | (1 << 16) | (2 << 10), DWT_CTRL);
71
72 // configure TPIU for one-wire, nrz, 6mbps
73 writel((__lpc43xx_main_clock_mhz / 6000000) - 1, TPI_ACPR);
74 writel(2, TPI_SPPR);
75 writel(0x100, TPI_FFCR);
76
77 // configure ITM
78 writel(0xC5ACCE55, ITM_LAR); // unlock regs
79 writel(0x0001000D, ITM_TCR); // ID=1, enable ITM, SYNC, DWT events
80 writel(0xFFFFFFFF, ITM_TER); // enable all trace ports
81
82 #ifdef UART_BASE
83 #if TARGET_DEBUG_BAUDRATE == 115200
84 // config for 115200-n-8-1 from 12MHz clock
85 writel(BASE_CLK_SEL(CLK_IRC), base_uart_clk[TARGET_DEBUG_UART - 1]);
86 writel(LCR_DLAB, UART_BASE + REG_LCR);
87 writel(4, UART_BASE + REG_DLL);
88 writel(0, UART_BASE + REG_DLM);
89 writel(FDR_DIVADDVAL(5) | FDR_MULVAL(8), UART_BASE + REG_FDR);
90 #else
91 uint32_t div = __lpc43xx_main_clock_mhz / 16 / TARGET_DEBUG_BAUDRATE;
92 writel(BASE_CLK_SEL(__lpc43xx_main_clock_sel),
93 base_uart_clk[TARGET_DEBUG_UART - 1]);
94 writel(LCR_DLAB, UART_BASE + REG_LCR);
95 writel(div & 0xFF, UART_BASE + REG_DLL);
96 writel((div >> 8) & 0xFF, UART_BASE + REG_DLM);
97 #endif
98 writel(LCR_WLS_8 | LCR_SBS_1, UART_BASE + REG_LCR);
99 writel(FCR_FIFOEN | FCR_RX_TRIG_1, UART_BASE + REG_FCR);
100 writel(IER_RBRIE, UART_BASE + REG_IER);
101 NVIC_EnableIRQ(UART_IRQn);
102 #endif
103 }
104
lpc43xx_debug_init(void)105 void lpc43xx_debug_init(void) {
106 cbuf_initialize(&console_rx_buf, 64);
107 }
108
109 #ifdef UART_BASE
UART_IRQ(void)110 void UART_IRQ (void) {
111 arm_cm_irq_entry();
112 while (readl(UART_BASE + REG_LSR) & LSR_RDR) {
113 unsigned c = readl(UART_BASE + REG_RBR);
114 if (cbuf_space_avail(&console_rx_buf)) {
115 cbuf_write_char(&console_rx_buf, c, false);
116 }
117 }
118 arm_cm_irq_exit(1);
119 }
120 #endif
121
platform_dputc(char c)122 void platform_dputc(char c) {
123 // if ITM is enabled, send character to STIM0
124 if (readl(ITM_TCR) & 1) {
125 while (!readl(ITM_STIM0)) ;
126 writeb(c, ITM_STIM0);
127 }
128 #ifdef UART_BASE
129 while (!(readl(UART_BASE + REG_LSR) & LSR_THRE)) ;
130 writel(c, UART_BASE + REG_THR);
131 #endif
132 }
133
platform_dgetc(char * c,bool wait)134 int platform_dgetc(char *c, bool wait) {
135 if (cbuf_read_char(&console_rx_buf, c, wait) == 0)
136 return -1;
137 return 0;
138 }
139
140 #define DCRDR 0xE000EDF8
141
_debugmonitor(void)142 void _debugmonitor(void) {
143 u32 n;
144 arm_cm_irq_entry();
145 n = readl(DCRDR);
146 if (n & 0x80000000) {
147 switch (n >> 24) {
148 case 0x80: // write to console
149 if (cbuf_space_avail(&console_rx_buf)) {
150 cbuf_write_char(&console_rx_buf, n & 0xFF, false);
151 }
152 n = 0;
153 break;
154 default:
155 n = 0x01000000;
156 }
157 writel(n, DCRDR);
158 }
159 arm_cm_irq_exit(1);
160 }
161