serial8250: DLL/DLM rework, Emma Mobile UART driver
[PATCH 01/06] serial8250: Add dl_read()/dl_write() callbacks
[PATCH 02/06] serial8250: Use dl_read()/dl_write() on Alchemy
[PATCH 03/06] serial8250: Use dl_read()/dl_write() on RM9K
[PATCH 04/06] serial8250: Clean up default map and dl code
[PATCH 05/06] serial8250: Introduce serial8250_register_8250_port()
[PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
This series cleans up and adjusts the 8250 code base to allow
hooking in a driver for the UART in Emma Mobile SoCs.
In short:
- Patch 1-4 tie in 8250-specific dl_read()/dl_write() callbacks.
- Patch 5 adds a new register function to use new callbacks.
- Patch 6 adds a new driver that makes use of the above.
I'd be happy to rework the above patches if needed. The
main challenge for Emma Mobile UART support is the non-
standard offsets for DLL and DLM registers.
Note that there is no DT support included at this point,
but it boils down to a 10 line change. The boot loader on
my board does not do DT so I'd like to use kexec for DT
development (as usual), but to use kexec I first need to
get a non-DT kernel working. Which is basically this. =)
Signed-off-by: Magnus Damm <[email protected]>
---
Builds on top of linux-next 20120501.
drivers/tty/serial/8250/8250.c | 373 ++++++++++++++++++-------------------
drivers/tty/serial/8250/8250.h | 14 +
drivers/tty/serial/8250/8250_em.c | 179 +++++++++++++++++
drivers/tty/serial/8250/Kconfig | 8
drivers/tty/serial/8250/Makefile | 1
include/linux/serial_8250.h | 1
6 files changed, 392 insertions(+), 184 deletions(-)
From: Magnus Damm <[email protected]>
Convert serial_dl_read() and serial_dl_write() from macro
to 8250 specific callbacks. This change makes it easier to
support 8250 hardware with non-standard DLL and DLM register
configurations such as Alchemy, RM9K and upcoming Emma Mobile
UART hardware.
Signed-off-by: Magnus Damm <[email protected]>
---
drivers/tty/serial/8250/8250.c | 117 +++++++++++++++++++++-------------------
drivers/tty/serial/8250/8250.h | 14 ++++
2 files changed, 78 insertions(+), 53 deletions(-)
--- 0001/drivers/tty/serial/8250/8250.c
+++ work/drivers/tty/serial/8250/8250.c 2012-05-01 22:50:38.000000000 +0900
@@ -284,6 +284,66 @@ static const struct serial8250_config ua
},
};
+/* Uart divisor latch read */
+static int default_dl_read(struct uart_8250_port *up)
+{
+ return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static void default_dl_write(struct uart_8250_port *up, int value)
+{
+ serial_out(up, UART_DLL, value & 0xff);
+ serial_out(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#if defined(CONFIG_MIPS_ALCHEMY)
+/* Au1x00 haven't got a standard divisor latch */
+static int _serial_dl_read(struct uart_8250_port *up)
+{
+ if (up->port.iotype == UPIO_AU)
+ return __raw_readl(up->port.membase + 0x28);
+ else
+ return default_dl_read(up);
+}
+
+static void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+ if (up->port.iotype == UPIO_AU)
+ __raw_writel(value, up->port.membase + 0x28);
+ else
+ default_dl_write(up, value);
+}
+#elif defined(CONFIG_SERIAL_8250_RM9K)
+static int _serial_dl_read(struct uart_8250_port *up)
+{
+ return (up->port.iotype == UPIO_RM9000) ?
+ (((__raw_readl(up->port.membase + 0x10) << 8) |
+ (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) :
+ default_dl_read(up);
+}
+
+static void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+ if (up->port.iotype == UPIO_RM9000) {
+ __raw_writel(value, up->port.membase + 0x08);
+ __raw_writel(value >> 8, up->port.membase + 0x10);
+ } else {
+ default_dl_write(up, value);
+ }
+}
+#else
+static int _serial_dl_read(struct uart_8250_port *up)
+{
+ return default_dl_read(up);
+}
+
+static void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+ default_dl_write(up, value);
+}
+#endif
+
#if defined(CONFIG_MIPS_ALCHEMY)
/* Au1x00 UART hardware has a weird register layout */
@@ -434,6 +494,10 @@ static void set_io_from_upio(struct uart
{
struct uart_8250_port *up =
container_of(p, struct uart_8250_port, port);
+
+ up->dl_read = _serial_dl_read;
+ up->dl_write = _serial_dl_write;
+
switch (p->iotype) {
case UPIO_HUB6:
p->serial_in = hub6_serial_in;
@@ -481,59 +545,6 @@ serial_port_out_sync(struct uart_port *p
}
}
-/* Uart divisor latch read */
-static inline int _serial_dl_read(struct uart_8250_port *up)
-{
- return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
-}
-
-/* Uart divisor latch write */
-static inline void _serial_dl_write(struct uart_8250_port *up, int value)
-{
- serial_out(up, UART_DLL, value & 0xff);
- serial_out(up, UART_DLM, value >> 8 & 0xff);
-}
-
-#if defined(CONFIG_MIPS_ALCHEMY)
-/* Au1x00 haven't got a standard divisor latch */
-static int serial_dl_read(struct uart_8250_port *up)
-{
- if (up->port.iotype == UPIO_AU)
- return __raw_readl(up->port.membase + 0x28);
- else
- return _serial_dl_read(up);
-}
-
-static void serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_AU)
- __raw_writel(value, up->port.membase + 0x28);
- else
- _serial_dl_write(up, value);
-}
-#elif defined(CONFIG_SERIAL_8250_RM9K)
-static int serial_dl_read(struct uart_8250_port *up)
-{
- return (up->port.iotype == UPIO_RM9000) ?
- (((__raw_readl(up->port.membase + 0x10) << 8) |
- (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) :
- _serial_dl_read(up);
-}
-
-static void serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_RM9000) {
- __raw_writel(value, up->port.membase + 0x08);
- __raw_writel(value >> 8, up->port.membase + 0x10);
- } else {
- _serial_dl_write(up, value);
- }
-}
-#else
-#define serial_dl_read(up) _serial_dl_read(up)
-#define serial_dl_write(up, value) _serial_dl_write(up, value)
-#endif
-
/*
* For the 16C950
*/
--- 0001/drivers/tty/serial/8250/8250.h
+++ work/drivers/tty/serial/8250/8250.h 2012-05-01 22:50:38.000000000 +0900
@@ -37,6 +37,10 @@ struct uart_8250_port {
unsigned char lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
unsigned char msr_saved_flags;
+
+ /* 8250 specific callbacks */
+ int (*dl_read)(struct uart_8250_port *);
+ void (*dl_write)(struct uart_8250_port *, int);
};
struct old_serial_port {
@@ -96,6 +100,16 @@ static inline void serial_out(struct uar
up->port.serial_out(&up->port, offset, value);
}
+static inline int serial_dl_read(struct uart_8250_port *up)
+{
+ return up->dl_read(up);
+}
+
+static inline void serial_dl_write(struct uart_8250_port *up, int value)
+{
+ up->dl_write(up, value);
+}
+
#if defined(__alpha__) && !defined(CONFIG_PCI)
/*
* Digital did something really horribly wrong with the OUT1 and OUT2
From: Magnus Damm <[email protected]>
Convert the 8250 Alchemy support code to make
use of the new dl_read()/dl_write() callbacks.
Signed-off-by: Magnus Damm <[email protected]>
---
drivers/tty/serial/8250/8250.c | 67 +++++++++++++++-------------------------
1 file changed, 26 insertions(+), 41 deletions(-)
--- 0005/drivers/tty/serial/8250/8250.c
+++ work/drivers/tty/serial/8250/8250.c 2012-05-01 22:51:07.000000000 +0900
@@ -297,24 +297,7 @@ static void default_dl_write(struct uart
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
-#if defined(CONFIG_MIPS_ALCHEMY)
-/* Au1x00 haven't got a standard divisor latch */
-static int _serial_dl_read(struct uart_8250_port *up)
-{
- if (up->port.iotype == UPIO_AU)
- return __raw_readl(up->port.membase + 0x28);
- else
- return default_dl_read(up);
-}
-
-static void _serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_AU)
- __raw_writel(value, up->port.membase + 0x28);
- else
- default_dl_write(up, value);
-}
-#elif defined(CONFIG_SERIAL_8250_RM9K)
+#if defined(CONFIG_SERIAL_8250_RM9K)
static int _serial_dl_read(struct uart_8250_port *up)
{
return (up->port.iotype == UPIO_RM9000) ?
@@ -344,7 +327,7 @@ static void _serial_dl_write(struct uart
}
#endif
-#if defined(CONFIG_MIPS_ALCHEMY)
+#ifdef CONFIG_MIPS_ALCHEMY
/* Au1x00 UART hardware has a weird register layout */
static const u8 au_io_in_map[] = {
@@ -365,22 +348,32 @@ static const u8 au_io_out_map[] = {
[UART_MCR] = 6,
};
-/* sane hardware needs no mapping */
-static inline int map_8250_in_reg(struct uart_port *p, int offset)
+static unsigned int au_serial_in(struct uart_port *p, int offset)
{
- if (p->iotype != UPIO_AU)
- return offset;
- return au_io_in_map[offset];
+ offset = au_io_in_map[offset] << p->regshift;
+ return __raw_readl(p->membase + offset);
}
-static inline int map_8250_out_reg(struct uart_port *p, int offset)
+static void au_serial_out(struct uart_port *p, int offset, int value)
{
- if (p->iotype != UPIO_AU)
- return offset;
- return au_io_out_map[offset];
+ offset = au_io_out_map[offset] << p->regshift;
+ __raw_writel(value, p->membase + offset);
+}
+
+/* Au1x00 haven't got a standard divisor latch */
+static int 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, int value)
+{
+ __raw_writel(value, up->port.membase + 0x28);
}
-#elif defined(CONFIG_SERIAL_8250_RM9K)
+#endif
+
+#if defined(CONFIG_SERIAL_8250_RM9K)
static const u8
regmap_in[8] = {
@@ -464,18 +457,6 @@ static unsigned int mem32_serial_in(stru
return readl(p->membase + offset);
}
-static unsigned int au_serial_in(struct uart_port *p, int 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);
-}
-
static unsigned int io_serial_in(struct uart_port *p, int offset)
{
offset = map_8250_in_reg(p, offset) << p->regshift;
@@ -515,10 +496,14 @@ static void set_io_from_upio(struct uart
p->serial_out = mem32_serial_out;
break;
+#ifdef CONFIG_MIPS_ALCHEMY
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;
From: Magnus Damm <[email protected]>
Convert the 8250 RM9K support code to make
use of the new dl_read()/dl_write() callbacks.
Signed-off-by: Magnus Damm <[email protected]>
---
drivers/tty/serial/8250/8250.c | 62 ++++++++++++++++++----------------------
1 file changed, 29 insertions(+), 33 deletions(-)
--- 0006/drivers/tty/serial/8250/8250.c
+++ work/drivers/tty/serial/8250/8250.c 2012-05-01 22:51:42.000000000 +0900
@@ -297,25 +297,6 @@ static void default_dl_write(struct uart
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
-#if defined(CONFIG_SERIAL_8250_RM9K)
-static int _serial_dl_read(struct uart_8250_port *up)
-{
- return (up->port.iotype == UPIO_RM9000) ?
- (((__raw_readl(up->port.membase + 0x10) << 8) |
- (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) :
- default_dl_read(up);
-}
-
-static void _serial_dl_write(struct uart_8250_port *up, int value)
-{
- if (up->port.iotype == UPIO_RM9000) {
- __raw_writel(value, up->port.membase + 0x08);
- __raw_writel(value >> 8, up->port.membase + 0x10);
- } else {
- default_dl_write(up, value);
- }
-}
-#else
static int _serial_dl_read(struct uart_8250_port *up)
{
return default_dl_read(up);
@@ -325,7 +306,6 @@ static void _serial_dl_write(struct uart
{
default_dl_write(up, value);
}
-#endif
#ifdef CONFIG_MIPS_ALCHEMY
@@ -373,7 +353,7 @@ static void au_serial_dl_write(struct ua
#endif
-#if defined(CONFIG_SERIAL_8250_RM9K)
+#ifdef CONFIG_SERIAL_8250_RM9K
static const u8
regmap_in[8] = {
@@ -397,28 +377,36 @@ static const u8
[UART_SCR] = 0x2c
};
-static inline int map_8250_in_reg(struct uart_port *p, int offset)
+static unsigned int rm9k_serial_in(struct uart_port *p, int offset)
+{
+ offset = regmap_in[offset] << p->regshift;
+ return readl(p->membase + offset);
+}
+
+static void rm9k_serial_out(struct uart_port *p, int offset, int value)
{
- if (p->iotype != UPIO_RM9000)
- return offset;
- return regmap_in[offset];
+ offset = regmap_out[offset] << p->regshift;
+ writel(value, p->membase + offset);
+}
+
+static int rm9k_serial_dl_read(struct uart_8250_port *up)
+{
+ return ((__raw_readl(up->port.membase + 0x10) << 8) |
+ (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff;
}
-static inline int map_8250_out_reg(struct uart_port *p, int offset)
+static void rm9k_serial_dl_write(struct uart_8250_port *up, int value)
{
- if (p->iotype != UPIO_RM9000)
- return offset;
- return regmap_out[offset];
+ __raw_writel(value, up->port.membase + 0x08);
+ __raw_writel(value >> 8, up->port.membase + 0x10);
}
-#else
+#endif
/* sane hardware needs no mapping */
#define map_8250_in_reg(up, offset) (offset)
#define map_8250_out_reg(up, offset) (offset)
-#endif
-
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
offset = map_8250_in_reg(p, offset) << p->regshift;
@@ -490,12 +478,20 @@ static void set_io_from_upio(struct uart
p->serial_out = mem_serial_out;
break;
- case UPIO_RM9000:
case UPIO_MEM32:
p->serial_in = mem32_serial_in;
p->serial_out = mem32_serial_out;
break;
+#ifdef CONFIG_SERIAL_8250_RM9K
+ case UPIO_RM9000:
+ p->serial_in = rm9k_serial_in;
+ p->serial_out = rm9k_serial_out;
+ up->dl_read = rm9k_serial_dl_read;
+ up->dl_write = rm9k_serial_dl_write;
+ break;
+#endif
+
#ifdef CONFIG_MIPS_ALCHEMY
case UPIO_AU:
p->serial_in = au_serial_in;
From: Magnus Damm <[email protected]>
Get rid of unused functions and macros now when
Alchemy and RM9K are converted over to callbacks.
Signed-off-by: Magnus Damm <[email protected]>
---
drivers/tty/serial/8250/8250.c | 38 ++++++++++++--------------------------
1 file changed, 12 insertions(+), 26 deletions(-)
--- 0007/drivers/tty/serial/8250/8250.c
+++ work/drivers/tty/serial/8250/8250.c 2012-05-01 22:52:30.000000000 +0900
@@ -285,28 +285,18 @@ static const struct serial8250_config ua
};
/* Uart divisor latch read */
-static int default_dl_read(struct uart_8250_port *up)
+static int default_serial_dl_read(struct uart_8250_port *up)
{
return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
}
/* Uart divisor latch write */
-static void default_dl_write(struct uart_8250_port *up, int value)
+static void default_serial_dl_write(struct uart_8250_port *up, int value)
{
serial_out(up, UART_DLL, value & 0xff);
serial_out(up, UART_DLM, value >> 8 & 0xff);
}
-static int _serial_dl_read(struct uart_8250_port *up)
-{
- return default_dl_read(up);
-}
-
-static void _serial_dl_write(struct uart_8250_port *up, int value)
-{
- default_dl_write(up, value);
-}
-
#ifdef CONFIG_MIPS_ALCHEMY
/* Au1x00 UART hardware has a weird register layout */
@@ -403,57 +393,53 @@ static void rm9k_serial_dl_write(struct
#endif
-/* sane hardware needs no mapping */
-#define map_8250_in_reg(up, offset) (offset)
-#define map_8250_out_reg(up, offset) (offset)
-
static unsigned int hub6_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
outb(p->hub6 - 1 + offset, p->iobase);
return inb(p->iobase + 1);
}
static void hub6_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
outb(p->hub6 - 1 + offset, p->iobase);
outb(value, p->iobase + 1);
}
static unsigned int mem_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = offset << p->regshift;
return readb(p->membase + offset);
}
static void mem_serial_out(struct uart_port *p, int offset, int value)
{
- offset = map_8250_out_reg(p, offset) << p->regshift;
+ offset = 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;
+ offset = 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;
+ offset = offset << p->regshift;
return readl(p->membase + offset);
}
static unsigned int io_serial_in(struct uart_port *p, int offset)
{
- offset = map_8250_in_reg(p, offset) << p->regshift;
+ offset = 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;
+ offset = offset << p->regshift;
outb(value, p->iobase + offset);
}
@@ -464,8 +450,8 @@ static void set_io_from_upio(struct uart
struct uart_8250_port *up =
container_of(p, struct uart_8250_port, port);
- up->dl_read = _serial_dl_read;
- up->dl_write = _serial_dl_write;
+ up->dl_read = default_serial_dl_read;
+ up->dl_write = default_serial_dl_write;
switch (p->iotype) {
case UPIO_HUB6:
From: Magnus Damm <[email protected]>
Introduce yet another 8250 registration function.
This time it is serial8250_register_8250_port() and it
allows us to register 8250 hardware instances using struct
uart_8250_port. The new function makes it possible to
register 8250 hardware that makes use of 8250 specific
callbacks such as ->dl_read() and ->dl_write().
Signed-off-by: Magnus Damm <[email protected]>
---
drivers/tty/serial/8250/8250.c | 89 ++++++++++++++++++++++++++--------------
include/linux/serial_8250.h | 1
--- 0005/drivers/tty/serial/8250/8250.c
+++ work/drivers/tty/serial/8250/8250.c 2012-05-02 20:51:44.000000000 +0900
@@ -3111,7 +3111,7 @@ static struct uart_8250_port *serial8250
}
/**
- * serial8250_register_port - register a serial port
+ * serial8250_register_8250_port - register a serial port
* @port: serial port template
*
* Configure the serial port specified by the request. If the
@@ -3123,52 +3123,56 @@ static struct uart_8250_port *serial8250
*
* On success the port is ready to use and the line number is returned.
*/
-int serial8250_register_port(struct uart_port *port)
+int serial8250_register_8250_port(struct uart_8250_port *up)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
- if (port->uartclk == 0)
+ if (up->port.uartclk == 0)
return -EINVAL;
mutex_lock(&serial_mutex);
- uart = serial8250_find_match_or_unused(port);
+ uart = serial8250_find_match_or_unused(&up->port);
if (uart) {
uart_remove_one_port(&serial8250_reg, &uart->port);
- uart->port.iobase = port->iobase;
- uart->port.membase = port->membase;
- uart->port.irq = port->irq;
- uart->port.irqflags = port->irqflags;
- uart->port.uartclk = port->uartclk;
- uart->port.fifosize = port->fifosize;
- uart->port.regshift = port->regshift;
- uart->port.iotype = port->iotype;
- uart->port.flags = port->flags | UPF_BOOT_AUTOCONF;
- uart->port.mapbase = port->mapbase;
- uart->port.private_data = port->private_data;
- if (port->dev)
- uart->port.dev = port->dev;
+ uart->port.iobase = up->port.iobase;
+ uart->port.membase = up->port.membase;
+ uart->port.irq = up->port.irq;
+ uart->port.irqflags = up->port.irqflags;
+ uart->port.uartclk = up->port.uartclk;
+ uart->port.fifosize = up->port.fifosize;
+ uart->port.regshift = up->port.regshift;
+ uart->port.iotype = up->port.iotype;
+ uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF;
+ uart->port.mapbase = up->port.mapbase;
+ uart->port.private_data = up->port.private_data;
+ if (up->port.dev)
+ uart->port.dev = up->port.dev;
- if (port->flags & UPF_FIXED_TYPE)
- serial8250_init_fixed_type_port(uart, port->type);
+ if (up->port.flags & UPF_FIXED_TYPE)
+ serial8250_init_fixed_type_port(uart, up->port.type);
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;
- if (port->handle_irq)
- uart->port.handle_irq = port->handle_irq;
+ if (up->port.serial_in)
+ uart->port.serial_in = up->port.serial_in;
+ if (up->port.serial_out)
+ uart->port.serial_out = up->port.serial_out;
+ if (up->port.handle_irq)
+ uart->port.handle_irq = up->port.handle_irq;
/* Possibly override set_termios call */
- if (port->set_termios)
- uart->port.set_termios = port->set_termios;
- if (port->pm)
- uart->port.pm = port->pm;
- if (port->handle_break)
- uart->port.handle_break = port->handle_break;
+ if (up->port.set_termios)
+ uart->port.set_termios = up->port.set_termios;
+ if (up->port.pm)
+ uart->port.pm = up->port.pm;
+ if (up->port.handle_break)
+ uart->port.handle_break = up->port.handle_break;
+ if (up->dl_read)
+ uart->dl_read = up->dl_read;
+ if (up->dl_write)
+ uart->dl_write = up->dl_write;
if (serial8250_isa_config != NULL)
serial8250_isa_config(0, &uart->port,
@@ -3182,6 +3186,29 @@ int serial8250_register_port(struct uart
return ret;
}
+EXPORT_SYMBOL(serial8250_register_8250_port);
+
+/**
+ * serial8250_register_port - register a serial port
+ * @port: serial port template
+ *
+ * Configure the serial port specified by the request. If the
+ * port exists and is in use, it is hung up and unregistered
+ * first.
+ *
+ * The port is then probed and if necessary the IRQ is autodetected
+ * If this fails an error is returned.
+ *
+ * On success the port is ready to use and the line number is returned.
+ */
+int serial8250_register_port(struct uart_port *port)
+{
+ struct uart_8250_port up;
+
+ memset(&up, 0, sizeof(up));
+ memcpy(&up.port, port, sizeof(*port));
+ return serial8250_register_8250_port(&up);
+}
EXPORT_SYMBOL(serial8250_register_port);
/**
--- 0001/include/linux/serial_8250.h
+++ work/include/linux/serial_8250.h 2012-05-02 20:47:21.000000000 +0900
@@ -69,6 +69,7 @@ enum {
struct uart_port;
struct uart_8250_port;
+int serial8250_register_8250_port(struct uart_8250_port *);
int serial8250_register_port(struct uart_port *);
void serial8250_unregister_port(int line);
void serial8250_suspend_port(int line);
From: Magnus Damm <[email protected]>
Add a platform driver for Emma Mobile 8250 hardware.
The hardware itself has according to the data sheet
up to 64 byte FIFOs but at this point we only make
use of the 16550 compatible mode.
To support this piece of hardware the common UART
registers need to be remapped, and the access size
differences need to be handled.
The DLL and DLM registers can due to offset collision
not be remapped easily, and because of that this
driver makes use of ->dl_read() and ->dl_write()
callbacks. This in turn requires a registration
function that takes 8250-specific paramenters.
Future potential enhancements include DT support,
early platform driver console and fine grained PM.
Signed-off-by: Magnus Damm <[email protected]>
---
Developed for the UARTs in Emma Mobile EV2, but should
in theory also support other SoCs in the Emma Mobile line.
drivers/tty/serial/8250/8250_em.c | 179 +++++++++++++++++++++++++++++++++++++
drivers/tty/serial/8250/Kconfig | 8 +
drivers/tty/serial/8250/Makefile | 1
3 files changed, 188 insertions(+)
--- /dev/null
+++ work/drivers/tty/serial/8250/8250_em.c 2012-05-02 17:25:20.000000000 +0900
@@ -0,0 +1,179 @@
+/*
+ * Renesas Emma Mobile 8250 driver
+ *
+ * Copyright (C) 2012 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include "8250.h"
+
+#define UART_DLL_EM 9
+#define UART_DLM_EM 10
+
+struct serial8250_em_priv {
+ struct clk *sclk;
+ int line;
+};
+
+static void serial8250_em_serial_out(struct uart_port *p, int offset, int value)
+{
+ switch (offset) {
+ case UART_TX: /* TX @ 0x00 */
+ writeb(value, p->membase);
+ break;
+ case UART_FCR: /* FCR @ 0x0c (+1) */
+ case UART_LCR: /* LCR @ 0x10 (+1) */
+ case UART_MCR: /* MCR @ 0x14 (+1) */
+ case UART_SCR: /* SCR @ 0x20 (+1) */
+ writel(value, p->membase + ((offset + 1) << 2));
+ break;
+ case UART_IER: /* IER @ 0x04 */
+ value &= 0x0f; /* only 4 valid bits - not Xscale */
+ /* fall-through */
+ case UART_DLL_EM: /* DLL @ 0x24 (+9) */
+ case UART_DLM_EM: /* DLM @ 0x28 (+9) */
+ writel(value, p->membase + (offset << 2));
+ }
+}
+
+static unsigned int serial8250_em_serial_in(struct uart_port *p, int offset)
+{
+ switch (offset) {
+ case UART_RX: /* RX @ 0x00 */
+ return readb(p->membase);
+ case UART_MCR: /* MCR @ 0x14 (+1) */
+ case UART_LSR: /* LSR @ 0x18 (+1) */
+ case UART_MSR: /* MSR @ 0x1c (+1) */
+ case UART_SCR: /* SCR @ 0x20 (+1) */
+ return readl(p->membase + ((offset + 1) << 2));
+ case UART_IER: /* IER @ 0x04 */
+ case UART_IIR: /* IIR @ 0x08 */
+ case UART_DLL_EM: /* DLL @ 0x24 (+9) */
+ case UART_DLM_EM: /* DLM @ 0x28 (+9) */
+ return readl(p->membase + (offset << 2));
+ }
+ return 0;
+}
+
+static int serial8250_em_serial_dl_read(struct uart_8250_port *up)
+{
+ return serial_in(up, UART_DLL_EM) | serial_in(up, UART_DLM_EM) << 8;
+}
+
+static void serial8250_em_serial_dl_write(struct uart_8250_port *up, int value)
+{
+ serial_out(up, UART_DLL_EM, value & 0xff);
+ serial_out(up, UART_DLM_EM, value >> 8 & 0xff);
+}
+
+static int __devinit serial8250_em_probe(struct platform_device *pdev)
+{
+ struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ struct serial8250_em_priv *priv;
+ struct uart_8250_port up;
+ int ret = -EINVAL;
+
+ if (!regs || !irq) {
+ dev_err(&pdev->dev, "missing registers or irq\n");
+ goto err0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "unable to allocate private data\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ priv->sclk = clk_get(&pdev->dev, "sclk");
+ if (!priv->sclk) {
+ dev_err(&pdev->dev, "unable to get clock\n");
+ goto err1;
+ }
+
+ memset(&up, 0, sizeof(up));
+ up.port.mapbase = regs->start; /* assume 1:1 entity map */
+ up.port.membase = (void __iomem *)regs->start;
+ up.port.irq = irq->start;
+ up.port.type = PORT_UNKNOWN;
+ up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
+ up.port.dev = &pdev->dev;
+ up.port.private_data = priv;
+
+ clk_enable(priv->sclk);
+ up.port.uartclk = clk_get_rate(priv->sclk);
+
+ up.port.iotype = UPIO_MEM32;
+ up.port.serial_in = serial8250_em_serial_in;
+ up.port.serial_out = serial8250_em_serial_out;
+ up.dl_read = serial8250_em_serial_dl_read;
+ up.dl_write = serial8250_em_serial_dl_write;
+
+ ret = serial8250_register_8250_port(&up);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register 8250 port\n");
+ goto err2;
+ }
+
+ priv->line = ret;
+ platform_set_drvdata(pdev, priv);
+ return 0;
+
+ err2:
+ clk_disable(priv->sclk);
+ clk_put(priv->sclk);
+ err1:
+ kfree(priv);
+ err0:
+ return ret;
+}
+
+static int __devexit serial8250_em_remove(struct platform_device *pdev)
+{
+ struct serial8250_em_priv *priv = platform_get_drvdata(pdev);
+
+ serial8250_unregister_port(priv->line);
+ clk_disable(priv->sclk);
+ clk_put(priv->sclk);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver serial8250_em_platform_driver = {
+ .driver = {
+ .name = "serial8250-em",
+ .owner = THIS_MODULE,
+ },
+ .probe = serial8250_em_probe,
+ .remove = __devexit_p(serial8250_em_remove),
+};
+
+module_platform_driver(serial8250_em_platform_driver);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("Renesas Emma Mobile 8250 Driver");
+MODULE_LICENSE("GPL v2");
--- 0001/drivers/tty/serial/8250/Kconfig
+++ work/drivers/tty/serial/8250/Kconfig 2012-05-02 17:25:19.000000000 +0900
@@ -278,3 +278,11 @@ config SERIAL_8250_DW
help
Selecting this option will enable handling of the extra features
present in the Synopsys DesignWare APB UART.
+
+config SERIAL_8250_EM
+ tristate "Support for Emma Mobile intergrated serial port"
+ depends on SERIAL_8250
+ help
+ Selecting this option will add support for the integrated serial
+ port hardware found on the Emma Mobile line of processors.
+ If unsure, say N.
--- 0001/drivers/tty/serial/8250/Makefile
+++ work/drivers/tty/serial/8250/Makefile 2012-05-02 17:25:19.000000000 +0900
@@ -18,3 +18,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_
obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
+obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
On Wed, 02 May 2012 21:46:42 +0900
Magnus Damm <[email protected]> wrote:
> serial8250: DLL/DLM rework, Emma Mobile UART driver
>
> [PATCH 01/06] serial8250: Add dl_read()/dl_write() callbacks
> [PATCH 02/06] serial8250: Use dl_read()/dl_write() on Alchemy
> [PATCH 03/06] serial8250: Use dl_read()/dl_write() on RM9K
> [PATCH 04/06] serial8250: Clean up default map and dl code
> [PATCH 05/06] serial8250: Introduce serial8250_register_8250_port()
> [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
>
> This series cleans up and adjusts the 8250 code base to allow
> hooking in a driver for the UART in Emma Mobile SoCs.
>
> In short:
> - Patch 1-4 tie in 8250-specific dl_read()/dl_write() callbacks.
> - Patch 5 adds a new register function to use new callbacks.
> - Patch 6 adds a new driver that makes use of the above.
>
> I'd be happy to rework the above patches if needed. The
> main challenge for Emma Mobile UART support is the non-
> standard offsets for DLL and DLM registers.
>
> Note that there is no DT support included at this point,
> but it boils down to a 10 line change. The boot loader on
> my board does not do DT so I'd like to use kexec for DT
> development (as usual), but to use kexec I first need to
> get a non-DT kernel working. Which is basically this. =)
>
> Signed-off-by: Magnus Damm <[email protected]>
Acked-by: Alan Cox <[email protected]>
On Wednesday 02 May 2012, Magnus Damm wrote:
> serial8250: DLL/DLM rework, Emma Mobile UART driver
>
> [PATCH 01/06] serial8250: Add dl_read()/dl_write() callbacks
> [PATCH 02/06] serial8250: Use dl_read()/dl_write() on Alchemy
> [PATCH 03/06] serial8250: Use dl_read()/dl_write() on RM9K
> [PATCH 04/06] serial8250: Clean up default map and dl code
> [PATCH 05/06] serial8250: Introduce serial8250_register_8250_port()
> [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
>
> This series cleans up and adjusts the 8250 code base to allow
> hooking in a driver for the UART in Emma Mobile SoCs.
>
> In short:
> - Patch 1-4 tie in 8250-specific dl_read()/dl_write() callbacks.
> - Patch 5 adds a new register function to use new callbacks.
> - Patch 6 adds a new driver that makes use of the above.
>
> I'd be happy to rework the above patches if needed. The
> main challenge for Emma Mobile UART support is the non-
> standard offsets for DLL and DLM registers.
>
> Note that there is no DT support included at this point,
> but it boils down to a 10 line change. The boot loader on
> my board does not do DT so I'd like to use kexec for DT
> development (as usual), but to use kexec I first need to
> get a non-DT kernel working. Which is basically this. =)
>
> Signed-off-by: Magnus Damm <[email protected]>
Looks good,
Acked-by: Arnd Bergmann <[email protected]>
Arnd
On 12-05-02 08:47 AM, Magnus Damm wrote:
> From: Magnus Damm <[email protected]>
>
> Add a platform driver for Emma Mobile 8250 hardware.
[...]
> +
> +config SERIAL_8250_EM
> + tristate "Support for Emma Mobile intergrated serial port"
> + depends on SERIAL_8250
Should we depend on arch=ARM or something similar? Even if the
driver is written to be arch independent, if the Emma itself is
physically tied to ARM platforms, we probably don't need to be
building it for sparc allyesconfig etc.
P.
> + help
> + Selecting this option will add support for the integrated serial
> + port hardware found on the Emma Mobile line of processors.
> + If unsure, say N.
> --- 0001/drivers/tty/serial/8250/Makefile
> +++ work/drivers/tty/serial/8250/Makefile 2012-05-02 17:25:19.000000000 +0900
> @@ -18,3 +18,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_
> obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
> obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
> obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
> +obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
On Wed, May 02, 2012 at 10:41:12AM -0400, Paul Gortmaker wrote:
> On 12-05-02 08:47 AM, Magnus Damm wrote:
> > From: Magnus Damm <[email protected]>
> >
> > Add a platform driver for Emma Mobile 8250 hardware.
>
>
> [...]
>
> > +
> > +config SERIAL_8250_EM
> > + tristate "Support for Emma Mobile intergrated serial port"
> > + depends on SERIAL_8250
>
> Should we depend on arch=ARM or something similar? Even if the
> driver is written to be arch independent, if the Emma itself is
> physically tied to ARM platforms, we probably don't need to be
> building it for sparc allyesconfig etc.
Yeah, it doesn't build on x86-64, I get the following build problems:
ERROR: "clk_get_rate" [drivers/tty/serial/8250/8250_em.ko] undefined!
ERROR: "clk_enable" [drivers/tty/serial/8250/8250_em.ko] undefined!
ERROR: "clk_get" [drivers/tty/serial/8250/8250_em.ko] undefined!
ERROR: "clk_put" [drivers/tty/serial/8250/8250_em.ko] undefined!
ERROR: "clk_disable" [drivers/tty/serial/8250/8250_em.ko] undefined!
So it should depend on some arch that has clk at the least.
Magnus, care to redo just this one patch that fixes this up? I've
applied the first 5 already.
thanks,
greg k-h
On Wednesday 02 May 2012, Greg KH wrote:
> > > +
> > > +config SERIAL_8250_EM
> > > + tristate "Support for Emma Mobile intergrated serial port"
> > > + depends on SERIAL_8250
> >
> > Should we depend on arch=ARM or something similar? Even if the
> > driver is written to be arch independent, if the Emma itself is
> > physically tied to ARM platforms, we probably don't need to be
> > building it for sparc allyesconfig etc.
>
> Yeah, it doesn't build on x86-64, I get the following build problems:
> ERROR: "clk_get_rate" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ERROR: "clk_enable" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ERROR: "clk_get" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ERROR: "clk_put" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ERROR: "clk_disable" [drivers/tty/serial/8250/8250_em.ko] undefined!
>
> So it should depend on some arch that has clk at the least.
>
> Magnus, care to redo just this one patch that fixes this up? I've
> applied the first 5 already.
I think it's best to make it depend on HAVE_CLK, but depending on
ARM && HAVE_CLK is also fine with me. I would not like to have it depend
on EMMA specifically though because I want to increase built coverage.
Arnd
On Thu, May 3, 2012 at 6:22 AM, Greg KH <[email protected]> wrote:
> On Wed, May 02, 2012 at 10:41:12AM -0400, Paul Gortmaker wrote:
>> On 12-05-02 08:47 AM, Magnus Damm wrote:
>> > From: Magnus Damm <[email protected]>
>> >
>> > Add a platform driver for Emma Mobile 8250 hardware.
>>
>>
>> [...]
>>
>> > +
>> > +config SERIAL_8250_EM
>> > + ? tristate "Support for Emma Mobile intergrated serial port"
>> > + ? depends on SERIAL_8250
>>
>> Should we depend on arch=ARM or something similar? ?Even if the
>> driver is written to be arch independent, if the Emma itself is
>> physically tied to ARM platforms, we probably don't need to be
>> building it for sparc allyesconfig etc.
>
> Yeah, it doesn't build on x86-64, I get the following build problems:
> ? ? ? ?ERROR: "clk_get_rate" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ? ? ? ?ERROR: "clk_enable" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ? ? ? ?ERROR: "clk_get" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ? ? ? ?ERROR: "clk_put" [drivers/tty/serial/8250/8250_em.ko] undefined!
> ? ? ? ?ERROR: "clk_disable" [drivers/tty/serial/8250/8250_em.ko] undefined!
>
> So it should depend on some arch that has clk at the least.
>
> Magnus, care to redo just this one patch that fixes this up? ?I've
> applied the first 5 already.
Done!
I decided to go with SERIAL_8250 && ARM && HAVE_CLK to limit the
exposure a bit but not hinder any single ARM kernel image work. Hope
this balance is making everyone happy.
It is not impossible that this driver can be used on older MIPS based
SoCs from NEC, but that can easily be dealt with later if needed.
Thanks for your help!
/ magnus
On Wednesday 02 May 2012, Magnus Damm wrote:
> Note that there is no DT support included at this point,
> but it boils down to a 10 line change. The boot loader on
> my board does not do DT so I'd like to use kexec for DT
> development (as usual), but to use kexec I first need to
> get a non-DT kernel working. Which is basically this. =)
As a follow-up on this, based on my comments to your emma platform
code, I think it would be easy enough to just use the appended
dtb support that we have, which allows you to boot a DT-enabled
kernel with a legacy boot loader.
Arnd
On Sat, May 5, 2012 at 1:28 AM, Arnd Bergmann <[email protected]> wrote:
> On Wednesday 02 May 2012, Magnus Damm wrote:
>> Note that there is no DT support included at this point,
>> but it boils down to a 10 line change. The boot loader on
>> my board does not do DT so I'd like to use kexec for DT
>> development (as usual), but to use kexec I first need to
>> get a non-DT kernel working. Which is basically this. =)
>
> As a follow-up on this, based on my comments to your emma platform
> code, I think it would be easy enough to just use the appended
> dtb support that we have, which allows you to boot a DT-enabled
> kernel with a legacy boot loader.
Thanks for your comments! I have some code going with DT now. Will
post tomorrow.
As for the appended dtb support, do you know the recommended way to
include it in the uImage? It feels a bit odd to recommend customers to
patch their kernel source to build a bootable kernel. I ended up with
this local hackery, but there must be better ways:
--- 0001/arch/arm/boot/Makefile
+++ work/arch/arm/boot/Makefile 2012-05-09 00:40:17.000000000 +0900
@@ -53,6 +53,7 @@ $(obj)/compressed/vmlinux: $(obj)/Image
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
+ cat $(obj)/emev2-kzm9d.dtb >> $@
@echo ' Kernel: $@ is ready'
endif
Cheers,
/ magnus