diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 15:53:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 15:53:22 -0700 |
commit | 8c7febe83915332276cab49e89f6580bb963fb9a (patch) | |
tree | c6ffa5fbdef402f8c6e2d75b19dcce813fd21ded /drivers/tty/serial/amba-pl011.c | |
parent | 23908db413eccd77084b09c9b0a4451dfb0524c0 (diff) | |
parent | 71206b9f8120eb513c621d4f31906577bb658df3 (diff) |
Merge tag 'tty-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH:
"Here's the tty and serial driver patches for 4.2-rc1.
A number of individual driver updates, some code cleanups, and other
minor things, full details in the shortlog.
All have been in linux-next for a while with no reported issues"
* tag 'tty-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (152 commits)
Doc: serial-rs485.txt: update RS485 driver interface
Doc: tty.txt: remove mention of the BKL
MAINTAINERS: tty: add serial docs directory
serial: sprd: check for NULL after calling devm_clk_get
serial: 8250_pci: Correct uartclk for xr17v35x expansion chips
serial: 8250_pci: Add support for 12 port Exar boards
serial: 8250_uniphier: add bindings document for UniPhier UART
serial: core: cleanup in uart_get_baud_rate()
serial: stm32-usart: Add STM32 USART Driver
tty/serial: kill off set_irq_flags usage
tty: move linux/gsmmux.h to uapi
doc: dt: add documentation for nxp,lpc1850-uart
serial: 8250: add LPC18xx/43xx UART driver
serial: 8250_uniphier: add UniPhier serial driver
serial: 8250_dw: support ACPI platforms with integrated DMA engine
serial: of_serial: check the return value of clk_prepare_enable()
serial: of_serial: use devm_clk_get() instead of clk_get()
serial: earlycon: Add support for big-endian MMIO accesses
serial: sirf: use hrtimer for data rx
serial: sirf: correct the fifo empty_bit
...
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 651 |
1 files changed, 419 insertions, 232 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 763eb20fe321..50cf5b10ceed 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -58,7 +58,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/sizes.h> #include <linux/io.h> -#include <linux/workqueue.h> +#include <linux/acpi.h> #define UART_NR 14 @@ -79,6 +79,8 @@ struct vendor_data { bool oversampling; bool dma_threshold; bool cts_event_workaround; + bool always_enabled; + bool fixed_options; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -95,9 +97,19 @@ static struct vendor_data vendor_arm = { .oversampling = false, .dma_threshold = false, .cts_event_workaround = false, + .always_enabled = false, + .fixed_options = false, .get_fifosize = get_fifosize_arm, }; +static struct vendor_data vendor_sbsa = { + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = true, + .fixed_options = true, +}; + static unsigned int get_fifosize_st(struct amba_device *dev) { return 64; @@ -110,6 +122,8 @@ static struct vendor_data vendor_st = { .oversampling = true, .dma_threshold = true, .cts_event_workaround = true, + .always_enabled = false, + .fixed_options = false, .get_fifosize = get_fifosize_st, }; @@ -157,9 +171,8 @@ struct uart_amba_port { unsigned int lcrh_tx; /* vendor-specific */ unsigned int lcrh_rx; /* vendor-specific */ unsigned int old_cr; /* state during shutdown */ - struct delayed_work tx_softirq_work; bool autorts; - unsigned int tx_irq_seen; /* 0=none, 1=1, 2=2 or more */ + unsigned int fixed_baud; /* vendor-set fixed baud rate */ char type[12]; #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ @@ -1172,15 +1185,14 @@ static void pl011_stop_tx(struct uart_port *port) pl011_dma_tx_stop(uap); } -static bool pl011_tx_chars(struct uart_amba_port *uap); +static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); /* Start TX with programmed I/O only (no DMA) */ static void pl011_start_tx_pio(struct uart_amba_port *uap) { uap->im |= UART011_TXIM; writew(uap->im, uap->port.membase + UART011_IMSC); - if (!uap->tx_irq_seen) - pl011_tx_chars(uap); + pl011_tx_chars(uap, false); } static void pl011_start_tx(struct uart_port *port) @@ -1247,15 +1259,11 @@ __acquires(&uap->port.lock) spin_lock(&uap->port.lock); } -/* - * Transmit a character - * - * Returns true if the character was successfully queued to the FIFO. - * Returns false otherwise. - */ -static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) +static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, + bool from_irq) { - if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + if (unlikely(!from_irq) && + readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) return false; /* unable to transmit character */ writew(c, uap->port.membase + UART01x_DR); @@ -1264,70 +1272,41 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) return true; } -static bool pl011_tx_chars(struct uart_amba_port *uap) +static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) { struct circ_buf *xmit = &uap->port.state->xmit; - int count; - - if (unlikely(uap->tx_irq_seen < 2)) - /* - * Initial FIFO fill level unknown: we must check TXFF - * after each write, so just try to fill up the FIFO. - */ - count = uap->fifosize; - else /* tx_irq_seen >= 2 */ - /* - * FIFO initially at least half-empty, so we can simply - * write half the FIFO without polling TXFF. - - * Note: the *first* TX IRQ can still race with - * pl011_start_tx_pio(), which can result in the FIFO - * being fuller than expected in that case. - */ - count = uap->fifosize >> 1; - - /* - * If the FIFO is full we're guaranteed a TX IRQ at some later point, - * and can't transmit immediately in any case: - */ - if (unlikely(uap->tx_irq_seen < 2 && - readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)) - return false; + int count = uap->fifosize >> 1; if (uap->port.x_char) { - if (!pl011_tx_char(uap, uap->port.x_char)) - goto done; + if (!pl011_tx_char(uap, uap->port.x_char, from_irq)) + return; uap->port.x_char = 0; --count; } if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { pl011_stop_tx(&uap->port); - goto done; + return; } /* If we are using DMA mode, try to send some characters. */ if (pl011_dma_tx_irq(uap)) - goto done; + return; - while (count-- > 0 && pl011_tx_char(uap, xmit->buf[xmit->tail])) { - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - if (uart_circ_empty(xmit)) + do { + if (likely(from_irq) && count-- == 0) break; - } + + if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) + break; + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } while (!uart_circ_empty(xmit)); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uap->port); - if (uart_circ_empty(xmit)) { + if (uart_circ_empty(xmit)) pl011_stop_tx(&uap->port); - goto done; - } - - if (unlikely(!uap->tx_irq_seen)) - schedule_delayed_work(&uap->tx_softirq_work, uap->port.timeout); - -done: - return false; } static void pl011_modem_status(struct uart_amba_port *uap) @@ -1354,26 +1333,23 @@ static void pl011_modem_status(struct uart_amba_port *uap) wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } -static void pl011_tx_softirq(struct work_struct *work) +static void check_apply_cts_event_workaround(struct uart_amba_port *uap) { - struct delayed_work *dwork = to_delayed_work(work); - struct uart_amba_port *uap = - container_of(dwork, struct uart_amba_port, tx_softirq_work); - - spin_lock(&uap->port.lock); - while (pl011_tx_chars(uap)) ; - spin_unlock(&uap->port.lock); -} + unsigned int dummy_read; -static void pl011_tx_irq_seen(struct uart_amba_port *uap) -{ - if (likely(uap->tx_irq_seen > 1)) + if (!uap->vendor->cts_event_workaround) return; - uap->tx_irq_seen++; - if (uap->tx_irq_seen < 2) - /* first TX IRQ */ - cancel_delayed_work(&uap->tx_softirq_work); + /* workaround to make sure that all bits are unlocked.. */ + writew(0x00, uap->port.membase + UART011_ICR); + + /* + * WA: introduce 26ns(1 uart clk) delay before W1C; + * single apb access will incur 2 pclk(133.12Mhz) delay, + * so add 2 dummy reads + */ + dummy_read = readw(uap->port.membase + UART011_ICR); + dummy_read = readw(uap->port.membase + UART011_ICR); } static irqreturn_t pl011_int(int irq, void *dev_id) @@ -1381,25 +1357,15 @@ static irqreturn_t pl011_int(int irq, void *dev_id) struct uart_amba_port *uap = dev_id; unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + u16 imsc; int handled = 0; - unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); - status = readw(uap->port.membase + UART011_MIS); + imsc = readw(uap->port.membase + UART011_IMSC); + status = readw(uap->port.membase + UART011_RIS) & imsc; if (status) { do { - if (uap->vendor->cts_event_workaround) { - /* workaround to make sure that all bits are unlocked.. */ - writew(0x00, uap->port.membase + UART011_ICR); - - /* - * WA: introduce 26ns(1 uart clk) delay before W1C; - * single apb access will incur 2 pclk(133.12Mhz) delay, - * so add 2 dummy reads - */ - dummy_read = readw(uap->port.membase + UART011_ICR); - dummy_read = readw(uap->port.membase + UART011_ICR); - } + check_apply_cts_event_workaround(uap); writew(status & ~(UART011_TXIS|UART011_RTIS| UART011_RXIS), @@ -1414,15 +1380,13 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & (UART011_DSRMIS|UART011_DCDMIS| UART011_CTSMIS|UART011_RIMIS)) pl011_modem_status(uap); - if (status & UART011_TXIS) { - pl011_tx_irq_seen(uap); - pl011_tx_chars(uap); - } + if (status & UART011_TXIS) + pl011_tx_chars(uap, true); if (pass_counter-- == 0) break; - status = readw(uap->port.membase + UART011_MIS); + status = readw(uap->port.membase + UART011_RIS) & imsc; } while (status != 0); handled = 1; } @@ -1617,6 +1581,32 @@ static void pl011_write_lcr_h(struct uart_amba_port *uap, unsigned int lcr_h) } } +static int pl011_allocate_irq(struct uart_amba_port *uap) +{ + writew(uap->im, uap->port.membase + UART011_IMSC); + + return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); +} + +/* + * Enable interrupts, only timeouts when using DMA + * if initial RX DMA job failed, start in interrupt mode + * as well. + */ +static void pl011_enable_interrupts(struct uart_amba_port *uap) +{ + spin_lock_irq(&uap->port.lock); + + /* Clear out any spuriously appearing RX interrupts */ + writew(UART011_RTIS | UART011_RXIS, + uap->port.membase + UART011_ICR); + uap->im = UART011_RTIM; + if (!pl011_dma_rx_running(uap)) + uap->im |= UART011_RXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + spin_unlock_irq(&uap->port.lock); +} + static int pl011_startup(struct uart_port *port) { struct uart_amba_port *uap = @@ -1628,20 +1618,12 @@ static int pl011_startup(struct uart_port *port) if (retval) goto clk_dis; - writew(uap->im, uap->port.membase + UART011_IMSC); - - /* - * Allocate the IRQ - */ - retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + retval = pl011_allocate_irq(uap); if (retval) goto clk_dis; writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); - /* Assume that TX IRQ doesn't work until we see one: */ - uap->tx_irq_seen = 0; - spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ @@ -1659,20 +1641,7 @@ static int pl011_startup(struct uart_port *port) /* Startup DMA */ pl011_dma_startup(uap); - /* - * Finally, enable interrupts, only timeouts when using DMA - * if initial RX DMA job failed, start in interrupt mode - * as well. - */ - spin_lock_irq(&uap->port.lock); - /* Clear out any spuriously appearing RX interrupts */ - writew(UART011_RTIS | UART011_RXIS, - uap->port.membase + UART011_ICR); - uap->im = UART011_RTIM; - if (!pl011_dma_rx_running(uap)) - uap->im |= UART011_RXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - spin_unlock_irq(&uap->port.lock); + pl011_enable_interrupts(uap); return 0; @@ -1681,6 +1650,28 @@ static int pl011_startup(struct uart_port *port) return retval; } +static int sbsa_uart_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + int retval; + + retval = pl011_hwinit(port); + if (retval) + return retval; + + retval = pl011_allocate_irq(uap); + if (retval) + return retval; + + /* The SBSA UART does not support any modem status lines. */ + uap->old_status = 0; + + pl011_enable_interrupts(uap); + + return 0; +} + static void pl011_shutdown_channel(struct uart_amba_port *uap, unsigned int lcrh) { @@ -1691,36 +1682,15 @@ static void pl011_shutdown_channel(struct uart_amba_port *uap, writew(val, uap->port.membase + lcrh); } -static void pl011_shutdown(struct uart_port *port) +/* + * disable the port. It should not disable RTS and DTR. + * Also RTS and DTR state should be preserved to restore + * it during startup(). + */ +static void pl011_disable_uart(struct uart_amba_port *uap) { - struct uart_amba_port *uap = - container_of(port, struct uart_amba_port, port); unsigned int cr; - cancel_delayed_work_sync(&uap->tx_softirq_work); - - /* - * disable all interrupts - */ - spin_lock_irq(&uap->port.lock); - uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); - spin_unlock_irq(&uap->port.lock); - - pl011_dma_shutdown(uap); - - /* - * Free the interrupt - */ - free_irq(uap->port.irq, uap); - - /* - * disable the port - * disable the port. It should not disable RTS and DTR. - * Also RTS and DTR state should be preserved to restore - * it during startup(). - */ uap->autorts = false; spin_lock_irq(&uap->port.lock); cr = readw(uap->port.membase + UART011_CR); @@ -1736,6 +1706,32 @@ static void pl011_shutdown(struct uart_port *port) pl011_shutdown_channel(uap, uap->lcrh_rx); if (uap->lcrh_rx != uap->lcrh_tx) pl011_shutdown_channel(uap, uap->lcrh_tx); +} + +static void pl011_disable_interrupts(struct uart_amba_port *uap) +{ + spin_lock_irq(&uap->port.lock); + + /* mask all interrupts and clear all pending ones */ + uap->im = 0; + writew(uap->im, uap->port.membase + UART011_IMSC); + writew(0xffff, uap->port.membase + UART011_ICR); + + spin_unlock_irq(&uap->port.lock); +} + +static void pl011_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + + pl011_disable_interrupts(uap); + + pl011_dma_shutdown(uap); + + free_irq(uap->port.irq, uap); + + pl011_disable_uart(uap); /* * Shut down the clock producer @@ -1756,6 +1752,51 @@ static void pl011_shutdown(struct uart_port *port) uap->port.ops->flush_buffer(port); } +static void sbsa_uart_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + + pl011_disable_interrupts(uap); + + free_irq(uap->port.irq, uap); + + if (uap->port.ops->flush_buffer) + uap->port.ops->flush_buffer(port); +} + +static void +pl011_setup_status_masks(struct uart_port *port, struct ktermios *termios) +{ + 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 & (IGNBRK | BRKINT | PARMRK)) + port->read_status_mask |= UART011_DR_BE; + + /* + * 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; +} + static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -1820,33 +1861,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, */ 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 & (IGNBRK | BRKINT | PARMRK)) - port->read_status_mask |= UART011_DR_BE; - - /* - * 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; + pl011_setup_status_masks(port, termios); if (UART_ENABLE_MS(port, termios->c_cflag)) pl011_enable_ms(port); @@ -1901,6 +1916,27 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&port->lock, flags); } +static void +sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_amba_port *uap = + container_of(port, struct uart_amba_port, port); + unsigned long flags; + + tty_termios_encode_baud_rate(termios, uap->fixed_baud, uap->fixed_baud); + + /* The SBSA UART only supports 8n1 without hardware flow control. */ + termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); + termios->c_cflag &= ~(CMSPAR | CRTSCTS); + termios->c_cflag |= CS8 | CLOCAL; + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, CS8, uap->fixed_baud); + pl011_setup_status_masks(port, termios); + spin_unlock_irqrestore(&port->lock, flags); +} + static const char *pl011_type(struct uart_port *port) { struct uart_amba_port *uap = @@ -1976,6 +2012,37 @@ static struct uart_ops amba_pl011_pops = { #endif }; +static void sbsa_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int sbsa_uart_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static const struct uart_ops sbsa_uart_pops = { + .tx_empty = pl011_tx_empty, + .set_mctrl = sbsa_uart_set_mctrl, + .get_mctrl = sbsa_uart_get_mctrl, + .stop_tx = pl011_stop_tx, + .start_tx = pl011_start_tx, + .stop_rx = pl011_stop_rx, + .startup = sbsa_uart_startup, + .shutdown = sbsa_uart_shutdown, + .set_termios = sbsa_uart_set_termios, + .type = pl011_type, + .release_port = pl011_release_port, + .request_port = pl011_request_port, + .config_port = pl011_config_port, + .verify_port = pl011_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = pl011_hwinit, + .poll_get_char = pl011_get_poll_char, + .poll_put_char = pl011_put_poll_char, +#endif +}; + static struct uart_amba_port *amba_ports[UART_NR]; #ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE @@ -1994,7 +2061,7 @@ static void pl011_console_write(struct console *co, const char *s, unsigned int count) { struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int status, old_cr, new_cr; + unsigned int status, old_cr = 0, new_cr; unsigned long flags; int locked = 1; @@ -2011,10 +2078,12 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) /* * First save the CR then disable the interrupts */ - old_cr = readw(uap->port.membase + UART011_CR); - new_cr = old_cr & ~UART011_CR_CTSEN; - new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(new_cr, uap->port.membase + UART011_CR); + if (!uap->vendor->always_enabled) { + old_cr = readw(uap->port.membase + UART011_CR); + new_cr = old_cr & ~UART011_CR_CTSEN; + new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; + writew(new_cr, uap->port.membase + UART011_CR); + } uart_console_write(&uap->port, s, count, pl011_console_putchar); @@ -2025,7 +2094,8 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) do { status = readw(uap->port.membase + UART01x_FR); } while (status & UART01x_FR_BUSY); - writew(old_cr, uap->port.membase + UART011_CR); + if (!uap->vendor->always_enabled) + writew(old_cr, uap->port.membase + UART011_CR); if (locked) spin_unlock(&uap->port.lock); @@ -2106,10 +2176,15 @@ static int __init pl011_console_setup(struct console *co, char *options) uap->port.uartclk = clk_get_rate(uap->clk); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - pl011_console_get_options(uap, &baud, &parity, &bits); + if (uap->vendor->fixed_options) { + baud = uap->fixed_baud; + } else { + if (options) + uart_parse_options(options, + &baud, &parity, &bits, &flow); + else + pl011_console_get_options(uap, &baud, &parity, &bits); + } return uart_set_options(&uap->port, co, baud, parity, bits, flow); } @@ -2201,97 +2276,126 @@ static int pl011_probe_dt_alias(int index, struct device *dev) return ret; } -static int pl011_probe(struct amba_device *dev, const struct amba_id *id) +/* unregisters the driver also if no more ports are left */ +static void pl011_unregister_port(struct uart_amba_port *uap) { - struct uart_amba_port *uap; - struct vendor_data *vendor = id->data; - void __iomem *base; - int i, ret; + int i; + bool busy = false; + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { + if (amba_ports[i] == uap) + amba_ports[i] = NULL; + else if (amba_ports[i]) + busy = true; + } + pl011_dma_remove(uap); + if (!busy) + uart_unregister_driver(&amba_reg); +} + +static int pl011_find_free_port(void) +{ + int i; for (i = 0; i < ARRAY_SIZE(amba_ports); i++) if (amba_ports[i] == NULL) - break; - - if (i == ARRAY_SIZE(amba_ports)) - return -EBUSY; + return i; - uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), - GFP_KERNEL); - if (uap == NULL) - return -ENOMEM; + return -EBUSY; +} - i = pl011_probe_dt_alias(i, &dev->dev); +static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, + struct resource *mmiobase, int index) +{ + void __iomem *base; - base = devm_ioremap(&dev->dev, dev->res.start, - resource_size(&dev->res)); + base = devm_ioremap_resource(dev, mmiobase); if (!base) return -ENOMEM; - uap->clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(uap->clk)) - return PTR_ERR(uap->clk); + index = pl011_probe_dt_alias(index, dev); - uap->vendor = vendor; - uap->lcrh_rx = vendor->lcrh_rx; - uap->lcrh_tx = vendor->lcrh_tx; uap->old_cr = 0; - uap->fifosize = vendor->get_fifosize(dev); - uap->port.dev = &dev->dev; - uap->port.mapbase = dev->res.start; + uap->port.dev = dev; + uap->port.mapbase = mmiobase->start; uap->port.membase = base; uap->port.iotype = UPIO_MEM; - uap->port.irq = dev->irq[0]; uap->port.fifosize = uap->fifosize; - uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; - uap->port.line = i; - INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq); + uap->port.line = index; - /* Ensure interrupts from this UART are masked and cleared */ - writew(0, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); + amba_ports[index] = uap; - snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); + return 0; +} - amba_ports[i] = uap; +static int pl011_register_port(struct uart_amba_port *uap) +{ + int ret; - amba_set_drvdata(dev, uap); + /* Ensure interrupts from this UART are masked and cleared */ + writew(0, uap->port.membase + UART011_IMSC); + writew(0xffff, uap->port.membase + UART011_ICR); if (!amba_reg.state) { ret = uart_register_driver(&amba_reg); if (ret < 0) { - dev_err(&dev->dev, + dev_err(uap->port.dev, "Failed to register AMBA-PL011 driver\n"); return ret; } } ret = uart_add_one_port(&amba_reg, &uap->port); - if (ret) { - amba_ports[i] = NULL; - uart_unregister_driver(&amba_reg); - } + if (ret) + pl011_unregister_port(uap); return ret; } +static int pl011_probe(struct amba_device *dev, const struct amba_id *id) +{ + struct uart_amba_port *uap; + struct vendor_data *vendor = id->data; + int portnr, ret; + + portnr = pl011_find_free_port(); + if (portnr < 0) + return portnr; + + uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); + if (!uap) + return -ENOMEM; + + uap->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(uap->clk)) + return PTR_ERR(uap->clk); + + uap->vendor = vendor; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; + uap->fifosize = vendor->get_fifosize(dev); + uap->port.irq = dev->irq[0]; + uap->port.ops = &amba_pl011_pops; + + snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); + + ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr); + if (ret) + return ret; + + amba_set_drvdata(dev, uap); + + return pl011_register_port(uap); +} + static int pl011_remove(struct amba_device *dev) { struct uart_amba_port *uap = amba_get_drvdata(dev); - bool busy = false; - int i; uart_remove_one_port(&amba_reg, &uap->port); - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == uap) - amba_ports[i] = NULL; - else if (amba_ports[i]) - busy = true; - - pl011_dma_remove(uap); - if (!busy) - uart_unregister_driver(&amba_reg); + pl011_unregister_port(uap); return 0; } @@ -2319,6 +2423,86 @@ static int pl011_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume); +static int sbsa_uart_probe(struct platform_device *pdev) +{ + struct uart_amba_port *uap; + struct resource *r; + int portnr, ret; + int baudrate; + + /* + * Check the mandatory baud rate parameter in the DT node early + * so that we can easily exit with the error. + */ + if (pdev->dev.of_node) { + struct device_node *np = pdev->dev.of_node; + + ret = of_property_read_u32(np, "current-speed", &baudrate); + if (ret) + return ret; + } else { + baudrate = 115200; + } + + portnr = pl011_find_free_port(); + if (portnr < 0) + return portnr; + + uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); + if (!uap) + return -ENOMEM; + + uap->vendor = &vendor_sbsa; + uap->fifosize = 32; + uap->port.irq = platform_get_irq(pdev, 0); + uap->port.ops = &sbsa_uart_pops; + uap->fixed_baud = baudrate; + + snprintf(uap->type, sizeof(uap->type), "SBSA"); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ret = pl011_setup_port(&pdev->dev, uap, r, portnr); + if (ret) + return ret; + + platform_set_drvdata(pdev, uap); + + return pl011_register_port(uap); +} + +static int sbsa_uart_remove(struct platform_device *pdev) +{ + struct uart_amba_port *uap = platform_get_drvdata(pdev); + + uart_remove_one_port(&amba_reg, &uap->port); + pl011_unregister_port(uap); + return 0; +} + +static const struct of_device_id sbsa_uart_of_match[] = { + { .compatible = "arm,sbsa-uart", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sbsa_uart_of_match); + +static const struct acpi_device_id sbsa_uart_acpi_match[] = { + { "ARMH0011", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, sbsa_uart_acpi_match); + +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(sbsa_uart_of_match), + .acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match), + }, +}; + static struct amba_id pl011_ids[] = { { .id = 0x00041011, @@ -2349,11 +2533,14 @@ static int __init pl011_init(void) { printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); + if (platform_driver_register(&arm_sbsa_uart_platform_driver)) + pr_warn("could not register SBSA UART platform driver\n"); return amba_driver_register(&pl011_driver); } static void __exit pl011_exit(void) { + platform_driver_unregister(&arm_sbsa_uart_platform_driver); amba_driver_unregister(&pl011_driver); } |