Commit 7d6a07d1 authored by David Daney's avatar David Daney Committed by Linus Torvalds

8250: Serial driver changes to support future Cavium OCTEON serial patches.

In order to use Cavium OCTEON specific serial i/o drivers, we first
patch the 8250 driver to use replaceable I/O functions.  Compatible
I/O functions are added for existing iotypeS.

An added benefit of this change is that it makes it easy to factor
some of the existing special cases out to board/SOC specific support
code.

The alternative is to load up 8250.c with a bunch of OCTEON specific
iotype code and bug work-arounds.
Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Signed-off-by: default avatarTomaso Paoletti <tpaoletti@caviumnetworks.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b430428a
...@@ -303,16 +303,16 @@ static const u8 au_io_out_map[] = { ...@@ -303,16 +303,16 @@ static const u8 au_io_out_map[] = {
}; };
/* sane hardware needs no mapping */ /* sane hardware needs no mapping */
static inline int map_8250_in_reg(struct uart_8250_port *up, int offset) static inline int map_8250_in_reg(struct uart_port *p, int offset)
{ {
if (up->port.iotype != UPIO_AU) if (p->iotype != UPIO_AU)
return offset; return offset;
return au_io_in_map[offset]; return au_io_in_map[offset];
} }
static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) static inline int map_8250_out_reg(struct uart_port *p, int offset)
{ {
if (up->port.iotype != UPIO_AU) if (p->iotype != UPIO_AU)
return offset; return offset;
return au_io_out_map[offset]; return au_io_out_map[offset];
} }
...@@ -341,16 +341,16 @@ static const u8 ...@@ -341,16 +341,16 @@ static const u8
[UART_SCR] = 0x2c [UART_SCR] = 0x2c
}; };
static inline int map_8250_in_reg(struct uart_8250_port *up, int offset) static inline int map_8250_in_reg(struct uart_port *p, int offset)
{ {
if (up->port.iotype != UPIO_RM9000) if (p->iotype != UPIO_RM9000)
return offset; return offset;
return regmap_in[offset]; return regmap_in[offset];
} }
static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) static inline int map_8250_out_reg(struct uart_port *p, int offset)
{ {
if (up->port.iotype != UPIO_RM9000) if (p->iotype != UPIO_RM9000)
return offset; return offset;
return regmap_out[offset]; return regmap_out[offset];
} }
...@@ -363,108 +363,170 @@ static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) ...@@ -363,108 +363,170 @@ static inline int map_8250_out_reg(struct uart_8250_port *up, int offset)
#endif #endif
static unsigned int serial_in(struct uart_8250_port *up, int offset) static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{ {
unsigned int tmp; offset = map_8250_in_reg(p, offset) << p->regshift;
offset = map_8250_in_reg(up, offset) << up->port.regshift; outb(p->hub6 - 1 + offset, p->iobase);
return inb(p->iobase + 1);
}
switch (up->port.iotype) { static void hub6_serial_out(struct uart_port *p, int offset, int value)
case UPIO_HUB6: {
outb(up->port.hub6 - 1 + offset, up->port.iobase); offset = map_8250_out_reg(p, offset) << p->regshift;
return inb(up->port.iobase + 1); outb(p->hub6 - 1 + offset, p->iobase);
outb(value, p->iobase + 1);
}
case UPIO_MEM: static unsigned int mem_serial_in(struct uart_port *p, int offset)
case UPIO_DWAPB: {
return readb(up->port.membase + offset); offset = map_8250_in_reg(p, offset) << p->regshift;
return readb(p->membase + offset);
}
case UPIO_RM9000: static void mem_serial_out(struct uart_port *p, int offset, int value)
case UPIO_MEM32: {
return readl(up->port.membase + offset); offset = map_8250_out_reg(p, offset) << p->regshift;
writeb(value, p->membase + offset);
}
static void mem32_serial_out(struct uart_port *p, int offset, int value)
{
offset = map_8250_out_reg(p, offset) << p->regshift;
writel(value, p->membase + offset);
}
static unsigned int mem32_serial_in(struct uart_port *p, int offset)
{
offset = map_8250_in_reg(p, offset) << p->regshift;
return readl(p->membase + offset);
}
#ifdef CONFIG_SERIAL_8250_AU1X00 #ifdef CONFIG_SERIAL_8250_AU1X00
case UPIO_AU: static unsigned int au_serial_in(struct uart_port *p, int offset)
return __raw_readl(up->port.membase + offset); {
offset = map_8250_in_reg(p, offset) << p->regshift;
return __raw_readl(p->membase + offset);
}
static void au_serial_out(struct uart_port *p, int offset, int value)
{
offset = map_8250_out_reg(p, offset) << p->regshift;
__raw_writel(value, p->membase + offset);
}
#endif #endif
case UPIO_TSI: static unsigned int tsi_serial_in(struct uart_port *p, int offset)
{
unsigned int tmp;
offset = map_8250_in_reg(p, offset) << p->regshift;
if (offset == UART_IIR) { if (offset == UART_IIR) {
tmp = readl(up->port.membase + (UART_IIR & ~3)); tmp = readl(p->membase + (UART_IIR & ~3));
return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */
} else } else
return readb(up->port.membase + offset); return readb(p->membase + offset);
}
default: static void tsi_serial_out(struct uart_port *p, int offset, int value)
return inb(up->port.iobase + offset); {
} offset = map_8250_out_reg(p, offset) << p->regshift;
if (!((offset == UART_IER) && (value & UART_IER_UUE)))
writeb(value, p->membase + offset);
} }
static void static void dwapb_serial_out(struct uart_port *p, int offset, int value)
serial_out(struct uart_8250_port *up, int offset, int value)
{ {
/* Save the offset before it's remapped */
int save_offset = offset; int save_offset = offset;
offset = map_8250_out_reg(up, offset) << up->port.regshift; offset = map_8250_out_reg(p, offset) << p->regshift;
/* Save the LCR value so it can be re-written when a
* Busy Detect interrupt occurs. */
if (save_offset == UART_LCR) {
struct uart_8250_port *up = (struct uart_8250_port *)p;
up->lcr = value;
}
writeb(value, p->membase + offset);
/* Read the IER to ensure any interrupt is cleared before
* returning from ISR. */
if (save_offset == UART_TX || save_offset == UART_IER)
value = p->serial_in(p, UART_IER);
}
switch (up->port.iotype) { static unsigned int io_serial_in(struct uart_port *p, int offset)
{
offset = map_8250_in_reg(p, offset) << p->regshift;
return inb(p->iobase + offset);
}
static void io_serial_out(struct uart_port *p, int offset, int value)
{
offset = map_8250_out_reg(p, offset) << p->regshift;
outb(value, p->iobase + offset);
}
static void set_io_from_upio(struct uart_port *p)
{
switch (p->iotype) {
case UPIO_HUB6: case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase); p->serial_in = hub6_serial_in;
outb(value, up->port.iobase + 1); p->serial_out = hub6_serial_out;
break; break;
case UPIO_MEM: case UPIO_MEM:
writeb(value, up->port.membase + offset); p->serial_in = mem_serial_in;
p->serial_out = mem_serial_out;
break; break;
case UPIO_RM9000: case UPIO_RM9000:
case UPIO_MEM32: case UPIO_MEM32:
writel(value, up->port.membase + offset); p->serial_in = mem32_serial_in;
p->serial_out = mem32_serial_out;
break; break;
#ifdef CONFIG_SERIAL_8250_AU1X00 #ifdef CONFIG_SERIAL_8250_AU1X00
case UPIO_AU: case UPIO_AU:
__raw_writel(value, up->port.membase + offset); p->serial_in = au_serial_in;
p->serial_out = au_serial_out;
break; break;
#endif #endif
case UPIO_TSI: case UPIO_TSI:
if (!((offset == UART_IER) && (value & UART_IER_UUE))) p->serial_in = tsi_serial_in;
writeb(value, up->port.membase + offset); p->serial_out = tsi_serial_out;
break; break;
case UPIO_DWAPB: case UPIO_DWAPB:
/* Save the LCR value so it can be re-written when a p->serial_in = mem_serial_in;
* Busy Detect interrupt occurs. */ p->serial_out = dwapb_serial_out;
if (save_offset == UART_LCR)
up->lcr = value;
writeb(value, up->port.membase + offset);
/* Read the IER to ensure any interrupt is cleared before
* returning from ISR. */
if (save_offset == UART_TX || save_offset == UART_IER)
value = serial_in(up, UART_IER);
break; break;
default: default:
outb(value, up->port.iobase + offset); p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
break;
} }
} }
static void static void
serial_out_sync(struct uart_8250_port *up, int offset, int value) serial_out_sync(struct uart_8250_port *up, int offset, int value)
{ {
switch (up->port.iotype) { struct uart_port *p = &up->port;
switch (p->iotype) {
case UPIO_MEM: case UPIO_MEM:
case UPIO_MEM32: case UPIO_MEM32:
#ifdef CONFIG_SERIAL_8250_AU1X00 #ifdef CONFIG_SERIAL_8250_AU1X00
case UPIO_AU: case UPIO_AU:
#endif #endif
case UPIO_DWAPB: case UPIO_DWAPB:
serial_out(up, offset, value); p->serial_out(p, offset, value);
serial_in(up, UART_LCR); /* safe, no side-effects */ p->serial_in(p, UART_LCR); /* safe, no side-effects */
break; break;
default: default:
serial_out(up, offset, value); p->serial_out(p, offset, value);
} }
} }
#define serial_in(up, offset) \
(up->port.serial_in(&(up)->port, (offset)))
#define serial_out(up, offset, value) \
(up->port.serial_out(&(up)->port, (offset), (value)))
/* /*
* We used to support using pause I/O for certain machines. We * We used to support using pause I/O for certain machines. We
* haven't supported this for a while, but just in case it's badly * haven't supported this for a while, but just in case it's badly
...@@ -2576,6 +2638,7 @@ static void __init serial8250_isa_init_ports(void) ...@@ -2576,6 +2638,7 @@ static void __init serial8250_isa_init_ports(void)
up->port.membase = old_serial_port[i].iomem_base; up->port.membase = old_serial_port[i].iomem_base;
up->port.iotype = old_serial_port[i].io_type; up->port.iotype = old_serial_port[i].io_type;
up->port.regshift = old_serial_port[i].iomem_reg_shift; up->port.regshift = old_serial_port[i].iomem_reg_shift;
set_io_from_upio(&up->port);
if (share_irqs) if (share_irqs)
up->port.flags |= UPF_SHARE_IRQ; up->port.flags |= UPF_SHARE_IRQ;
} }
...@@ -2769,6 +2832,13 @@ int __init early_serial_setup(struct uart_port *port) ...@@ -2769,6 +2832,13 @@ int __init early_serial_setup(struct uart_port *port)
p->flags = port->flags; p->flags = port->flags;
p->mapbase = port->mapbase; p->mapbase = port->mapbase;
p->private_data = port->private_data; p->private_data = port->private_data;
set_io_from_upio(p);
if (port->serial_in)
p->serial_in = port->serial_in;
if (port->serial_out)
p->serial_out = port->serial_out;
return 0; return 0;
} }
...@@ -2833,6 +2903,8 @@ static int __devinit serial8250_probe(struct platform_device *dev) ...@@ -2833,6 +2903,8 @@ static int __devinit serial8250_probe(struct platform_device *dev)
port.mapbase = p->mapbase; port.mapbase = p->mapbase;
port.hub6 = p->hub6; port.hub6 = p->hub6;
port.private_data = p->private_data; port.private_data = p->private_data;
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
port.dev = &dev->dev; port.dev = &dev->dev;
if (share_irqs) if (share_irqs)
port.flags |= UPF_SHARE_IRQ; port.flags |= UPF_SHARE_IRQ;
...@@ -2986,6 +3058,12 @@ int serial8250_register_port(struct uart_port *port) ...@@ -2986,6 +3058,12 @@ int serial8250_register_port(struct uart_port *port)
uart->port.private_data = port->private_data; uart->port.private_data = port->private_data;
if (port->dev) if (port->dev)
uart->port.dev = port->dev; uart->port.dev = port->dev;
set_io_from_upio(&uart->port);
/* Possibly override default I/O functions. */
if (port->serial_in)
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
ret = uart_add_one_port(&serial8250_reg, &uart->port); ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0) if (ret == 0)
......
...@@ -28,6 +28,8 @@ struct plat_serial8250_port { ...@@ -28,6 +28,8 @@ struct plat_serial8250_port {
unsigned char iotype; /* UPIO_* */ unsigned char iotype; /* UPIO_* */
unsigned char hub6; unsigned char hub6;
upf_t flags; /* UPF_* flags */ upf_t flags; /* UPF_* flags */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
}; };
/* /*
......
...@@ -248,6 +248,8 @@ struct uart_port { ...@@ -248,6 +248,8 @@ struct uart_port {
spinlock_t lock; /* port lock */ spinlock_t lock; /* port lock */
unsigned long iobase; /* in/out[bwl] */ unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
unsigned int irq; /* irq number */ unsigned int irq; /* irq number */
unsigned int uartclk; /* base uart clock */ unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size */ unsigned int fifosize; /* tx fifo size */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment