blob: 7710c99760df0fa337ff3c7228cc70809debbb05 [file] [log] [blame]
jinkun.hongac490b82014-06-22 20:40:39 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 Rockchip Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
jinkun.hongac490b82014-06-22 20:40:39 -070020#include <arch/io.h>
21#include <boot/coreboot_tables.h>
22#include <console/console.h> /* for __console definition */
Julius Werner7a453eb2014-10-20 13:14:55 -070023#include <console/uart.h>
jinkun.hongac490b82014-06-22 20:40:39 -070024#include <drivers/uart/uart8250reg.h>
Julius Werner7a453eb2014-10-20 13:14:55 -070025#include <stdint.h>
jinkun.hongac490b82014-06-22 20:40:39 -070026
27/*
28 * TODO: Use DRIVERS_UART_8250MEM driver instead.
29 * There is an issue in the IO call functions where x86 and ARM
30 * ordering is reversed. This 8250MEM driver uses the x86 convention.
31 * This driver can be replaced once the IO calls are sorted.
32 */
33
34struct rk3288_uart {
35 union {
36 uint32_t thr; /* Transmit holding register. */
37 uint32_t rbr; /* Receive buffer register. */
38 uint32_t dll; /* Divisor latch lsb. */
39 };
40 union {
41 uint32_t ier; /* Interrupt enable register. */
42 uint32_t dlm; /* Divisor latch msb. */
43 };
44 union {
45 uint32_t iir; /* Interrupt identification register. */
46 uint32_t fcr; /* FIFO control register. */
47 };
48 uint32_t lcr; /* Line control register. */
49 uint32_t mcr; /* Modem control register. */
50 uint32_t lsr; /* Line status register. */
51 uint32_t msr; /* Modem status register. */
52 uint32_t scr;
53 uint32_t reserved1[(0x30 - 0x20) / 4];
54 uint32_t srbr[(0x70 - 0x30) / 4];
55 uint32_t far;
56 uint32_t tfr;
57 uint32_t rfw;
58 uint32_t usr;
59 uint32_t tfl;
60 uint32_t rfl;
61 uint32_t srr;
62 uint32_t srts;
63 uint32_t sbcr;
64 uint32_t sdmam;
65 uint32_t sfe;
66 uint32_t srt;
67 uint32_t stet;
68 uint32_t htx;
69 uint32_t dmasa;
70 uint32_t reserver2[(0xf4 - 0xac) / 4];
71 uint32_t cpr;
72 uint32_t ucv;
73 uint32_t ctr;
74} __attribute__ ((packed));
75
76
77static struct rk3288_uart * const uart_ptr =
78 (void *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
79
80static void rk3288_uart_tx_flush(void);
81static int rk3288_uart_tst_byte(void);
82
83static void rk3288_uart_init(void)
84{
85 /* FIXME: Use a hardcoded divisor for now.
86 * uint16_t divisor = (u16) uart_baudrate_divisor(default_baudrate(),
87 * uart_platform_refclk(), 16)
88 */
89 const unsigned divisor = 13;
90 const uint8_t line_config = UART8250_LCR_WLS_8; // 8n1
91
92 rk3288_uart_tx_flush();
93
94 // Disable interrupts.
Julius Werner2f37bd62015-02-19 14:51:15 -080095 write32(&uart_ptr->ier, 0);
jinkun.hongac490b82014-06-22 20:40:39 -070096 // Force DTR and RTS to high.
Julius Werner2f37bd62015-02-19 14:51:15 -080097 write32(&uart_ptr->mcr, UART8250_MCR_DTR | UART8250_MCR_RTS);
jinkun.hongac490b82014-06-22 20:40:39 -070098 // Set line configuration, access divisor latches.
Julius Werner2f37bd62015-02-19 14:51:15 -080099 write32(&uart_ptr->lcr, UART8250_LCR_DLAB | line_config);
jinkun.hongac490b82014-06-22 20:40:39 -0700100 // Set the divisor.
Julius Werner2f37bd62015-02-19 14:51:15 -0800101 write32(&uart_ptr->dll, divisor & 0xff);
102 write32(&uart_ptr->dlm, (divisor >> 8) & 0xff);
jinkun.hongac490b82014-06-22 20:40:39 -0700103 // Hide the divisor latches.
Julius Werner2f37bd62015-02-19 14:51:15 -0800104 write32(&uart_ptr->lcr, line_config);
jinkun.hongac490b82014-06-22 20:40:39 -0700105 // Enable FIFOs, and clear receive and transmit.
Julius Werner94184762015-02-19 20:19:23 -0800106 write32(&uart_ptr->fcr, UART8250_FCR_FIFO_EN |
107 UART8250_FCR_CLEAR_RCVR | UART8250_FCR_CLEAR_XMIT);
jinkun.hongac490b82014-06-22 20:40:39 -0700108}
109
110static void rk3288_uart_tx_byte(unsigned char data)
111{
Julius Werner2f37bd62015-02-19 14:51:15 -0800112 while (!(read32(&uart_ptr->lsr) & UART8250_LSR_THRE));
113 write32(&uart_ptr->thr, data);
jinkun.hongac490b82014-06-22 20:40:39 -0700114}
115
116static void rk3288_uart_tx_flush(void)
117{
Julius Werner2f37bd62015-02-19 14:51:15 -0800118 while (!(read32(&uart_ptr->lsr) & UART8250_LSR_TEMT));
jinkun.hongac490b82014-06-22 20:40:39 -0700119}
120
121static unsigned char rk3288_uart_rx_byte(void)
122{
123 if (!rk3288_uart_tst_byte())
124 return 0;
Julius Werner2f37bd62015-02-19 14:51:15 -0800125 return read32(&uart_ptr->rbr);
jinkun.hongac490b82014-06-22 20:40:39 -0700126}
127
128static int rk3288_uart_tst_byte(void)
129{
Julius Werner2f37bd62015-02-19 14:51:15 -0800130 return (read32(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR;
jinkun.hongac490b82014-06-22 20:40:39 -0700131}
132
133
134
135void uart_init(int idx)
136{
137 rk3288_uart_init();
138}
139
140unsigned char uart_rx_byte(int idx)
141{
142 return rk3288_uart_rx_byte();
143}
144
145void uart_tx_byte(int idx, unsigned char data)
146{
147 rk3288_uart_tx_byte(data);
148}
149
150void uart_tx_flush(int idx)
151{
152 rk3288_uart_tx_flush();
153}
154
155#ifndef __PRE_RAM__
156void uart_fill_lb(void *data)
157{
158 struct lb_serial serial;
159 serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED;
160 serial.baseaddr = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
161 serial.baud = default_baudrate();
Vadim Bendebury9dccf1c2015-01-09 16:54:19 -0800162 serial.regwidth = 1;
jinkun.hongac490b82014-06-22 20:40:39 -0700163 lb_add_serial(&serial, data);
164
165 lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data);
166}
167#endif