summaryrefslogtreecommitdiff
path: root/src/soc/marvell/armada38x/uart.c
blob: dbd8dca78f0817b5939d6dad6433aa135be85b61 (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2015 Google 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 <console/uart.h>
#include <console/console.h>
#include <drivers/uart/uart8250reg.h>
#include <boot/coreboot_tables.h>
#include <stdint.h>
#include <assert.h>
#include <soc/common.h>
#include <soc/clock.h>

struct armada38x_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 lcr; // Line control register.
	uint32_t mcr; // Modem control register.
	uint32_t lsr; // Line status register.
	uint32_t msr; // Modem status register.
} __attribute__ ((packed));

static void armada38x_uart_tx_flush(struct armada38x_uart *uart_ptr);
static int armada38x_uart_tst_byte(struct armada38x_uart *uart_ptr);

static void armada38x_uart_init(struct armada38x_uart *uart_ptr)
{
	const uint8_t line_config = UART8250_LCR_WLS_8;
	uint16_t divisor = (u16) uart_baudrate_divisor(default_baudrate(),
		uart_platform_refclk(), 16);

	armada38x_uart_tx_flush(uart_ptr);
	// Disable interrupts.
	write8(&uart_ptr->ier, 0);
	// Enable access to divisor latches.
	write8(&uart_ptr->lcr, UART8250_LCR_DLAB);
	// Set the divisor.
	write8(&uart_ptr->dll, divisor & 0xff);
	write8(&uart_ptr->dlm, (divisor >> 8) & 0xff);
	// Hide divisor latches and program line config.
	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 armada38x_uart_tx_byte(struct armada38x_uart *uart_ptr,
					unsigned char data)
{
	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_THRE))
		;
	write8(&uart_ptr->thr, data);
}

static void armada38x_uart_tx_flush(struct armada38x_uart *uart_ptr)
{
	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_TEMT))
		;
}

static unsigned char armada38x_uart_rx_byte(struct armada38x_uart *uart_ptr)
{
	if (!armada38x_uart_tst_byte(uart_ptr))
		return 0;
	return read8(&uart_ptr->rbr);
}

static int armada38x_uart_tst_byte(struct armada38x_uart *uart_ptr)
{
	return (read8(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR;
}

unsigned int uart_platform_refclk(void)
{
	return mv_tclk_get();
}

uintptr_t uart_platform_base(int idx)
{
	/* Default to UART 0 */
	unsigned int base = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;

	assert((idx >= 0) && (idx < 2));
	base += idx * 0x100;
	return base;
}

void uart_init(int idx)
{
	struct armada38x_uart *uart_ptr = uart_platform_baseptr(idx);

	armada38x_uart_init(uart_ptr);
}

void uart_tx_byte(int idx, unsigned char data)
{
	struct armada38x_uart *uart_ptr = uart_platform_baseptr(idx);

	armada38x_uart_tx_byte(uart_ptr, data);
}

void uart_tx_flush(int idx)
{
	struct armada38x_uart *uart_ptr = uart_platform_baseptr(idx);

	armada38x_uart_tx_flush(uart_ptr);
}

unsigned char uart_rx_byte(int idx)
{
	struct armada38x_uart *uart_ptr = uart_platform_baseptr(idx);

	return armada38x_uart_rx_byte(uart_ptr);
}

#if ENV_RAMSTAGE
void uart_fill_lb(void *data)
{
	struct lb_serial serial;

	serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED;
	serial.baseaddr = uart_platform_base(CONFIG_UART_FOR_CONSOLE);
	serial.baud = default_baudrate();
	serial.regwidth = 1;
	lb_add_serial(&serial, data);

	lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data);
}
#endif