diff options
author | David Daney <david.daney@cavium.com> | 2013-06-19 20:37:27 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-07-01 15:10:54 +0200 |
commit | d5f1af7ece96cf52e0b110c72210ac15c2f65438 (patch) | |
tree | 9fa028dbb2ac19e7a65d553bcaa266c530d0fe73 /drivers | |
parent | 5219343f83a033fe5dfcbb0274b5a78e8b2d0fee (diff) | |
download | linux-d5f1af7ece96cf52e0b110c72210ac15c2f65438.tar.bz2 |
tty/8250_dw: Add support for OCTEON UARTS.
A few differences needed by OCTEON:
o These are DWC UARTS, but have USR at a different offset.
o Internal SoC buses require reading back from registers to maintain
write ordering.
o 8250 on OCTEON appears with 64-bit wide registers, so when using
readb/writeb in big endian mode we have to adjust the membase to hit
the proper part of the register.
o No UCV register, so we hard code some properties.
Because OCTEON doesn't have a UCV register, I change where
dw8250_setup_port(), which reads the UCV, is called by pushing it in
to the OF and ACPI probe functions, and move unchanged
dw8250_setup_port() earlier in the file.
Signed-off-by: David Daney <david.daney@cavium.com>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: linux-mips@linux-mips.org
Cc: Jamie Iles <jamie@jamieiles.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/5516/
Acked-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 108 |
1 files changed, 69 insertions, 39 deletions
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d07b6af3a937..76a8daadff47 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -29,6 +29,8 @@ #include <linux/clk.h> #include <linux/pm_runtime.h> +#include <asm/byteorder.h> + #include "8250.h" /* Offsets for the DesignWare specific registers */ @@ -57,6 +59,7 @@ struct dw8250_data { int last_lcr; int line; struct clk *clk; + u8 usr_reg; }; static void dw8250_serial_out(struct uart_port *p, int offset, int value) @@ -77,6 +80,13 @@ static unsigned int dw8250_serial_in(struct uart_port *p, int offset) return readb(p->membase + offset); } +/* Read Back (rb) version to ensure register access ording. */ +static void dw8250_serial_out_rb(struct uart_port *p, int offset, int value) +{ + dw8250_serial_out(p, offset, value); + dw8250_serial_in(p, UART_LCR); +} + static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -104,7 +114,7 @@ static int dw8250_handle_irq(struct uart_port *p) return 1; } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* Clear the USR and write the LCR again. */ - (void)p->serial_in(p, DW_UART_USR); + (void)p->serial_in(p, d->usr_reg); p->serial_out(p, UART_LCR, d->last_lcr); return 1; @@ -125,12 +135,60 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) pm_runtime_put_sync_suspend(port->dev); } -static int dw8250_probe_of(struct uart_port *p) +static void dw8250_setup_port(struct uart_8250_port *up) +{ + struct uart_port *p = &up->port; + u32 reg = readl(p->membase + DW_UART_UCV); + + /* + * If the Component Version Register returns zero, we know that + * ADDITIONAL_FEATURES are not enabled. No need to go any further. + */ + if (!reg) + return; + + dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + + reg = readl(p->membase + DW_UART_CPR); + if (!reg) + return; + + /* Select the type based on fifo */ + if (reg & DW_UART_CPR_FIFO_MODE) { + p->type = PORT_16550A; + p->flags |= UPF_FIXED_TYPE; + p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); + up->tx_loadsz = p->fifosize; + up->capabilities = UART_CAP_FIFO; + } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; +} + +static int dw8250_probe_of(struct uart_port *p, + struct dw8250_data *data) { struct device_node *np = p->dev->of_node; u32 val; - - if (!of_property_read_u32(np, "reg-io-width", &val)) { + bool has_ucv = true; + + if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { +#ifdef __BIG_ENDIAN + /* + * Low order bits of these 64-bit registers, when + * accessed as a byte, are 7 bytes further down in the + * address space in big endian mode. + */ + p->membase += 7; +#endif + p->serial_out = dw8250_serial_out_rb; + p->flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->usr_reg = 0x27; + has_ucv = false; + } else if (!of_property_read_u32(np, "reg-io-width", &val)) { switch (val) { case 1: break; @@ -144,6 +202,8 @@ static int dw8250_probe_of(struct uart_port *p) return -EINVAL; } } + if (has_ucv) + dw8250_setup_port(container_of(p, struct uart_8250_port, port)); if (!of_property_read_u32(np, "reg-shift", &val)) p->regshift = val; @@ -168,6 +228,8 @@ static int dw8250_probe_acpi(struct uart_8250_port *up) const struct acpi_device_id *id; struct uart_port *p = &up->port; + dw8250_setup_port(up); + id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); if (!id) return -ENODEV; @@ -196,38 +258,6 @@ static inline int dw8250_probe_acpi(struct uart_8250_port *up) } #endif /* CONFIG_ACPI */ -static void dw8250_setup_port(struct uart_8250_port *up) -{ - struct uart_port *p = &up->port; - u32 reg = readl(p->membase + DW_UART_UCV); - - /* - * If the Component Version Register returns zero, we know that - * ADDITIONAL_FEATURES are not enabled. No need to go any further. - */ - if (!reg) - return; - - dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", - (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - - reg = readl(p->membase + DW_UART_CPR); - if (!reg) - return; - - /* Select the type based on fifo */ - if (reg & DW_UART_CPR_FIFO_MODE) { - p->type = PORT_16550A; - p->flags |= UPF_FIXED_TYPE; - p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); - up->tx_loadsz = p->fifosize; - up->capabilities = UART_CAP_FIFO; - } - - if (reg & DW_UART_CPR_AFCE_MODE) - up->capabilities |= UART_CAP_AFE; -} - static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; @@ -259,6 +289,7 @@ static int dw8250_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->usr_reg = DW_UART_USR; data->clk = devm_clk_get(&pdev->dev, NULL); if (!IS_ERR(data->clk)) { clk_prepare_enable(data->clk); @@ -270,10 +301,8 @@ static int dw8250_probe(struct platform_device *pdev) uart.port.serial_out = dw8250_serial_out; uart.port.private_data = data; - dw8250_setup_port(&uart); - if (pdev->dev.of_node) { - err = dw8250_probe_of(&uart.port); + err = dw8250_probe_of(&uart.port, data); if (err) return err; } else if (ACPI_HANDLE(&pdev->dev)) { @@ -362,6 +391,7 @@ static const struct dev_pm_ops dw8250_pm_ops = { static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, + { .compatible = "cavium,octeon-3860-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); |