summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/8250/8250_rt288x.c
blob: 6415ca8d3adff33fde085e526cda6bf1404b25a6 (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
// SPDX-License-Identifier: GPL-2.0
/*
 * RT288x/Au1xxx driver
 */

#include <linux/module.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>

#include "8250.h"

#define RT288X_DL	0x28

/* Au1x00/RT288x UART hardware has a weird register layout */
static const u8 au_io_in_map[7] = {
	[UART_RX]	= 0,
	[UART_IER]	= 2,
	[UART_IIR]	= 3,
	[UART_LCR]	= 5,
	[UART_MCR]	= 6,
	[UART_LSR]	= 7,
	[UART_MSR]	= 8,
};

static const u8 au_io_out_map[5] = {
	[UART_TX]	= 1,
	[UART_IER]	= 2,
	[UART_FCR]	= 4,
	[UART_LCR]	= 5,
	[UART_MCR]	= 6,
};

static unsigned int au_serial_in(struct uart_port *p, int offset)
{
	if (offset >= ARRAY_SIZE(au_io_in_map))
		return UINT_MAX;
	offset = au_io_in_map[offset];

	return __raw_readl(p->membase + (offset << p->regshift));
}

static void au_serial_out(struct uart_port *p, int offset, int value)
{
	if (offset >= ARRAY_SIZE(au_io_out_map))
		return;
	offset = au_io_out_map[offset];

	__raw_writel(value, p->membase + (offset << p->regshift));
}

/* Au1x00 haven't got a standard divisor latch */
static u32 au_serial_dl_read(struct uart_8250_port *up)
{
	return __raw_readl(up->port.membase + RT288X_DL);
}

static void au_serial_dl_write(struct uart_8250_port *up, u32 value)
{
	__raw_writel(value, up->port.membase + RT288X_DL);
}

int au_platform_setup(struct plat_serial8250_port *p)
{
	p->iotype = UPIO_AU;

	p->serial_in = au_serial_in;
	p->serial_out = au_serial_out;
	p->dl_read = au_serial_dl_read;
	p->dl_write = au_serial_dl_write;

	p->mapsize = 0x1000;

	p->bugs |= UART_BUG_NOMSR;

	return 0;
}
EXPORT_SYMBOL_GPL(au_platform_setup);

int rt288x_setup(struct uart_port *p)
{
	struct uart_8250_port *up = up_to_u8250p(p);

	p->iotype = UPIO_AU;

	p->serial_in = au_serial_in;
	p->serial_out = au_serial_out;
	up->dl_read = au_serial_dl_read;
	up->dl_write = au_serial_dl_write;

	p->mapsize = 0x100;

	up->bugs |= UART_BUG_NOMSR;

	return 0;
}
EXPORT_SYMBOL_GPL(rt288x_setup);

#ifdef CONFIG_SERIAL_8250_CONSOLE
static void au_putc(struct uart_port *port, unsigned char c)
{
	unsigned int status;

	au_serial_out(port, UART_TX, c);

	for (;;) {
		status = au_serial_in(port, UART_LSR);
		if (uart_lsr_tx_empty(status))
			break;
		cpu_relax();
	}
}

static void au_early_serial8250_write(struct console *console,
				      const char *s, unsigned int count)
{
	struct earlycon_device *device = console->data;
	struct uart_port *port = &device->port;

	uart_console_write(port, s, count, au_putc);
}

static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
{
	rt288x_setup(&dev->port);
	dev->con->write = au_early_serial8250_write;

	return 0;
}
OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
#endif

MODULE_DESCRIPTION("RT288x/Au1xxx UART driver");
MODULE_LICENSE("GPL");