coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
uart.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
5  */
6 
7 #include <device/mmio.h>
8 #include <console/uart.h>
9 #include <delay.h>
10 #include <endian.h>
11 #include <stdint.h>
12 #include <soc/clock.h>
13 #include <soc/uart.h>
14 #include <assert.h>
15 #include <soc/addressmap.h>
16 #include <drivers/uart/pl011.h>
17 
19  u64 u;
20  struct {
22  u64 uaa_rst : 1;
23  u64 : 2;
25  u64 : 19;
27  u64 : 1;
31  u64 : 33;
32  } s;
33 };
34 
35 struct cn81xx_uart {
36  struct pl011_uart pl011;
38  u8 rsvd4[0x8];
40  u8 rsvd5[0xe0];
42 };
43 
44 #define UART_IBRD_BAUD_DIVINT_SHIFT 0
45 #define UART_IBRD_BAUD_DIVINT_MASK 0xffff
46 
47 #define UART_FBRD_BAUD_DIVFRAC_SHIFT 0
48 #define UART_FBRD_BAUD_DIVFRAC_MASK 0x3f
49 
50 check_member(cn81xx_uart, uctl_ctl, 0x1000);
51 check_member(cn81xx_uart, uctl_spare1, 0x10f8);
52 
53 #define UART_SCLK_DIV 3
54 
55 /**
56  * Returns the current UART HCLK divider
57  *
58  * @param reg The H_CLKDIV_SEL value
59  * @return The HCLK divider
60  */
61 static size_t uart_sclk_divisor(const size_t reg)
62 {
63  static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32};
64 
65  assert(reg < ARRAY_SIZE(div));
66 
67  return div[reg];
68 }
69 
70 /**
71  * Returns the current UART HCLK
72  *
73  * @param uart The UART to operate on
74  * @return The HCLK in Hz
75  */
76 static size_t uart_hclk(struct cn81xx_uart *uart)
77 {
78  union cn81xx_uart_ctl ctl;
79  const uint64_t sclk = thunderx_get_io_clock();
80 
81  ctl.u = read64(&uart->uctl_ctl);
82  return sclk / uart_sclk_divisor(ctl.s.h_clkdiv_sel);
83 }
84 
85 unsigned int uart_platform_refclk(void)
86 {
87  struct cn81xx_uart *uart =
88  (struct cn81xx_uart *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
89 
90  if (!uart)
91  return 0;
92 
93  return uart_hclk(uart);
94 }
95 
96 uintptr_t uart_platform_base(unsigned int idx)
97 {
98  return CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
99 }
100 
101 /**
102  * Waits given count if HCLK cycles
103  *
104  * @param uart The UART to operate on
105  * @param hclks The number of HCLK cycles to wait
106  */
107 static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks)
108 {
109  const size_t hclk = uart_hclk(uart);
110  const size_t delay = (hclks * 1000000ULL) / hclk;
111  udelay(MAX(delay, 1));
112 }
113 
114 /**
115  * Returns the UART state.
116  *
117  * @param bus The UART to operate on
118  * @return Boolean: True if UART is enabled
119  */
120 int uart_is_enabled(const size_t bus)
121 {
122  struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
123  union cn81xx_uart_ctl ctl;
124 
125  assert(uart);
126  if (!uart)
127  return 0;
128 
129  ctl.u = read64(&uart->uctl_ctl);
130  return !!ctl.s.csclk_en;
131 }
132 
133 /**
134  * Setup UART with desired BAUD rate in 8N1, no parity mode.
135  *
136  * @param bus The UART to operate on
137  * @param baudrate baudrate to set up
138  *
139  * @return Boolean: True on error
140  */
141 int uart_setup(const size_t bus, int baudrate)
142 {
143  union cn81xx_uart_ctl ctl;
144  struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
145 
146  assert(uart);
147  if (!uart)
148  return 1;
149 
150  /* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */
151  /* 1. Wait for IOI reset (srst_n) to deassert. */
152 
153  /**
154  * 2. Assert all resets:
155  * a. UAA reset: UCTL_CTL[UAA_RST] = 1
156  * b. UCTL reset: UCTL_CTL[UCTL_RST] = 1
157  */
158  ctl.u = read64(&uart->uctl_ctl);
159  ctl.s.uctl_rst = 1;
160  ctl.s.uaa_rst = 1;
161  write64(&uart->uctl_ctl, ctl.u);
162 
163  /**
164  * 3. Configure the HCLK:
165  * a. Reset the clock dividers: UCTL_CTL[H_CLKDIV_RST] = 1.
166  * b. Select the HCLK frequency
167  * i. UCTL_CTL[H_CLKDIV] = desired value,
168  * ii. UCTL_CTL[H_CLKDIV_EN] = 1 to enable the HCLK.
169  * iii. Readback UCTL_CTL to ensure the values take effect.
170  * c. Deassert the HCLK clock divider reset: UCTL_CTL[H_CLKDIV_RST] = 0.
171  */
172  ctl.u = read64(&uart->uctl_ctl);
174  write64(&uart->uctl_ctl, ctl.u);
175 
176  ctl.u = read64(&uart->uctl_ctl);
177  ctl.s.h_clk_byp_sel = 0;
178  write64(&uart->uctl_ctl, ctl.u);
179 
180  ctl.u = read64(&uart->uctl_ctl);
181  ctl.s.h_clk_en = 1;
182  write64(&uart->uctl_ctl, ctl.u);
183 
184  ctl.u = read64(&uart->uctl_ctl);
185  ctl.s.h_clkdiv_rst = 0;
186  write64(&uart->uctl_ctl, ctl.u);
187 
188  /**
189  * 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo
190  * to properly reset.
191  */
192  uart_wait_hclk(uart, 20 + 1);
193 
194  /**
195  * 5. Deassert UCTL and UAHC resets:
196  * a. UCTL_CTL[UCTL_RST] = 0
197  * b. Wait 10 HCLK cycles.
198  * c. UCTL_CTL[UAHC_RST] = 0
199  * d. You will have to wait 10 HCLK cycles before accessing any
200  * HCLK-only registers.
201  */
202  ctl.u = read64(&uart->uctl_ctl);
203  ctl.s.uctl_rst = 0;
204  write64(&uart->uctl_ctl, ctl.u);
205 
206  uart_wait_hclk(uart, 10 + 1);
207 
208  ctl.u = read64(&uart->uctl_ctl);
209  ctl.s.uaa_rst = 0;
210  write64(&uart->uctl_ctl, ctl.u);
211 
212  uart_wait_hclk(uart, 10 + 1);
213 
214  /**
215  * 6. Enable conditional SCLK of UCTL by writing
216  * UCTL_CTL[CSCLK_EN] = 1.
217  */
218  ctl.u = read64(&uart->uctl_ctl);
219  ctl.s.csclk_en = 1;
220  write64(&uart->uctl_ctl, ctl.u);
221 
222  /**
223  * Exit here if the UART is not going to be used in coreboot.
224  * The previous initialization steps are sufficient to make the Linux
225  * kernel not panic.
226  */
227  if (!baudrate)
228  return 0;
229 
230  /**
231  * 7. Initialize the integer and fractional baud rate divider registers
232  * UARTIBRD and UARTFBRD as follows:
233  * a. Baud Rate Divisor = UARTCLK/(16xBaud Rate) = BRDI + BRDF
234  * b. The fractional register BRDF, m is calculated as
235  * integer(BRDF x 64 + 0.5)
236  * Example calculation:
237  * If the required baud rate is 230400 and hclk = 4MHz then:
238  * Baud Rate Divisor = (4x10^6)/(16x230400) = 1.085
239  * This means BRDI = 1 and BRDF = 0.085.
240  * Therefore, fractional part, BRDF = integer((0.085x64)+0.5) = 5
241  * Generated baud rate divider = 1+5/64 = 1.078
242  */
243  u64 divisor = thunderx_get_io_clock() /
244  (baudrate * 16 * uart_sclk_divisor(UART_SCLK_DIV) / 64);
245  write32(&uart->pl011.ibrd, divisor >> 6);
246  write32(&uart->pl011.fbrd, divisor & UART_FBRD_BAUD_DIVFRAC_MASK);
247 
248  /**
249  * 8. Program the line control register UAA(0..1)_LCR_H and the control
250  * register UAA(0..1)_CR
251  */
252  /* 8-bits, FIFO enable */
255  /* RX/TX enable, UART enable */
258 
259  return 0;
260 }
static void write32(void *addr, uint32_t val)
Definition: mmio.h:40
void write64(void *addr, uint64_t val)
uint64_t read64(const void *addr)
#define assert(statement)
Definition: assert.h:74
#define ARRAY_SIZE(a)
Definition: helpers.h:12
#define MAX(a, b)
Definition: helpers.h:40
void delay(unsigned int secs)
Definition: delay.c:8
uintptr_t uart_platform_base(unsigned int idx)
Definition: uart.c:8
#define PL011_UARTCR_RXE
Definition: pl011.h:61
#define PL011_UARTCR_TXE
Definition: pl011.h:62
#define PL011_UARTLCR_H_FEN
Definition: pl011.h:75
#define PL011_UARTCR_UARTEN
Definition: pl011.h:64
#define PL011_UARTLCR_H_WLEN_8
Definition: pl011.h:71
u64 thunderx_get_io_clock(void)
Returns the I/O clock speed in Hz.
Definition: clock.c:45
#define UAAx_PF_BAR0(x)
Definition: addressmap.h:94
static size_t uart_sclk_divisor(const size_t reg)
Returns the current UART HCLK divider.
Definition: uart.c:61
#define UART_FBRD_BAUD_DIVFRAC_MASK
Definition: uart.c:48
check_member(cn81xx_uart, uctl_ctl, 0x1000)
static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks)
Waits given count if HCLK cycles.
Definition: uart.c:107
int uart_setup(const size_t bus, int baudrate)
Setup UART with desired BAUD rate in 8N1, no parity mode.
Definition: uart.c:141
unsigned int uart_platform_refclk(void)
Definition: uart.c:85
int uart_is_enabled(const size_t bus)
Returns the UART state.
Definition: uart.c:120
static size_t uart_hclk(struct cn81xx_uart *uart)
Returns the current UART HCLK.
Definition: uart.c:76
#define UART_SCLK_DIV
Definition: uart.c:53
uint64_t u64
Definition: stdint.h:54
unsigned long uintptr_t
Definition: stdint.h:21
unsigned long long uint64_t
Definition: stdint.h:17
uint8_t u8
Definition: stdint.h:45
Definition: device.h:76
union cn81xx_uart_ctl uctl_ctl
Definition: uart.c:37
u8 rsvd4[0x8]
Definition: uart.c:38
u8 rsvd5[0xe0]
Definition: uart.c:40
u64 uctl_spare0
Definition: uart.c:39
struct pl011_uart pl011
Definition: uart.c:36
u64 uctl_spare1
Definition: uart.c:41
u32 fbrd
Definition: pl011.h:17
u32 ibrd
Definition: pl011.h:16
u32 cr
Definition: pl011.h:19
u32 lcr_h
Definition: pl011.h:18
void udelay(uint32_t us)
Definition: udelay.c:15
u64 csclk_en
Definition: uart.c:24
u64 h_clk_byp_sel
Definition: uart.c:29
u64 h_clkdiv_rst
Definition: uart.c:28
u64 uaa_rst
Definition: uart.c:22
u64 h_clk_en
Definition: uart.c:30
u64 uctl_rst
Definition: uart.c:21
struct cn81xx_uart_ctl::@445 s
u64 h_clkdiv_sel
Definition: uart.c:26