162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * RT288x/Au1xxx driver
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/console.h>
1062306a36Sopenharmony_ci#include <linux/serial.h>
1162306a36Sopenharmony_ci#include <linux/serial_8250.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "8250.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define RT288X_DL	0x28
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Au1x00/RT288x UART hardware has a weird register layout */
1862306a36Sopenharmony_cistatic const u8 au_io_in_map[7] = {
1962306a36Sopenharmony_ci	[UART_RX]	= 0,
2062306a36Sopenharmony_ci	[UART_IER]	= 2,
2162306a36Sopenharmony_ci	[UART_IIR]	= 3,
2262306a36Sopenharmony_ci	[UART_LCR]	= 5,
2362306a36Sopenharmony_ci	[UART_MCR]	= 6,
2462306a36Sopenharmony_ci	[UART_LSR]	= 7,
2562306a36Sopenharmony_ci	[UART_MSR]	= 8,
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const u8 au_io_out_map[5] = {
2962306a36Sopenharmony_ci	[UART_TX]	= 1,
3062306a36Sopenharmony_ci	[UART_IER]	= 2,
3162306a36Sopenharmony_ci	[UART_FCR]	= 4,
3262306a36Sopenharmony_ci	[UART_LCR]	= 5,
3362306a36Sopenharmony_ci	[UART_MCR]	= 6,
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic unsigned int au_serial_in(struct uart_port *p, int offset)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	if (offset >= ARRAY_SIZE(au_io_in_map))
3962306a36Sopenharmony_ci		return UINT_MAX;
4062306a36Sopenharmony_ci	offset = au_io_in_map[offset];
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return __raw_readl(p->membase + (offset << p->regshift));
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic void au_serial_out(struct uart_port *p, int offset, int value)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	if (offset >= ARRAY_SIZE(au_io_out_map))
4862306a36Sopenharmony_ci		return;
4962306a36Sopenharmony_ci	offset = au_io_out_map[offset];
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	__raw_writel(value, p->membase + (offset << p->regshift));
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Au1x00 haven't got a standard divisor latch */
5562306a36Sopenharmony_cistatic u32 au_serial_dl_read(struct uart_8250_port *up)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	return __raw_readl(up->port.membase + RT288X_DL);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void au_serial_dl_write(struct uart_8250_port *up, u32 value)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	__raw_writel(value, up->port.membase + RT288X_DL);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciint au_platform_setup(struct plat_serial8250_port *p)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	p->iotype = UPIO_AU;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	p->serial_in = au_serial_in;
7062306a36Sopenharmony_ci	p->serial_out = au_serial_out;
7162306a36Sopenharmony_ci	p->dl_read = au_serial_dl_read;
7262306a36Sopenharmony_ci	p->dl_write = au_serial_dl_write;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	p->mapsize = 0x1000;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	p->bugs |= UART_BUG_NOMSR;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(au_platform_setup);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ciint rt288x_setup(struct uart_port *p)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct uart_8250_port *up = up_to_u8250p(p);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	p->iotype = UPIO_AU;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	p->serial_in = au_serial_in;
8962306a36Sopenharmony_ci	p->serial_out = au_serial_out;
9062306a36Sopenharmony_ci	up->dl_read = au_serial_dl_read;
9162306a36Sopenharmony_ci	up->dl_write = au_serial_dl_write;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	p->mapsize = 0x100;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	up->bugs |= UART_BUG_NOMSR;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt288x_setup);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#ifdef CONFIG_SERIAL_8250_CONSOLE
10262306a36Sopenharmony_cistatic void au_putc(struct uart_port *port, unsigned char c)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	unsigned int status;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	au_serial_out(port, UART_TX, c);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	for (;;) {
10962306a36Sopenharmony_ci		status = au_serial_in(port, UART_LSR);
11062306a36Sopenharmony_ci		if (uart_lsr_tx_empty(status))
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		cpu_relax();
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void au_early_serial8250_write(struct console *console,
11762306a36Sopenharmony_ci				      const char *s, unsigned int count)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct earlycon_device *device = console->data;
12062306a36Sopenharmony_ci	struct uart_port *port = &device->port;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	uart_console_write(port, s, count, au_putc);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int __init early_au_setup(struct earlycon_device *dev, const char *opt)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	rt288x_setup(&dev->port);
12862306a36Sopenharmony_ci	dev->con->write = au_early_serial8250_write;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ciOF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
13362306a36Sopenharmony_ci#endif
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ciMODULE_DESCRIPTION("RT288x/Au1xxx UART driver");
13662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
137