Add a new UART I/O type for slow busses such as the ixp4xx expansion bus --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -27,6 +27,7 @@ struct plat_serial8250_port { void *private_data; unsigned char regshift; /* register shift */ unsigned char iotype; /* UPIO_* */ + unsigned int rw_delay; /* udelay for slower busses IXP4XX Expansion Bus */ unsigned char hub6; upf_t flags; /* UPF_* flags */ unsigned int type; /* If UPF_FIXED_TYPE */ --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -324,6 +324,7 @@ struct uart_port { #define UPIO_AU (4) /* Au1x00 type IO */ #define UPIO_TSI (5) /* Tsi108/109 type IO */ #define UPIO_RM9000 (6) /* RM9000 type IO */ +#define UPIO_MEM_DELAY (7) unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ @@ -368,6 +369,7 @@ struct uart_port { unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ + unsigned int rw_delay; /* udelay for slow busses, IXP4XX Expansion Bus */ unsigned int type; /* port type */ const struct uart_ops *ops; unsigned int custom_divisor; --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -400,6 +400,20 @@ static void mem_serial_out(struct uart_p writeb(value, p->membase + offset); } +static unsigned int memdelay_serial_in(struct uart_port *p, int offset) +{ + struct uart_8250_port *up = (struct uart_8250_port *)p; + udelay(up->port.rw_delay); + return mem_serial_in(p, offset); +} + +static void memdelay_serial_out(struct uart_port *p, int offset, int value) +{ + struct uart_8250_port *up = (struct uart_8250_port *)p; + udelay(up->port.rw_delay); + mem_serial_out(p, offset, value); +} + static void mem32_serial_out(struct uart_port *p, int offset, int value) { offset = map_8250_out_reg(p, offset) << p->regshift; @@ -459,6 +473,11 @@ static void set_io_from_upio(struct uart p->serial_out = mem32_serial_out; break; + case UPIO_MEM_DELAY: + p->serial_in = memdelay_serial_in; + p->serial_out = memdelay_serial_out; + break; + case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; @@ -481,6 +500,7 @@ serial_out_sync(struct uart_8250_port *u switch (p->iotype) { case UPIO_MEM: case UPIO_MEM32: + case UPIO_MEM_DELAY: case UPIO_AU: p->serial_out(p, offset, value); p->serial_in(p, UART_LCR); /* safe, no side-effects */ @@ -2498,6 +2518,7 @@ static int serial8250_request_std_resour case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM: + case UPIO_MEM_DELAY: if (!up->port.mapbase) break; @@ -2534,6 +2555,7 @@ static void serial8250_release_std_resou case UPIO_TSI: case UPIO_MEM32: case UPIO_MEM: + case UPIO_MEM_DELAY: if (!up->port.mapbase) break; @@ -3050,6 +3072,7 @@ static int __devinit serial8250_probe(st port.set_termios = p->set_termios; port.pm = p->pm; port.dev = &dev->dev; + port.rw_delay = p->rw_delay; port.irqflags |= irqflag; ret = serial8250_register_port(&port); if (ret < 0) { @@ -3199,6 +3222,7 @@ int serial8250_register_port(struct uart uart->port.iotype = port->iotype; uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; uart->port.mapbase = port->mapbase; + uart->port.rw_delay = port->rw_delay; uart->port.private_data = port->private_data; if (port->dev) uart->port.dev = port->dev; --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2021,6 +2021,7 @@ uart_report_port(struct uart_driver *drv snprintf(address, sizeof(address), "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); break; + case UPIO_MEM_DELAY: case UPIO_MEM: case UPIO_MEM32: case UPIO_AU: @@ -2436,6 +2437,7 @@ int uart_match_port(struct uart_port *po case UPIO_HUB6: return (port1->iobase == port2->iobase) && (port1->hub6 == port2->hub6); + case UPIO_MEM_DELAY: case UPIO_MEM: case UPIO_MEM32: case UPIO_AU: