blob: 98477344d32d7a45331c9f1a066599797a5a26a2 [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.
jinkun.hongac490b82014-06-22 20:40:39 -070014 */
15
jinkun.hongac490b82014-06-22 20:40:39 -070016#include <arch/io.h>
17#include <boot/coreboot_tables.h>
18#include <console/console.h> /* for __console definition */
Julius Werner7a453eb2014-10-20 13:14:55 -070019#include <console/uart.h>
jinkun.hongac490b82014-06-22 20:40:39 -070020#include <drivers/uart/uart8250reg.h>
Julius Werner7a453eb2014-10-20 13:14:55 -070021#include <stdint.h>
jinkun.hongac490b82014-06-22 20:40:39 -070022
23/*
24 * TODO: Use DRIVERS_UART_8250MEM driver instead.
25 * There is an issue in the IO call functions where x86 and ARM
26 * ordering is reversed. This 8250MEM driver uses the x86 convention.
27 * This driver can be replaced once the IO calls are sorted.
28 */
29
30struct rk3288_uart {
31 union {
32 uint32_t thr; /* Transmit holding register. */
33 uint32_t rbr; /* Receive buffer register. */
34 uint32_t dll; /* Divisor latch lsb. */
35 };
36 union {
37 uint32_t ier; /* Interrupt enable register. */
38 uint32_t dlm; /* Divisor latch msb. */
39 };
40 union {
41 uint32_t iir; /* Interrupt identification register. */
42 uint32_t fcr; /* FIFO control register. */
43 };
44 uint32_t lcr; /* Line control register. */
45 uint32_t mcr; /* Modem control register. */
46 uint32_t lsr; /* Line status register. */
47 uint32_t msr; /* Modem status register. */
48 uint32_t scr;
49 uint32_t reserved1[(0x30 - 0x20) / 4];
50 uint32_t srbr[(0x70 - 0x30) / 4];
51 uint32_t far;
52 uint32_t tfr;
53 uint32_t rfw;
54 uint32_t usr;
55 uint32_t tfl;
56 uint32_t rfl;
57 uint32_t srr;
58 uint32_t srts;
59 uint32_t sbcr;
60 uint32_t sdmam;
61 uint32_t sfe;
62 uint32_t srt;
63 uint32_t stet;
64 uint32_t htx;
65 uint32_t dmasa;
66 uint32_t reserver2[(0xf4 - 0xac) / 4];
67 uint32_t cpr;
68 uint32_t ucv;
69 uint32_t ctr;
70} __attribute__ ((packed));
71
72
73static struct rk3288_uart * const uart_ptr =
74 (void *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
75
76static void rk3288_uart_tx_flush(void);
77static int rk3288_uart_tst_byte(void);
78
79static void rk3288_uart_init(void)
80{
81 /* FIXME: Use a hardcoded divisor for now.
82 * uint16_t divisor = (u16) uart_baudrate_divisor(default_baudrate(),
83 * uart_platform_refclk(), 16)
84 */
85 const unsigned divisor = 13;
86 const uint8_t line_config = UART8250_LCR_WLS_8; // 8n1
87
88 rk3288_uart_tx_flush();
89
90 // Disable interrupts.
Julius Werner2f37bd62015-02-19 14:51:15 -080091 write32(&uart_ptr->ier, 0);
jinkun.hongac490b82014-06-22 20:40:39 -070092 // Force DTR and RTS to high.
Julius Werner2f37bd62015-02-19 14:51:15 -080093 write32(&uart_ptr->mcr, UART8250_MCR_DTR | UART8250_MCR_RTS);
jinkun.hongac490b82014-06-22 20:40:39 -070094 // Set line configuration, access divisor latches.
Julius Werner2f37bd62015-02-19 14:51:15 -080095 write32(&uart_ptr->lcr, UART8250_LCR_DLAB | line_config);
jinkun.hongac490b82014-06-22 20:40:39 -070096 // Set the divisor.
Julius Werner2f37bd62015-02-19 14:51:15 -080097 write32(&uart_ptr->dll, divisor & 0xff);
98 write32(&uart_ptr->dlm, (divisor >> 8) & 0xff);
jinkun.hongac490b82014-06-22 20:40:39 -070099 // Hide the divisor latches.
Julius Werner2f37bd62015-02-19 14:51:15 -0800100 write32(&uart_ptr->lcr, line_config);
jinkun.hongac490b82014-06-22 20:40:39 -0700101 // Enable FIFOs, and clear receive and transmit.
Julius Werner94184762015-02-19 20:19:23 -0800102 write32(&uart_ptr->fcr, UART8250_FCR_FIFO_EN |
103 UART8250_FCR_CLEAR_RCVR | UART8250_FCR_CLEAR_XMIT);
jinkun.hongac490b82014-06-22 20:40:39 -0700104}
105
106static void rk3288_uart_tx_byte(unsigned char data)
107{
Julius Werner2f37bd62015-02-19 14:51:15 -0800108 while (!(read32(&uart_ptr->lsr) & UART8250_LSR_THRE));
109 write32(&uart_ptr->thr, data);
jinkun.hongac490b82014-06-22 20:40:39 -0700110}
111
112static void rk3288_uart_tx_flush(void)
113{
Julius Werner2f37bd62015-02-19 14:51:15 -0800114 while (!(read32(&uart_ptr->lsr) & UART8250_LSR_TEMT));
jinkun.hongac490b82014-06-22 20:40:39 -0700115}
116
117static unsigned char rk3288_uart_rx_byte(void)
118{
119 if (!rk3288_uart_tst_byte())
120 return 0;
Julius Werner2f37bd62015-02-19 14:51:15 -0800121 return read32(&uart_ptr->rbr);
jinkun.hongac490b82014-06-22 20:40:39 -0700122}
123
124static int rk3288_uart_tst_byte(void)
125{
Julius Werner2f37bd62015-02-19 14:51:15 -0800126 return (read32(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR;
jinkun.hongac490b82014-06-22 20:40:39 -0700127}
128
129
130
131void uart_init(int idx)
132{
133 rk3288_uart_init();
134}
135
136unsigned char uart_rx_byte(int idx)
137{
138 return rk3288_uart_rx_byte();
139}
140
141void uart_tx_byte(int idx, unsigned char data)
142{
143 rk3288_uart_tx_byte(data);
144}
145
146void uart_tx_flush(int idx)
147{
148 rk3288_uart_tx_flush();
149}
150
151#ifndef __PRE_RAM__
152void uart_fill_lb(void *data)
153{
154 struct lb_serial serial;
155 serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED;
156 serial.baseaddr = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
157 serial.baud = default_baudrate();
Vadim Bendebury9dccf1c2015-01-09 16:54:19 -0800158 serial.regwidth = 1;
jinkun.hongac490b82014-06-22 20:40:39 -0700159 lb_add_serial(&serial, data);
160
161 lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data);
162}
163#endif