summaryrefslogtreecommitdiff
path: root/chip/g/uartn.c
blob: 6140e8cff4e6663ebbab931bf4f384e2dd528a49 (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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "clock.h"
#include "common.h"
#include "gpio.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "uart.h"
#include "uart_bitbang.h"
#include "util.h"

#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
			       defined(SECTION_IS_RO)))

struct uartn_interrupts {
	int tx_int;
	int rx_int;
};
static struct uartn_interrupts interrupt[] = {
	{GC_IRQNUM_UART0_TXINT, GC_IRQNUM_UART0_RXINT},
	{GC_IRQNUM_UART1_TXINT, GC_IRQNUM_UART1_RXINT},
	{GC_IRQNUM_UART2_TXINT, GC_IRQNUM_UART2_RXINT},
};

struct uartn_function_ptrs uartn_funcs[3] = {
	{
		_uartn_rx_available,
		_uartn_write_char,
		_uartn_read_char,
	},

	{
		_uartn_rx_available,
		_uartn_write_char,
		_uartn_read_char,
	},

	{
		_uartn_rx_available,
		_uartn_write_char,
		_uartn_read_char,
	},
};

void uartn_tx_start(int uart)
{
	if (!uart_init_done())
		return;

	/* If interrupt is already enabled, nothing to do */
	if (GR_UART_ICTRL(uart) & GC_UART_ICTRL_TX_MASK)
		return;

	/* Do not allow deep sleep while transmit in progress */
	disable_sleep(SLEEP_MASK_UART);

	/*
	 * Re-enable the transmit interrupt, then forcibly trigger the
	 * interrupt.  This works around a hardware problem with the
	 * UART where the FIFO only triggers the interrupt when its
	 * threshold is _crossed_, not just met.
	 */
	/* TODO(crosbug.com/p/33819): Do we need this hack here? Find out. */
	REG_WRITE_MLV(GR_UART_ICTRL(uart), GC_UART_ICTRL_TX_MASK,
		      GC_UART_ICTRL_TX_LSB, 1);
	task_trigger_irq(interrupt[uart].tx_int);
}

void uartn_tx_stop(int uart)
{
	/* Disable the TX interrupt */
	REG_WRITE_MLV(GR_UART_ICTRL(uart), GC_UART_ICTRL_TX_MASK,
		      GC_UART_ICTRL_TX_LSB, 0);

	/* Re-allow deep sleep */
	enable_sleep(SLEEP_MASK_UART);
}

int uartn_tx_in_progress(int uart)
{
	/* Transmit is in progress unless the TX FIFO is empty and idle. */
	return !(GR_UART_STATE(uart) & (GC_UART_STATE_TXIDLE_MASK |
				     GC_UART_STATE_TXEMPTY_MASK));
}

void uartn_tx_flush(int uart)
{
	timestamp_t ts;
	int i;

	/* Wait until TX FIFO is idle. */
	while (uartn_tx_in_progress(uart))
		;
	/*
	 * Even when uartn_tx_in_progress() returns false, the chip seems to
	 * be still trasmitting, resetting at this point results in an eaten
	 * last symbol. Let's just wait some time (required to transmit 10
	 * bits at 115200 baud).
	 */
	ts = get_time(); /* Start time. */
	for (i = 0; i < 1000; i++) /* Limit it in case timer is not running. */
		if ((get_time().val - ts.val) > ((1000000 * 10) / 115200))
			return;
}

int uartn_tx_ready(int uart)
{
	/* True if the TX buffer is not completely full */
	return !(GR_UART_STATE(uart) & GC_UART_STATE_TX_MASK);
}

int _uartn_rx_available(int uart)
{
	/* True if the RX buffer is not completely empty. */
	return !(GR_UART_STATE(uart) & GC_UART_STATE_RXEMPTY_MASK);
}

int uartn_rx_available(int uart)
{
	return uartn_funcs[uart]._rx_available(uart);
}

void _uartn_write_char(int uart, char c)
{
	/* Wait for space in transmit FIFO. */
	while (!uartn_tx_ready(uart))
		;

	GR_UART_WDATA(uart) = c;
}

void uartn_write_char(int uart, char c)
{
	uartn_funcs[uart]._write_char(uart, c);
}

int _uartn_read_char(int uart)
{
	return GR_UART_RDATA(uart);
}

int uartn_read_char(int uart)
{
	return uartn_funcs[uart]._read_char(uart);
}

#ifdef CONFIG_UART_BITBANG
int _uart_bitbang_rx_available(int uart)
{
	if (uart_bitbang_is_enabled(uart))
		return uart_bitbang_is_char_available(uart);

	return 0;
}

void _uart_bitbang_write_char(int uart, char c)
{
	if (uart_bitbang_is_enabled(uart))
		uart_bitbang_write_char(uart, c);
}

int _uart_bitbang_read_char(int uart)
{
	if (uart_bitbang_is_enabled(uart))
		return uart_bitbang_read_char(uart);

	return 0;
}
#endif /* defined(CONFIG_UART_BITBANG) */

void uartn_disable_interrupt(int uart)
{
	task_disable_irq(interrupt[uart].tx_int);
	task_disable_irq(interrupt[uart].rx_int);
}

void uartn_enable_interrupt(int uart)
{
	task_enable_irq(interrupt[uart].tx_int);
	task_enable_irq(interrupt[uart].rx_int);
}


void uartn_enable(int uart)
{
	/* Enable TX and RX. Disable HW flow control and loopback. */
	GR_UART_CTRL(uart) = 0x03;
}

/* Disable TX, RX, HW flow control, and loopback */
void uartn_disable(int uart)
{
	GR_UART_CTRL(uart) = 0;
}

void uartn_init(int uart)
{
	long long setting = (16 * (1 << UART_NCO_WIDTH) *
			     (long long)CONFIG_UART_BAUD_RATE / PCLK_FREQ);

	/* set frequency */
	GR_UART_NCO(uart) = setting;

	/*
	 * Interrupt when RX fifo has anything, when TX fifo <= half
	 * empty and reset (clear) both FIFOs
	 */
	GR_UART_FIFO(uart) = 0x63;

	/* enable RX interrupts in block */
	/* Note: doesn't do anything unless turned on in NVIC */
	GR_UART_ICTRL(uart) = 0x02;

#if USE_UART_INTERRUPTS
	/* Enable interrupts for UART */
	uartn_enable_interrupt(uart);
#endif
}