Hi Andre, On 08/29/2014 12:13 PM, Andre Przywara wrote: > The ARM Server Base System Architecture (SBSA) describes a generic > UART which all compliant level 1 systems should implement. This is > actually a PL011 subset, so a full PL011 implementation will satisfy > this requirement. > However if a system does not have a PL011, a very stripped down > implementation complying to the SBSA defined specification will > suffice. The Linux PL011 driver is not guaranteed to drive this > limited device (and indeed the fast model implentation hangs the > kernel if driven by the PL011 driver). > So introduce a new driver just implementing the part specified by the > SBSA (which lacks DMA, the modem control signals and many of the > registers including baud rate control). This driver has been derived > by the actual PL011 one, removing all unnecessary code. > > Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx> > --- > .../devicetree/bindings/serial/arm_sbsa_uart.txt | 6 + > drivers/tty/serial/Kconfig | 28 + > drivers/tty/serial/Makefile | 1 + > drivers/tty/serial/sbsa_uart.c | 793 ++++++++++++++++++++ > include/uapi/linux/serial_core.h | 1 + > 5 files changed, 829 insertions(+) > create mode 100644 Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt > create mode 100644 drivers/tty/serial/sbsa_uart.c > > diff --git a/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt > new file mode 100644 > index 0000000..8e2c5d6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt > @@ -0,0 +1,6 @@ > +* ARM SBSA defined generic UART > + > +Required properties: > +- compatible: must be "arm,sbsa-uart" > +- reg: exactly one register range > +- interrupts: exactly one interrupt specifier > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > index 26cec64..faecfad 100644 > --- a/drivers/tty/serial/Kconfig > +++ b/drivers/tty/serial/Kconfig > @@ -45,6 +45,34 @@ config SERIAL_AMBA_PL010_CONSOLE > your boot loader (lilo or loadlin) about how to pass options to the > kernel at boot time.) > > +config SERIAL_SBSA_UART > + tristate "ARM SBSA UART serial port support" > + select SERIAL_CORE > + help > + This selects the UART defined in the ARM(R) Server Base System > + Architecture (SBSA). > + It is a true subset of the ARM(R) PL011 UART and this driver can > + also drive those full featured UARTs. > + To match the SBSA, this driver will not support initialization, DMA, > + baudrate control and modem control lines. > + > +config SERIAL_SBSA_UART_CONSOLE > + bool "Support for console on ARM SBSA UART serial port" > + depends on SERIAL_SBSA_UART=y > + select SERIAL_CORE_CONSOLE > + select SERIAL_EARLYCON > + ---help--- > + Say Y here if you wish to use an ARM SBSA UART as the system > + console (the system console is the device which receives all kernel > + messages and warnings and which allows logins in single user mode). > + > + Even if you say Y here, the currently visible framebuffer console > + (/dev/tty0) will still be used as the system console by default, but > + you can alter that using a kernel command line option such as > + "console=ttyAMA0". (Try "man bootparam" or see the documentation of > + your boot loader (lilo or loadlin) about how to pass options to the > + kernel at boot time.) > + > config SERIAL_AMBA_PL011 > tristate "ARM AMBA PL011 serial port support" > depends on ARM_AMBA > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile > index 0080cc3..2e560e9 100644 > --- a/drivers/tty/serial/Makefile > +++ b/drivers/tty/serial/Makefile > @@ -22,6 +22,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250/ > > obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o > obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o > +obj-$(CONFIG_SERIAL_SBSA_UART) += sbsa_uart.o > obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o > obj-$(CONFIG_SERIAL_PXA) += pxa.o > obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o > diff --git a/drivers/tty/serial/sbsa_uart.c b/drivers/tty/serial/sbsa_uart.c > new file mode 100644 > index 0000000..941a305 > --- /dev/null > +++ b/drivers/tty/serial/sbsa_uart.c > @@ -0,0 +1,793 @@ > +/* > + * Driver for ARM SBSA specified serial ports (stripped down PL011) > + * > + * Based on drivers/tty/char/amba-pl011.c > + * > + * Copyright 2014 ARM Limited > + * > + * 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, or > + * (at your option) any later version. > + * > + * 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, see <http://www.gnu.org/licenses/>. > + * > + * This is a driver implementing the ARM SBSA specified generic UART, > + * which is a subset of the PL011 UART, but lacking: > + * - DMA > + * - hardware flow control > + * - modem control > + * - IrDA SIR > + * - word lengths other than 8 bits > + * - baudrate settings > + */ > + > + > +#if defined(CONFIG_SERIAL_SBSA_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) > +#define SUPPORT_SYSRQ > +#endif > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/console.h> > +#include <linux/sysrq.h> > +#include <linux/device.h> > +#include <linux/tty.h> > +#include <linux/tty_flip.h> > +#include <linux/serial_core.h> > +#include <linux/serial.h> > +#include <linux/amba/serial.h> > +#include <linux/slab.h> > +#include <linux/types.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/sizes.h> > +#include <linux/io.h> > + > +#define UART_NR 14 > + > +#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) > +#define UART_DUMMY_DR_RX (1 << 16) > + > +/* > + * Reads up to 256 characters from the FIFO or until it's empty and > + * inserts them into the TTY layer. Returns the number of characters > + * read from the FIFO. > + */ > +static int sbsa_uart_fifo_to_tty(struct uart_port *port) > +{ > + u16 status, ch; > + unsigned int flag, max_count = 256; > + int fifotaken = 0; > + > + while (max_count--) { > + status = readw(port->membase + UART01x_FR); > + if (status & UART01x_FR_RXFE) > + break; > + > + /* Take chars from the FIFO and update status */ > + ch = readw(port->membase + UART01x_DR) | UART_DUMMY_DR_RX; > + flag = TTY_NORMAL; > + port->icount.rx++; > + fifotaken++; > + > + if (unlikely(ch & UART_DR_ERROR)) { > + if (ch & UART011_DR_BE) { > + ch &= ~(UART011_DR_FE | UART011_DR_PE); > + port->icount.brk++; > + if (uart_handle_break(port)) > + continue; > + } else if (ch & UART011_DR_PE) > + port->icount.parity++; > + else if (ch & UART011_DR_FE) > + port->icount.frame++; > + if (ch & UART011_DR_OE) > + port->icount.overrun++; > + > + ch &= port->read_status_mask; > + > + if (ch & UART011_DR_BE) > + flag = TTY_BREAK; > + else if (ch & UART011_DR_PE) > + flag = TTY_PARITY; > + else if (ch & UART011_DR_FE) > + flag = TTY_FRAME; > + } > + > + if (uart_handle_sysrq_char(port, ch & 255)) > + continue; > + > + uart_insert_char(port, ch, UART011_DR_OE, ch, flag); > + } > + > + return fifotaken; > +} > + > +static void sbsa_uart_mask_irq(struct uart_port *port, u16 irqclr, u16 irqset) > +{ > + u16 imsc; > + > + imsc = readw(port->membase + UART011_IMSC); > + imsc = (imsc & ~irqclr) | irqset; > + writew(imsc, port->membase + UART011_IMSC); > +} > + > +static void sbsa_uart_stop_tx(struct uart_port *port) > +{ > + sbsa_uart_mask_irq(port, UART011_TXIM, 0); > +} > + > +static void sbsa_uart_start_tx(struct uart_port *port) > +{ > + sbsa_uart_mask_irq(port, 0, UART011_TXIM); > +} > + > +static void sbsa_uart_stop_rx(struct uart_port *port) > +{ > + sbsa_uart_mask_irq(port, UART011_RXIM | UART011_RTIM, 0); > +} > + > +static void sbsa_uart_rx_chars(struct uart_port *port) > +__releases(&uap->port.lock) > +__acquires(&uap->port.lock) ^^^^^^^^^^ I think sparse will choke here. > +{ > + sbsa_uart_fifo_to_tty(port); > + > + spin_unlock(&port->lock); > + tty_flip_buffer_push(&port->state->port); > + spin_lock(&port->lock); > +} > + > +static void sbsa_uart_tx_chars(struct uart_port *port) > +{ > + struct circ_buf *xmit = &port->state->xmit; > + int count; > + > + if (port->x_char) { > + writew(port->x_char, port->membase + UART01x_DR); > + port->icount.tx++; > + port->x_char = 0; > + return; > + } > + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { > + sbsa_uart_stop_tx(port); > + return; > + } > + > + count = port->fifosize >> 1; > + do { > + writew(xmit->buf[xmit->tail], port->membase + UART01x_DR); > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + port->icount.tx++; > + if (uart_circ_empty(xmit)) > + break; > + } while (--count > 0); > + > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(port); > + > + if (uart_circ_empty(xmit)) > + sbsa_uart_stop_tx(port); > +} > + > +static irqreturn_t sbsa_uart_int(int irq, void *dev_id) > +{ > + struct uart_port *port = dev_id; > + unsigned long flags; > + unsigned int status, pass_counter = 32; > + int handled = 0; > + > + spin_lock_irqsave(&port->lock, flags); > + status = readw(port->membase + UART011_RIS); > + status &= readw(port->membase + UART011_IMSC); > + if (status) { > + do { > + writew(status & ~(UART011_TXIS|UART011_RTIS| > + UART011_RXIS), > + port->membase + UART011_ICR); > + > + if (status & (UART011_RTIS|UART011_RXIS)) > + sbsa_uart_rx_chars(port); > + > + if (status & UART011_TXIS) > + sbsa_uart_tx_chars(port); > + > + if (pass_counter-- == 0) > + break; > + > + status = readw(port->membase + UART011_RIS); > + status &= ~readw(port->membase + UART011_IMSC); > + } while (status != 0); > + handled = 1; > + } > + > + spin_unlock_irqrestore(&port->lock, flags); > + > + return IRQ_RETVAL(handled); > +} > + > +static unsigned int sbsa_uart_tx_empty(struct uart_port *port) > +{ > + unsigned int status = readw(port->membase + UART01x_FR); > + > + return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; > +} > + > +static unsigned int sbsa_uart_get_mctrl(struct uart_port *port) > +{ > + return 0; > +} > + > +static void sbsa_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) > +{ > +} > + > +#ifdef CONFIG_CONSOLE_POLL > + > +static void sbsa_uart_quiesce_irqs(struct uart_port *port) > +{ > + writew(readw(port->membase + UART011_RIS), port->membase + UART011_ICR); > + /* > + * There is no way to clear TXIM as this is "ready to transmit IRQ", so > + * we simply mask it. start_tx() will unmask it. > + * > + * Note we can race with start_tx(), and if the race happens, the > + * polling user might get another interrupt just after we clear it. > + * But it should be OK and can happen even w/o the race, e.g. > + * controller immediately got some new data and raised the IRQ. > + * > + * And whoever uses polling routines assumes that it manages the device > + * (including tx queue), so we're also fine with start_tx()'s caller > + * side. > + */ > + writew(readw(port->membase + UART011_IMSC) & ~UART011_TXIM, > + port->membase + UART011_IMSC); > +} > + > +static int sbsa_uart_get_poll_char(struct uart_port *port) > +{ > + unsigned int status; > + > + /* > + * The caller might need IRQs lowered, e.g. if used with KDB NMI > + * debugger. > + */ > + sbsa_uart_quiesce_irqs(port); > + > + status = readw(port->membase + UART01x_FR); > + if (status & UART01x_FR_RXFE) > + return NO_POLL_CHAR; > + > + return readw(port->membase + UART01x_DR); > +} > + > +static void sbsa_uart_put_poll_char(struct uart_port *port, unsigned char ch) > +{ > + while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF) > + barrier(); > + > + writew(ch, port->membase + UART01x_DR); > +} > + > +#endif /* CONFIG_CONSOLE_POLL */ > + > +static int sbsa_uart_hwinit(struct uart_port *port) > +{ > + /* Clear pending error and receive interrupts */ > + writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS | > + UART011_RTIS | UART011_RXIS, port->membase + UART011_ICR); > + > + writew(UART011_RTIM | UART011_RXIM, port->membase + UART011_IMSC); > + > + return 0; > +} > + > +static int sbsa_uart_startup(struct uart_port *port) > +{ > + int retval; > + > + retval = sbsa_uart_hwinit(port); > + if (retval) > + return retval; > + > + /* > + * Allocate the IRQ > + */ > + retval = request_irq(port->irq, sbsa_uart_int, 0, "uart-sbsa", port); > + if (retval) > + return retval; > + > + /* > + * Provoke TX FIFO interrupt into asserting. Taking care to preserve > + * baud rate and data format specified by FBRD, IBRD and LCRH as the > + * UART may already be in use as a console. > + */ > + spin_lock_irq(&port->lock); > + > + /* > + * this does not work on the SBSA UART, because it does not have > + * a CR and thus not loopback enable bit > + */ > + writew(0, port->membase + UART01x_DR); > + while (readw(port->membase + UART01x_FR) & UART01x_FR_BUSY) > + barrier(); > + > + /* Clear out any spuriously appearing RX interrupts and enable them */ > + writew(UART011_RTIS | UART011_RXIS, port->membase + UART011_ICR); > + writew(UART011_RTIM | UART011_RXIM, port->membase + UART011_IMSC); > + spin_unlock_irq(&port->lock); > + > + return 0; > +} > + > +static void sbsa_uart_shutdown(struct uart_port *port) > +{ > + u16 im; > + > + /* > + * disable all interrupts > + */ > + spin_lock_irq(&port->lock); > + im = 0; > + writew(im, port->membase + UART011_IMSC); > + writew(0xffff, port->membase + UART011_ICR); > + spin_unlock_irq(&port->lock); > + > + /* > + * Free the interrupt > + */ > + free_irq(port->irq, port); > + > +} > + > +static void > +sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios, > + struct ktermios *old) > +{ > + unsigned long flags; > + unsigned int baud = 115200; > + > + spin_lock_irqsave(&port->lock, flags); ^^^^^^^^^^^^^^^^^ Can be spin_lock_irq(&port->lock) here. > + > + /* > + * Update the per-port timeout. > + */ > + uart_update_timeout(port, termios->c_cflag, baud); > + > + port->read_status_mask = UART011_DR_OE | 255; > + if (termios->c_iflag & INPCK) > + port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; > + if (termios->c_iflag & (BRKINT | PARMRK)) ^^^^^^^ (IGNBRK | BRKINT | PARMRK) > + port->read_status_mask |= UART011_DR_BE; Otherwise, if IGNBRK is set, read_status_mask will mask the BRK condition so a garbage TTY_NORMAL byte will be added by uart_insert_char(). The pl011 driver has been fixed in -next. > + > + /* > + * Characters to ignore > + */ > + port->ignore_status_mask = 0; > + if (termios->c_iflag & IGNPAR) > + port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE; > + if (termios->c_iflag & IGNBRK) { > + port->ignore_status_mask |= UART011_DR_BE; > + /* > + * If we're ignoring parity and break indicators, > + * ignore overruns too (for real raw support). > + */ > + if (termios->c_iflag & IGNPAR) > + port->ignore_status_mask |= UART011_DR_OE; > + } > + > + /* > + * Ignore all characters if CREAD is not set. > + */ > + if ((termios->c_cflag & CREAD) == 0) > + port->ignore_status_mask |= UART_DUMMY_DR_RX; > + > + spin_unlock_irqrestore(&port->lock, flags); > +} > + > +static const char *sbsa_uart_type(struct uart_port *port) > +{ > + return port->type == PORT_SBSA ? "SBSA" : NULL; > +} > + > +/* > + * Release the memory region(s) being used by 'port' > + */ > +static void sbsa_uart_release_port(struct uart_port *port) > +{ > + release_mem_region(port->mapbase, SZ_4K); > +} > + > +/* > + * Request the memory region(s) being used by 'port' > + */ > +static int sbsa_uart_request_port(struct uart_port *port) > +{ > + return request_mem_region(port->mapbase, SZ_4K, "uart-sbsa") > + != NULL ? 0 : -EBUSY; > +} > + > +/* > + * Configure/autoconfigure the port. > + */ > +static void sbsa_uart_config_port(struct uart_port *port, int flags) > +{ > + if (flags & UART_CONFIG_TYPE) { > + port->type = PORT_SBSA; > + sbsa_uart_request_port(port); > + } > +} > + > +/* > + * verify the new serial_struct (for TIOCSSERIAL). > + */ > +static int sbsa_uart_verify_port(struct uart_port *port, > + struct serial_struct *ser) > +{ > + int ret = 0; > + > + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SBSA) > + ret = -EINVAL; > + if (ser->irq < 0 || ser->irq >= nr_irqs) > + ret = -EINVAL; > + return ret; > +} > + > +static struct uart_ops sbsa_uart_pops = { > + .tx_empty = sbsa_uart_tx_empty, > + .set_mctrl = sbsa_uart_set_mctrl, > + .get_mctrl = sbsa_uart_get_mctrl, > + .stop_tx = sbsa_uart_stop_tx, > + .start_tx = sbsa_uart_start_tx, > + .stop_rx = sbsa_uart_stop_rx, > + .enable_ms = NULL, > + .break_ctl = NULL, > + .startup = sbsa_uart_startup, > + .shutdown = sbsa_uart_shutdown, > + .flush_buffer = NULL, > + .set_termios = sbsa_uart_set_termios, > + .type = sbsa_uart_type, > + .release_port = sbsa_uart_release_port, > + .request_port = sbsa_uart_request_port, > + .config_port = sbsa_uart_config_port, > + .verify_port = sbsa_uart_verify_port, > +#ifdef CONFIG_CONSOLE_POLL > + .poll_init = sbsa_uart_hwinit, > + .poll_get_char = sbsa_uart_get_poll_char, > + .poll_put_char = sbsa_uart_put_poll_char, > +#endif > +}; > + > +static struct uart_port *sbsa_uart_ports[UART_NR]; > + > +#ifdef CONFIG_SERIAL_SBSA_UART_CONSOLE > + > +static void sbsa_uart_console_putchar(struct uart_port *port, int ch) > +{ > + while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF) > + barrier(); > + writew(ch, port->membase + UART01x_DR); > +} > + > +static void > +sbsa_uart_console_write(struct console *co, const char *s, unsigned int count) > +{ > + struct uart_port *port = sbsa_uart_ports[co->index]; > + unsigned int status; > + unsigned long flags; > + int locked = 1; > + > + local_irq_save(flags); > + if (port->sysrq) > + locked = 0; > + else if (oops_in_progress) > + locked = spin_trylock(&port->lock); > + else > + spin_lock(&port->lock); > + > + uart_console_write(port, s, count, sbsa_uart_console_putchar); > + > + /* Finally, wait for transmitter to become empty */ > + do { > + status = readw(port->membase + UART01x_FR); > + } while (status & UART01x_FR_BUSY); > + > + if (locked) > + spin_unlock(&port->lock); > + local_irq_restore(flags); > +} > + > +static void __init > +sbsa_uart_console_get_options(struct uart_port *port, int *baud, > + int *parity, int *bits) > +{ > + *parity = 'n'; > + *bits = 8; > + *baud = 115200; > +} > + > +static int __init sbsa_uart_console_setup(struct console *co, char *options) > +{ > + struct uart_port *port; > + int baud = 38400; > + int bits = 8; > + int parity = 'n'; > + int flow = 'n'; > + > + /* > + * Check whether an invalid uart number has been specified, and > + * if so, search for the first available port that does have > + * console support. > + */ > + if (co->index >= UART_NR) > + co->index = 0; > + port = sbsa_uart_ports[co->index]; > + if (!port) > + return -ENODEV; > + > + if (options) > + uart_parse_options(options, &baud, &parity, &bits, &flow); > + else > + sbsa_uart_console_get_options(port, &baud, &parity, &bits); > + > + return uart_set_options(port, co, baud, parity, bits, flow); > +} > + > +static struct uart_driver sbsa_uart_reg; > +static struct console sbsa_uart_console = { > + .name = "ttyAMA", > + .write = sbsa_uart_console_write, > + .device = uart_console_device, > + .setup = sbsa_uart_console_setup, > + .flags = CON_PRINTBUFFER, > + .index = -1, > + .data = &sbsa_uart_reg, > +}; > + > +#define SBSA_UART_CONSOLE (&sbsa_uart_console) > + > +static void sbsa_uart_putc(struct uart_port *port, int c) > +{ > + while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF) > + ; > + writew(c & 0xff, port->membase + UART01x_DR); > + while (readw(port->membase + UART01x_FR) & UART01x_FR_BUSY) > + ; > +} > + > +static void sbsa_uart_early_write(struct console *con, const char *s, > + unsigned n) > +{ > + struct earlycon_device *dev = con->data; > + > + uart_console_write(&dev->port, s, n, sbsa_uart_putc); > +} > + > +static int __init sbsa_uart_early_console_setup(struct earlycon_device *device, > + const char *opt) > +{ > + if (!device->port.membase) > + return -ENODEV; > + > + device->con->write = sbsa_uart_early_write; > + return 0; > +} > +EARLYCON_DECLARE(pl011, sbsa_uart_early_console_setup); > +OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", sbsa_uart_early_console_setup); > + > +#else > +#define SBSA_UART_CONSOLE NULL > +#endif > + > +static struct uart_driver sbsa_uart_reg = { > + .owner = THIS_MODULE, > + .driver_name = "sbsa_uart", > + .dev_name = "ttyAMA", > + .nr = UART_NR, > + .cons = SBSA_UART_CONSOLE, > +}; > + > +#ifdef CONFIG_OF > + > +static int dt_probe_serial_alias(int index, struct device *dev) > +{ > + struct device_node *np; > + static bool seen_dev_with_alias; > + static bool seen_dev_without_alias; > + int ret = index; > + > + if (!IS_ENABLED(CONFIG_OF)) > + return ret; > + > + np = dev->of_node; > + if (!np) > + return ret; > + > + ret = of_alias_get_id(np, "serial"); > + if (IS_ERR_VALUE(ret)) { > + seen_dev_without_alias = true; > + ret = index; > + } else { > + seen_dev_with_alias = true; > + if (ret >= ARRAY_SIZE(sbsa_uart_ports) || > + sbsa_uart_ports[ret] != NULL) { > + dev_warn(dev, "requested serial port %d not available.\n", ret); > + ret = index; > + } > + } > + > + if (seen_dev_with_alias && seen_dev_without_alias) > + dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n"); > + > + return ret; > +} > + > +static const struct of_device_id arm_sbsa_match[] = { > + { .compatible = "arm,sbsa-uart", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, arm_sbsa_match); > + > +#else > + > +static int dt_probe_serial_alias(int index, struct device *dev) > +{ > + static int portnr; > + > + return portnr++; > +} > + > +#endif > + > +static int sbsa_uart_probe(struct platform_device *pdev) > +{ > + struct resource *r; > + struct uart_port *port; > + int i, ret; > + > + pr_info("DT probing for PL011-based SBSA UART\n"); > + for (i = 0; i < ARRAY_SIZE(sbsa_uart_ports); i++) > + if (sbsa_uart_ports[i] == NULL) > + break; > + > + if (i == ARRAY_SIZE(sbsa_uart_ports)) { > + ret = -EBUSY; > + goto out; > + } > + > + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + port = devm_kzalloc(&pdev->dev, sizeof(struct uart_port), GFP_KERNEL); > + if (port == NULL) { > + ret = -ENOMEM; > + goto out; > + } > + > + i = dt_probe_serial_alias(i, &pdev->dev); > + > + port->membase = devm_ioremap_resource(&pdev->dev, r); > + if (!port->membase) { > + ret = -ENOMEM; > + goto out; > + } > + > + port->irq = platform_get_irq(pdev, 0); > + > + dev_info(&pdev->dev, "found SBSA UART #%d at 0x%08llx\n", i, r->start); > + > + port->fifosize = 32; > + port->dev = &pdev->dev; > + port->mapbase = r->start; > + port->iotype = UPIO_MEM; > + port->ops = &sbsa_uart_pops; > + port->flags = UPF_BOOT_AUTOCONF; > + port->line = i; > + > + /* Ensure interrupts from this UART are masked and cleared */ > + writew(0, port->membase + UART011_IMSC); > + writew(0xffff, port->membase + UART011_ICR); > + > + sbsa_uart_ports[i] = port; > + > + platform_set_drvdata(pdev, port); > + > + if (!sbsa_uart_reg.state) { > + ret = uart_register_driver(&sbsa_uart_reg); > + if (ret < 0) { > + pr_err("Failed to register ARM SBSA UART driver\n"); > + return ret; > + } > + } > + > + ret = uart_add_one_port(&sbsa_uart_reg, port); > + if (ret) { > + sbsa_uart_ports[i] = NULL; > + uart_unregister_driver(&sbsa_uart_reg); > + } > + out: > + return ret; > +} > + > +static int sbsa_uart_remove(struct platform_device *pdev) > +{ > + struct uart_port *port = platform_get_drvdata(pdev); > + bool busy = false; > + int i; > + > + uart_remove_one_port(&sbsa_uart_reg, port); > + > + for (i = 0; i < ARRAY_SIZE(sbsa_uart_ports); i++) > + if (sbsa_uart_ports[i] == port) > + sbsa_uart_ports[i] = NULL; > + else if (sbsa_uart_ports[i]) > + busy = true; > + > + if (!busy) > + uart_unregister_driver(&sbsa_uart_reg); > + return 0; > +} > + > +static struct platform_driver arm_sbsa_uart_platform_driver = { > + .probe = sbsa_uart_probe, > + .remove = sbsa_uart_remove, > + .driver = { > + .name = "sbsa-uart", > + .of_match_table = of_match_ptr(arm_sbsa_match), > + }, > +}; > + > +module_platform_driver(arm_sbsa_uart_platform_driver); > + > +#ifdef CONFIG_PM_SLEEP > +static int sbsa_uart_suspend(struct device *dev) > +{ > + struct uart_port *port = dev_get_drvdata(dev); > + > + if (!port) > + return -EINVAL; > + > + return uart_suspend_port(&sbsa_uart_reg, port); > +} > + > +static int sbsa_uart_resume(struct device *dev) > +{ > + struct uart_port *port = dev_get_drvdata(dev); > + > + if (!port) > + return -EINVAL; > + > + return uart_resume_port(&sbsa_uart_reg, port); > +} > + > +static SIMPLE_DEV_PM_OPS(sbsa_uart_dev_pm_ops, sbsa_uart_suspend, > + sbsa_uart_resume); > + > +#endif > + > +static int __init sbsa_uart_init(void) > +{ > + pr_info("Serial: ARM SBSA generic UART (PL011) driver\n"); > + > + return 0; > +} > + > +static void __exit sbsa_uart_exit(void) > +{ > +} > + > +/* > + * While this can be a module, if builtin it's most likely the console > + * So let's leave module_exit but move module_init to an earlier place > + */ > +arch_initcall(sbsa_uart_init); > +module_exit(sbsa_uart_exit); > + > +MODULE_AUTHOR("ARM Ltd"); > +MODULE_DESCRIPTION("ARM SBSA generic UART serial port driver"); > +MODULE_LICENSE("GPL"); > diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h > index 5820269..9f9ee0c 100644 > --- a/include/uapi/linux/serial_core.h > +++ b/include/uapi/linux/serial_core.h > @@ -67,6 +67,7 @@ > #define PORT_CLPS711X 33 > #define PORT_SA1100 34 > #define PORT_UART00 35 > +#define PORT_SBSA 36 > #define PORT_21285 37 > > /* Sparc type numbers. */ > Regards, Peter Hurley -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html