serial8250: DLL/DLM rework, Emma Mobile UART driver
Subsystems (4)
cover serial8250: DLL/DLM rework, Emma Mobile UART driver
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. =)
---
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(-)
PATCH 1/6 serial8250: Add dl_read()/dl_write() callbacks
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.
---
drivers/tty/serial/8250/8250.c | 117 +++++++++++++++++++++------------------- drivers/tty/serial/8250/8250.h | 14 ++++ 2 files changed, 78 insertions(+), 53 deletions(-)
drivers/tty/serial/8250/8250.c +64 -53
@@ -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 */
drivers/tty/serial/8250/8250.h +14 -0
@@ -37,6 +37,10 @@ struct uart_8250_port {unsigned char lsr_saved_flags;#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTAunsigned 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
PATCH 2/6 serial8250: Use dl_read()/dl_write() on Alchemy
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
Convert the 8250 Alchemy support code to make
use of the new dl_read()/dl_write() callbacks.
---
drivers/tty/serial/8250/8250.c | 67 +++++++++++++++------------------------- 1 file changed, 26 insertions(+), 41 deletions(-)
drivers/tty/serial/8250/8250.c +26 -41
@@ -297,24 +297,7 @@ static void default_dl_write(struct uartserial_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(strureturn 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;
PATCH 3/6 serial8250: Use dl_read()/dl_write() on RM9K
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
Convert the 8250 RM9K support code to make
use of the new dl_read()/dl_write() callbacks.
---
drivers/tty/serial/8250/8250.c | 62 ++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 33 deletions(-)
drivers/tty/serial/8250/8250.c +29 -33
@@ -297,25 +297,6 @@ static void default_dl_write(struct uartserial_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;
PATCH 4/6 serial8250: Clean up default map and dl code
In reply to Magnus DammFrom: Magnus Damm <[email protected]>
Get rid of unused functions and macros now when
Alchemy and RM9K are converted over to callbacks.
---
drivers/tty/serial/8250/8250.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-)
drivers/tty/serial/8250/8250.c +12 -26
@@ -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 uartstruct 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 :
PATCH 5/6 serial8250: Introduce serial8250_register_8250_port()
In reply to Magnus DammFrom: 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().
---
drivers/tty/serial/8250/8250.c | 89 ++++++++++++++++++++++++++-------------- include/linux/serial_8250.h | 1
drivers/tty/serial/8250/8250.c +58 -31
@@ -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 uartreturn 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);/**
include/linux/serial_8250.h +1 -0
@@ -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);
PATCH 6/6 serial8250-em: Add Emma Mobile UART driver
In reply to Magnus DammFrom: 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.
---
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(+)
drivers/tty/serial/8250/8250_em.c new file +179 -0
@@ -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" );
drivers/tty/serial/8250/Kconfig +8 -0
@@ -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.
drivers/tty/serial/8250/Makefile +1 -0
@@ -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
Re: [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
Quoted 3 lines · Magnus Damm
From: Magnus Damm <[email protected]>
Add a platform driver for Emma Mobile 8250 hardware.
[...]
drivers/tty/serial/8250/Kconfig config SERIAL_8250_DW · line 284
Quoted 4 lines
drivers/tty/serial/8250/Kconfig +4
-0+ +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.
Quoted 11 lines
drivers/tty/serial/8250/Kconfig +4
-0+ 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.drivers/tty/serial/8250/Makefile +1
-0@@ -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
Greg KH
maintainerRe: [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
Quoted 8 lines · Paul Gortmaker
Quoted 3 lines · Magnus Damm
From: Magnus Damm <[email protected]>
Add a platform driver for Emma Mobile 8250 hardware.
[...]
Quoted 9 lines
drivers/tty/serial/8250/Kconfig config SERIAL_8250_DW · line 284
Quoted 4 lines
drivers/tty/serial/8250/Kconfig +4
-0+ +config SERIAL_8250_EM + tristate "Support for Emma Mobile intergrated serial port" + depends on SERIAL_8250Should 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
Re: [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
Quoted 17 lines · Greg KH
Quoted 9 lines
drivers/tty/serial/8250/Kconfig config SERIAL_8250_DW · line 284
Quoted 4 lines
drivers/tty/serial/8250/Kconfig +4
-0+ +config SERIAL_8250_EM + tristate "Support for Emma Mobile intergrated serial port" + depends on SERIAL_8250Should 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!
Quoted 4 lines
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
Re: [PATCH 06/06] serial8250-em: Add Emma Mobile UART driver
In reply to Greg KHQuoted 26 lines · Greg KH
Quoted 8 lines · Paul Gortmaker
Quoted 3 lines · Magnus Damm
From: Magnus Damm <[email protected]>
Add a platform driver for Emma Mobile 8250 hardware.
[...]
Quoted 9 lines
drivers/tty/serial/8250/Kconfig config SERIAL_8250_DW · line 284
Quoted 4 lines
drivers/tty/serial/8250/Kconfig +4
-0+ +config SERIAL_8250_EM + tristate "Support for Emma Mobile intergrated serial port" + depends on SERIAL_8250Should 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!
Quoted 4 lines
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
Quoted 21 lines · 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 driverThis 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.
Quoted 7 lines
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]>
Quoted 21 lines · Magnus Damm
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 driverThis 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.
Quoted 7 lines
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,
Arnd
Quoted 5 lines · Magnus Damm
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
Quoted 11 lines · Arnd Bergmann
Quoted 5 lines · Magnus Damm
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:
arch/arm/boot/Makefile 2012-05-09 00:40:17.000000000 +0900 renamed from arch/arm/boot/Makefile +1 -0
@@ -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