serial: 8250: RT288x/Au1xxx code away from core

A non-trivial amount of RT288x/Au1xxx code is encapsulated into
ifdeffery in 8250_port / 8250_early and some if UPIO_AU blocks.
Create a separate file from them.

Also handle errors properly in the cases where RT288x/Au1xxx code is
not configured.

It seems that 0x1000 mapsize is likely overkill but I've kept it the
same as previously (the value was shrunk to that value in commit
b2b13cdfd0 ("SERIAL 8250: Fixes for Alchemy UARTs.")). Seemingly, the
driver only needs to access register at 0x28 for the divisor latch.

The Kconfig side is a bit tricky. As SERIAL_8250_RT288X is bool it can
only be =y. It is possible to have SERIAL_8250=m + SERIAL_8250_RT288X=y
which required altering when 8250/ is included or the rt288x would not
be built.

Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230511121029.13128-5-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Ilpo Järvinen 2023-05-11 15:10:27 +03:00 committed by Greg Kroah-Hartman
parent 30c61f53fd
commit b334214ea0
8 changed files with 161 additions and 105 deletions

View File

@ -51,9 +51,9 @@ static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
#define PORT(_base, _irq) \
{ \
.mapbase = _base, \
.mapsize = 0x1000, \
.irq = _irq, \
.regshift = 2, \
.iotype = UPIO_AU, \
.flags = UPF_SKIP_TEST | UPF_IOREMAP | \
UPF_FIXED_TYPE, \
.type = PORT_16550A, \
@ -124,8 +124,14 @@ static void __init alchemy_setup_uarts(int ctype)
au1xx0_uart_device.dev.platform_data = ports;
/* Fill up uartclk. */
for (s = 0; s < c; s++)
for (s = 0; s < c; s++) {
ports[s].uartclk = uartclk;
if (au_platform_setup(&ports[s]) < 0) {
kfree(ports);
printk(KERN_INFO "Alchemy: missing support for UARTs\n");
return;
}
}
if (platform_device_register(&au1xx0_uart_device))
printk(KERN_INFO "Alchemy: failed to register UARTs\n");
}

View File

@ -36,7 +36,6 @@
static unsigned int serial8250_early_in(struct uart_port *port, int offset)
{
int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@ -50,8 +49,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
return ioread32be(port->membase + offset);
case UPIO_PORT:
return inb(port->iobase + offset);
case UPIO_AU:
return port->serial_in(port, reg_offset);
default:
return 0;
}
@ -59,7 +56,6 @@ static unsigned int serial8250_early_in(struct uart_port *port, int offset)
static void serial8250_early_out(struct uart_port *port, int offset, int value)
{
int reg_offset = offset;
offset <<= port->regshift;
switch (port->iotype) {
@ -78,9 +74,6 @@ static void serial8250_early_out(struct uart_port *port, int offset, int value)
case UPIO_PORT:
outb(value, port->iobase + offset);
break;
case UPIO_AU:
port->serial_out(port, reg_offset, value);
break;
}
}
@ -199,17 +192,3 @@ OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
#endif
#ifdef CONFIG_SERIAL_8250_RT288X
static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
{
dev->port.serial_in = au_serial_in;
dev->port.serial_out = au_serial_out;
dev->port.iotype = UPIO_AU;
dev->con->write = early_serial8250_write;
return 0;
}
OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
#endif

View File

