summaryrefslogtreecommitdiffstats
path: root/drivers/serial/serial_bcm6345.c
blob: f08e91ff3ba4525aeb281731aad4eab645b13a16 (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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
 *
 * Derived from linux/drivers/tty/serial/bcm63xx_uart.c:
 *	Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
 */

#include <clk.h>
#include <dm.h>
#include <debug_uart.h>
#include <errno.h>
#include <malloc.h>
#include <serial.h>
#include <asm/io.h>
#include <asm/types.h>

/* UART Control register */
#define UART_CTL_REG			0x0
#define UART_CTL_RXTIMEOUT_MASK		0x1f
#define UART_CTL_RXTIMEOUT_5		0x5
#define UART_CTL_RSTRXFIFO_SHIFT	6
#define UART_CTL_RSTRXFIFO_MASK		(1 << UART_CTL_RSTRXFIFO_SHIFT)
#define UART_CTL_RSTTXFIFO_SHIFT	7
#define UART_CTL_RSTTXFIFO_MASK		(1 << UART_CTL_RSTTXFIFO_SHIFT)
#define UART_CTL_STOPBITS_SHIFT		8
#define UART_CTL_STOPBITS_MASK		(0xf << UART_CTL_STOPBITS_SHIFT)
#define UART_CTL_STOPBITS_1		(0x7 << UART_CTL_STOPBITS_SHIFT)
#define UART_CTL_BITSPERSYM_SHIFT	12
#define UART_CTL_BITSPERSYM_MASK	(0x3 << UART_CTL_BITSPERSYM_SHIFT)
#define UART_CTL_BITSPERSYM_8		(0x3 << UART_CTL_BITSPERSYM_SHIFT)
#define UART_CTL_XMITBRK_SHIFT		14
#define UART_CTL_XMITBRK_MASK		(1 << UART_CTL_XMITBRK_SHIFT)
#define UART_CTL_RSVD_SHIFT		15
#define UART_CTL_RSVD_MASK		(1 << UART_CTL_RSVD_SHIFT)
#define UART_CTL_RXPAREVEN_SHIFT	16
#define UART_CTL_RXPAREVEN_MASK		(1 << UART_CTL_RXPAREVEN_SHIFT)
#define UART_CTL_RXPAREN_SHIFT		17
#define UART_CTL_RXPAREN_MASK		(1 << UART_CTL_RXPAREN_SHIFT)
#define UART_CTL_TXPAREVEN_SHIFT	18
#define UART_CTL_TXPAREVEN_MASK		(1 << UART_CTL_TXPAREVEN_SHIFT)
#define UART_CTL_TXPAREN_SHIFT		19
#define UART_CTL_TXPAREN_MASK		(1 << UART_CTL_TXPAREN_SHIFT)
#define UART_CTL_LOOPBACK_SHIFT		20
#define UART_CTL_LOOPBACK_MASK		(1 << UART_CTL_LOOPBACK_SHIFT)
#define UART_CTL_RXEN_SHIFT		21
#define UART_CTL_RXEN_MASK		(1 << UART_CTL_RXEN_SHIFT)
#define UART_CTL_TXEN_SHIFT		22
#define UART_CTL_TXEN_MASK		(1 << UART_CTL_TXEN_SHIFT)
#define UART_CTL_BRGEN_SHIFT		23
#define UART_CTL_BRGEN_MASK		(1 << UART_CTL_BRGEN_SHIFT)

/* UART Baudword register */
#define UART_BAUD_REG			0x4

/* UART FIFO Config register */
#define UART_FIFO_CFG_REG		0x8
#define UART_FIFO_CFG_RX_SHIFT		8
#define UART_FIFO_CFG_RX_MASK		(0xf << UART_FIFO_CFG_RX_SHIFT)
#define UART_FIFO_CFG_RX_4		(0x4 << UART_FIFO_CFG_RX_SHIFT)
#define UART_FIFO_CFG_TX_SHIFT		12
#define UART_FIFO_CFG_TX_MASK		(0xf << UART_FIFO_CFG_TX_SHIFT)
#define UART_FIFO_CFG_TX_4		(0x4 << UART_FIFO_CFG_TX_SHIFT)

/* UART Interrupt register */
#define UART_IR_REG			0x10
#define UART_IR_STAT(x)			(1 << (x))
#define UART_IR_TXEMPTY			5
#define UART_IR_RXOVER			7
#define UART_IR_RXNOTEMPTY		11

/* UART FIFO register */
#define UART_FIFO_REG			0x14
#define UART_FIFO_VALID_MASK		0xff
#define UART_FIFO_FRAMEERR_SHIFT	8
#define UART_FIFO_FRAMEERR_MASK		(1 << UART_FIFO_FRAMEERR_SHIFT)
#define UART_FIFO_PARERR_SHIFT		9
#define UART_FIFO_PARERR_MASK		(1 << UART_FIFO_PARERR_SHIFT)
#define UART_FIFO_BRKDET_SHIFT		10
#define UART_FIFO_BRKDET_MASK		(1 << UART_FIFO_BRKDET_SHIFT)
#define UART_FIFO_ANYERR_MASK		(UART_FIFO_FRAMEERR_MASK |	\
					UART_FIFO_PARERR_MASK |		\
					UART_FIFO_BRKDET_MASK)

struct bcm6345_serial_priv {
	void __iomem *base;
	ulong uartclk;
};

/* enable rx & tx operation on uart */
static void bcm6345_serial_enable(void __iomem *base)
{
	setbits_32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK |
		   UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
}

/* disable rx & tx operation on uart */
static void bcm6345_serial_disable(void __iomem *base)
{
	clrbits_32(base + UART_CTL_REG, UART_CTL_BRGEN_MASK |
		   UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
}

/* clear all unread data in rx fifo and unsent data in tx fifo */
static void bcm6345_serial_flush(void __iomem *base)
{
	/* empty rx and tx fifo */
	setbits_32(base + UART_CTL_REG, UART_CTL_RSTRXFIFO_MASK |
		   UART_CTL_RSTTXFIFO_MASK);

	/* read any pending char to make sure all irq status are cleared */
	readl(base + UART_FIFO_REG);
}

static int bcm6345_serial_init(void __iomem *base, ulong clk, u32 baudrate)
{
	u32 val;

	/* mask all irq and flush port */
	bcm6345_serial_disable(base);
	bcm6345_serial_flush(base);

	/* set uart control config */
	clrsetbits_32(base + UART_CTL_REG,
		      /* clear rx timeout */
		      UART_CTL_RXTIMEOUT_MASK |
		      /* clear stop bits */
		      UART_CTL_STOPBITS_MASK |
		      /* clear bits per symbol */
		      UART_CTL_BITSPERSYM_MASK |
		      /* clear xmit break */
		      UART_CTL_XMITBRK_MASK |
		      /* clear reserved bit */
		      UART_CTL_RSVD_MASK |
		      /* disable parity */
		      UART_CTL_RXPAREN_MASK |
		      UART_CTL_TXPAREN_MASK |
		      /* disable loopback */
		      UART_CTL_LOOPBACK_MASK,
		      /* set timeout to 5 */
		      UART_CTL_RXTIMEOUT_5 |
		      /* set 8 bits/symbol */
		      UART_CTL_BITSPERSYM_8 |
		      /* set 1 stop bit */
		      UART_CTL_STOPBITS_1 |
		      /* set parity to even */
		      UART_CTL_RXPAREVEN_MASK |
		      UART_CTL_TXPAREVEN_MASK);

	/* set uart fifo config */
	clrsetbits_32(base + UART_FIFO_CFG_REG,
		      /* clear fifo config */
		      UART_FIFO_CFG_RX_MASK |
		      UART_FIFO_CFG_TX_MASK,
		      /* set fifo config to 4 */
		      UART_FIFO_CFG_RX_4 |
		      UART_FIFO_CFG_TX_4);

	/* set baud rate */
	val = ((clk / baudrate) >> 4);
	if (val & 0x1)
		val = (val >> 1);
	else
		val = (val >> 1) - 1;
	writel(val, base + UART_BAUD_REG);

	/* clear interrupts */
	writel(0, base + UART_IR_REG);

	/* enable uart */
	bcm6345_serial_enable(base);

	return 0;
}

static int bcm6345_serial_pending(struct udevice *dev, bool input)
{
	struct bcm6345_serial_priv *priv = dev_get_priv(dev);
	u32 val = readl(priv->base + UART_IR_REG);

	if (input)
		return !!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY));
	else
		return !(val & UART_IR_STAT(UART_IR_TXEMPTY));
}

static int bcm6345_serial_setbrg(struct udevice *dev, int baudrate)
{
	struct bcm6345_serial_priv *priv = dev_get_priv(dev);

	return bcm6345_serial_init(priv->base, priv->uartclk, baudrate);
}

static int bcm6345_serial_putc(struct udevice *dev, const char ch)
{
	struct bcm6345_serial_priv *priv = dev_get_priv(dev);
	u32 val;

	val = readl(priv->base + UART_IR_REG);
	if (!(val & UART_IR_STAT(UART_IR_TXEMPTY)))
		return -EAGAIN;

	writel(ch, priv->base + UART_FIFO_REG);

	return 0;
}

static int bcm6345_serial_getc(struct udevice *dev)
{
	struct bcm6345_serial_priv *priv = dev_get_priv(dev);
	u32 val;

	val = readl(priv->base + UART_IR_REG);
	if (val & UART_IR_STAT(UART_IR_RXOVER))
		setbits_32(priv->base + UART_CTL_REG, UART_CTL_RSTRXFIFO_MASK);
	if (!(val & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
		return -EAGAIN;

	val = readl(priv->base + UART_FIFO_REG);
	if (val & UART_FIFO_ANYERR_MASK)
		return -EAGAIN;

	return val & UART_FIFO_VALID_MASK;
}

static int bcm6345_serial_probe(struct udevice *dev)
{
	struct bcm6345_serial_priv *priv = dev_get_priv(dev);
	struct clk clk;
	int ret;

	/* get address */
	priv->base = dev_remap_addr(dev);
	if (!priv->base)
		return -EINVAL;

	/* get clock rate */
	ret = clk_get_by_index(dev, 0, &clk);
	if (ret < 0)
		return ret;
	priv->uartclk = clk_get_rate(&clk);
	clk_free(&clk);

	/* initialize serial */
	return bcm6345_serial_init(priv->base, priv->uartclk, CONFIG_BAUDRATE);
}

static const struct dm_serial_ops bcm6345_serial_ops = {
	.putc = bcm6345_serial_putc,
	.pending = bcm6345_serial_pending,
	.getc = bcm6345_serial_getc,
	.setbrg = bcm6345_serial_setbrg,
};

static const struct udevice_id bcm6345_serial_ids[] = {
	{ .compatible = "brcm,bcm6345-uart" },
	{ /* sentinel */ }
};

U_BOOT_DRIVER(bcm6345_serial) = {
	.name = "bcm6345-uart",
	.id = UCLASS_SERIAL,
	.of_match = bcm6345_serial_ids,
	.probe = bcm6345_serial_probe,
	.priv_auto	= sizeof(struct bcm6345_serial_priv),
	.ops = &bcm6345_serial_ops,
};

#ifdef CONFIG_DEBUG_UART_BCM6345
static inline void _debug_uart_init(void)
{
	void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;

	bcm6345_serial_init(base, CONFIG_DEBUG_UART_CLOCK, CONFIG_BAUDRATE);
}

static inline void wait_xfered(void __iomem *base)
{
	do {
		u32 val = readl(base + UART_IR_REG);
		if (val & UART_IR_STAT(UART_IR_TXEMPTY))
			break;
	} while (1);
}

static inline void _debug_uart_putc(int ch)
{
	void __iomem *base = (void __iomem *)CONFIG_DEBUG_UART_BASE;

	wait_xfered(base);
	writel(ch, base + UART_FIFO_REG);
	wait_xfered(base);
}

DEBUG_UART_FUNCS
#endif