summaryrefslogtreecommitdiff
path: root/src/soc/mediatek/mt8173/uart.c
blob: 961e3eb9be0edf2871c8edf780f53d4e011acd7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2015 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <arch/io.h>
#include <boot/coreboot_tables.h>
#include <console/console.h>	/* for __console definition */
#include <console/uart.h>
#include <drivers/uart/uart8250reg.h>
#include <stdint.h>
#include <compiler.h>

#include <soc/addressmap.h>

struct mtk_uart {
	union {
		uint32_t thr;	/* Transmit holding register. */
		uint32_t rbr;	/* Receive buffer register. */
		uint32_t dll;	/* Divisor latch lsb. */
	};
	union {
		uint32_t ier;	/* Interrupt enable register. */
		uint32_t dlm;	/* Divisor latch msb. */
	};
	union {
		uint32_t iir;	/* Interrupt identification register. */
		uint32_t fcr;	/* FIFO control register. */
		uint32_t efr;	/* Enhanced feature register. */
	};
	uint32_t lcr;		/* Line control register. */
	union {
		uint32_t mcr;	/* Modem control register. */
		uint32_t xn1;	/* XON1 */
	};
	union {
		uint32_t lsr;	/* Line status register. */
		uint32_t xn2;	/* XON2 */
	};
	union {
		uint32_t msr;	/* Modem status register. */
		uint32_t xf1;	/* XOFF1 */
	};
	union {
		uint32_t scr;	/* Scratch register. */
		uint32_t xf2;	/* XOFF2 */
	};
	uint32_t autobaud_en;	/* Enable auto baudrate. */
	uint32_t highspeed;	/* High speed UART. */
} __packed;

/* Peripheral Reset and Power Down registers */
struct mtk_peri_globalcon {
	uint32_t rst0;
	uint32_t rst1;
	uint32_t pdn0_set;
	uint32_t pdn1_set;
	uint32_t pdn0_clr;
	uint32_t pdn1_clr;
	uint32_t pdn0_sta;
	uint32_t pdn1_sta;
	uint32_t pdn_md1_set;
	uint32_t pdn_md2_set;
	uint32_t pdn_md1_clr;
	uint32_t pdn_md2_clr;
	uint32_t pdn_md1_sta;
	uint32_t pdn_md2_sta;
	uint32_t pdn_md_mask;
} __packed;

static struct mtk_uart *const uart_ptr = (void *)UART0_BASE;

static void mtk_uart_tx_flush(void);
static int mtk_uart_tst_byte(void);

static void mtk_uart_init(void)
{
	/* Use a hardcoded divisor for now. */
	const unsigned int uartclk = 26 * MHz;
	const unsigned int baudrate = get_uart_baudrate();
	const uint8_t line_config = UART8250_LCR_WLS_8;  /* 8n1 */
	unsigned int highspeed, quot, divisor, remainder;

	if (baudrate <= 115200) {
		highspeed = 0;
		quot = 16;
	} else {
		highspeed = 2;
		quot = 4;
	}

	/* Set divisor DLL and DLH  */
	divisor = uartclk / (quot * baudrate);
	remainder = uartclk % (quot * baudrate);

	if (remainder >= (quot / 2) * baudrate)
		divisor += 1;

	mtk_uart_tx_flush();

	/* Disable interrupts. */
	write8(&uart_ptr->ier, 0);
	/* Force DTR and RTS to high. */
	write8(&uart_ptr->mcr, UART8250_MCR_DTR | UART8250_MCR_RTS);
	/* Set High speed UART register. */
	write8(&uart_ptr->highspeed, highspeed);
	/* Set line configuration, access divisor latches. */
	write8(&uart_ptr->lcr, UART8250_LCR_DLAB | line_config);
	/* Set the divisor. */
	write8(&uart_ptr->dll, divisor & 0xff);
	write8(&uart_ptr->dlm, (divisor >> 8) & 0xff);
	/* Hide the divisor latches. */
	write8(&uart_ptr->lcr, line_config);
	/* Enable FIFOs, and clear receive and transmit. */
	write8(&uart_ptr->fcr,
	       UART8250_FCR_FIFO_EN | UART8250_FCR_CLEAR_RCVR |
	       UART8250_FCR_CLEAR_XMIT);

}

static void mtk_uart_tx_byte(unsigned char data)
{
	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_THRE))
		;
	write8(&uart_ptr->thr, data);
}

static void mtk_uart_tx_flush(void)
{
	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_TEMT))
		;
}

static unsigned char mtk_uart_rx_byte(void)
{
	if (!mtk_uart_tst_byte())
		return 0;
	return read8(&uart_ptr->rbr);
}

static int mtk_uart_tst_byte(void)
{
	return (read8(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR;
}

void uart_init(int idx)
{
	mtk_uart_init();
}

unsigned char uart_rx_byte(int idx)
{
	return mtk_uart_rx_byte();
}

void uart_tx_byte(int idx, unsigned char data)
{
	mtk_uart_tx_byte(data);
}

void uart_tx_flush(int idx)
{
	mtk_uart_tx_flush();
}

#ifndef __PRE_RAM__
void uart_fill_lb(void *data)
{
	struct lb_serial serial;
	serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED;
	serial.baseaddr = UART0_BASE;
	serial.baud = get_uart_baudrate();
	serial.regwidth = 4;
	lb_add_serial(&serial, data);

	lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data);
}
#endif