@ -171,7 +171,9 @@ static int of_platform_serial_setup(struct platform_device *ofdev,
switch (type) {
case PORT_RT2880:
port->iotype = UPIO_AU;
ret = rt288x_setup(port);
if (ret)
goto err_unprepare;
break;
}

View File

@ -341,66 +341,6 @@ static void default_serial_dl_write(struct uart_8250_port *up, u32 value)
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
#ifdef CONFIG_SERIAL_8250_RT288X
#define UART_REG_UNMAPPED -1
/* Au1x00/RT288x UART hardware has a weird register layout */
static const s8 au_io_in_map[8] = {
[UART_RX] = 0,
[UART_IER] = 2,
[UART_IIR] = 3,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = 7,
[UART_MSR] = 8,
[UART_SCR] = UART_REG_UNMAPPED,
};
static const s8 au_io_out_map[8] = {
[UART_TX] = 1,
[UART_IER] = 2,
[UART_FCR] = 4,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = UART_REG_UNMAPPED,
[UART_MSR] = UART_REG_UNMAPPED,
[UART_SCR] = UART_REG_UNMAPPED,
};
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];
if (offset == UART_REG_UNMAPPED)
return UINT_MAX;
return __raw_readl(p->membase + (offset << p->regshift));
}
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];
if (offset == UART_REG_UNMAPPED)
return;
__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 + 0x28);
}
static void au_serial_dl_write(struct uart_8250_port *up, u32 value)
{
__raw_writel(value, up->port.membase + 0x28);
}
#endif
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
offset = offset << p->regshift;
@ -510,15 +450,6 @@ static void set_io_from_upio(struct uart_port *p)
p->serial_out = mem32be_serial_out;
break;
#ifdef CONFIG_SERIAL_8250_RT288X
case 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;
break;
#endif
default:
p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
@ -2968,11 +2899,6 @@ static unsigned int serial8250_port_size(struct uart_8250_port *pt)
{
if (pt->port.mapsize)
return pt->port.mapsize;
if (pt->port.iotype == UPIO_AU) {
if (pt->port.type == PORT_RT2880)
return 0x100;
return 0x1000;
}
if (is_omap1_8250(pt))
return 0x16 << pt->port.regshift;
@ -3222,10 +3148,6 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (flags & UART_CONFIG_TYPE)
autoconfig(up);
/* if access method is AU, it is a 16550 with a quirk */
if (port->type == PORT_16550A && port->iotype == UPIO_AU)
up->bugs |= UART_BUG_NOMSR;
/* HW bugs may trigger IRQ while IIR == NO_INT */
if (port->type == PORT_TEGRA)
up->bugs |= UART_BUG_NOMSR;

View File

@ -0,0 +1,142 @@
// 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 UART_REG_UNMAPPED -1
/* Au1x00/RT288x UART hardware has a weird register layout */
static const s8 au_io_in_map[8] = {
[UART_RX] = 0,
[UART_IER] = 2,
[UART_IIR] = 3,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = 7,
[UART_MSR] = 8,
[UART_SCR] = UART_REG_UNMAPPED,
};
static const s8 au_io_out_map[8] = {
[UART_TX] = 1,
[UART_IER] = 2,
[UART_FCR] = 4,
[UART_LCR] = 5,
[UART_MCR] = 6,
[UART_LSR] = UART_REG_UNMAPPED,
[UART_MSR] = UART_REG_UNMAPPED,
[UART_SCR] = UART_REG_UNMAPPED,
};
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];
if (offset == UART_REG_UNMAPPED)
return UINT_MAX;
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];
if (offset == UART_REG_UNMAPPED)
return;
__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 + 0x28);
}
static void au_serial_dl_write(struct uart_8250_port *up, u32 value)
{
__raw_writel(value, up->port.membase + 0x28);
}
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");

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o
obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o
obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o
obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o
obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o
obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o

View File

@ -21,7 +21,7 @@ obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
# Now bring in any enabled 8250/16450/16550 type drivers.
obj-$(CONFIG_SERIAL_8250) += 8250/
obj-y += 8250/
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o

View File

@ -7,6 +7,7 @@
#ifndef _LINUX_SERIAL_8250_H
#define _LINUX_SERIAL_8250_H
#include <linux/errno.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/platform_device.h>
@ -211,8 +212,11 @@ void serial8250_set_isa_configurator(void (*v)(int port, struct uart_port *up,
u32 *capabilities));
#ifdef CONFIG_SERIAL_8250_RT288X
unsigned int au_serial_in(struct uart_port *p, int offset);
void au_serial_out(struct uart_port *p, int offset, int value);
int rt288x_setup(struct uart_port *p);
int au_platform_setup(struct plat_serial8250_port *p);
#else
static inline int rt288x_setup(struct uart_port *p) { return -ENODEV; }
static inline int au_platform_setup(struct plat_serial8250_port *p) { return -ENODEV; }
#endif
#endif