1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Western Digital Corporation or its affiliates.
5 *
6 * Authors:
7 * Anup Patel <anup.patel@wdc.com>
8 */
9
10 #include <sbi/riscv_io.h>
11 #include <sbi_utils/serial/uart8250.h>
12
13 /* clang-format off */
14
15 #define UART_RBR_OFFSET 0 /* In: Recieve Buffer Register */
16 #define UART_THR_OFFSET 0 /* Out: Transmitter Holding Register */
17 #define UART_DLL_OFFSET 0 /* Out: Divisor Latch Low */
18 #define UART_IER_OFFSET 1 /* I/O: Interrupt Enable Register */
19 #define UART_DLM_OFFSET 1 /* Out: Divisor Latch High */
20 #define UART_FCR_OFFSET 2 /* Out: FIFO Control Register */
21 #define UART_IIR_OFFSET 2 /* I/O: Interrupt Identification Register */
22 #define UART_LCR_OFFSET 3 /* Out: Line Control Register */
23 #define UART_MCR_OFFSET 4 /* Out: Modem Control Register */
24 #define UART_LSR_OFFSET 5 /* In: Line Status Register */
25 #define UART_MSR_OFFSET 6 /* In: Modem Status Register */
26 #define UART_SCR_OFFSET 7 /* I/O: Scratch Register */
27 #define UART_MDR1_OFFSET 8 /* I/O: Mode Register */
28
29 #define UART_LSR_FIFOE 0x80 /* Fifo error */
30 #define UART_LSR_TEMT 0x40 /* Transmitter empty */
31 #define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
32 #define UART_LSR_BI 0x10 /* Break interrupt indicator */
33 #define UART_LSR_FE 0x08 /* Frame error indicator */
34 #define UART_LSR_PE 0x04 /* Parity error indicator */
35 #define UART_LSR_OE 0x02 /* Overrun error indicator */
36 #define UART_LSR_DR 0x01 /* Receiver data ready */
37 #define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */
38
39 /* clang-format on */
40
41 static volatile void *uart8250_base;
42 static u32 uart8250_in_freq;
43 static u32 uart8250_baudrate;
44 static u32 uart8250_reg_width;
45 static u32 uart8250_reg_shift;
46
get_reg(u32 num)47 static u32 get_reg(u32 num)
48 {
49 u32 offset = num << uart8250_reg_shift;
50
51 if (uart8250_reg_width == 1)
52 return readb(uart8250_base + offset);
53 else if (uart8250_reg_width == 2)
54 return readw(uart8250_base + offset);
55 else
56 return readl(uart8250_base + offset);
57 }
58
set_reg(u32 num,u32 val)59 static void set_reg(u32 num, u32 val)
60 {
61 u32 offset = num << uart8250_reg_shift;
62
63 if (uart8250_reg_width == 1)
64 writeb(val, uart8250_base + offset);
65 else if (uart8250_reg_width == 2)
66 writew(val, uart8250_base + offset);
67 else
68 writel(val, uart8250_base + offset);
69 }
70
uart8250_putc(char ch)71 void uart8250_putc(char ch)
72 {
73 while ((get_reg(UART_LSR_OFFSET) & UART_LSR_THRE) == 0)
74 ;
75
76 set_reg(UART_THR_OFFSET, ch);
77 }
78
uart8250_getc(void)79 int uart8250_getc(void)
80 {
81 if (get_reg(UART_LSR_OFFSET) & UART_LSR_DR)
82 return get_reg(UART_RBR_OFFSET);
83 return -1;
84 }
85
uart8250_init(unsigned long base,u32 in_freq,u32 baudrate,u32 reg_shift,u32 reg_width)86 int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
87 u32 reg_width)
88 {
89 u16 bdiv;
90
91 uart8250_base = (volatile void *)base;
92 uart8250_reg_shift = reg_shift;
93 uart8250_reg_width = reg_width;
94 uart8250_in_freq = in_freq;
95 uart8250_baudrate = baudrate;
96
97 bdiv = uart8250_in_freq / (16 * uart8250_baudrate);
98
99 /* Disable all interrupts */
100 set_reg(UART_IER_OFFSET, 0x00);
101 /* Enable DLAB */
102 set_reg(UART_LCR_OFFSET, 0x80);
103
104 if (bdiv) {
105 /* Set divisor low byte */
106 set_reg(UART_DLL_OFFSET, bdiv & 0xff);
107 /* Set divisor high byte */
108 set_reg(UART_DLM_OFFSET, (bdiv >> 8) & 0xff);
109 }
110
111 /* 8 bits, no parity, one stop bit */
112 set_reg(UART_LCR_OFFSET, 0x03);
113 /* Enable FIFO */
114 set_reg(UART_FCR_OFFSET, 0x01);
115 /* No modem control DTR RTS */
116 set_reg(UART_MCR_OFFSET, 0x00);
117 /* Clear line status */
118 get_reg(UART_LSR_OFFSET);
119 /* Read receive buffer */
120 get_reg(UART_RBR_OFFSET);
121 /* Set scratchpad */
122 set_reg(UART_SCR_OFFSET, 0x00);
123
124 return 0;
125 }
126