Merge 3.19-rc5 into tty-next
We want those tty fixes in that release in this branch as well.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/devicetree/bindings/serial/digicolor-usart.txt b/Documentation/devicetree/bindings/serial/digicolor-usart.txt
new file mode 100644
index 0000000..2d3ede6
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/digicolor-usart.txt
@@ -0,0 +1,27 @@
+Binding for Conexant Digicolor USART
+
+Note: this binding is only applicable for using the USART peripheral as
+UART. USART also support synchronous serial protocols like SPI and I2S. Use
+the binding that matches the wiring of your system.
+
+Required properties:
+- compatible : should be "cnxt,cx92755-usart".
+- reg: Should contain USART controller registers location and length.
+- interrupts: Should contain a single USART controller interrupt.
+- clocks: Must contain phandles to the USART clock
+ See ../clocks/clock-bindings.txt for details.
+
+Note: Each UART port should have an alias correctly numbered
+in "aliases" node.
+
+Example:
+ aliases {
+ serial0 = &uart0;
+ };
+
+ uart0: uart@f0000740 {
+ compatible = "cnxt,cx92755-usart";
+ reg = <0xf0000740 0x20>;
+ clocks = <&main_clk>;
+ interrupts = <44>;
+ };
diff --git a/Documentation/devicetree/bindings/serial/sirf-uart.txt b/Documentation/devicetree/bindings/serial/sirf-uart.txt
index 3acdd96..f0c3926 100644
--- a/Documentation/devicetree/bindings/serial/sirf-uart.txt
+++ b/Documentation/devicetree/bindings/serial/sirf-uart.txt
@@ -2,7 +2,7 @@
Required properties:
- compatible : Should be "sirf,prima2-uart", "sirf, prima2-usp-uart",
- "sirf,marco-uart" or "sirf,marco-bt-uart" which means
+ "sirf,atlas7-uart" or "sirf,atlas7-bt-uart" which means
uart located in BT module and used for BT.
- reg : Offset and length of the register set for the device
- interrupts : Should contain uart interrupt
@@ -37,7 +37,7 @@
for uart use in BT module,
uart6: uart@11000000 {
cell-index = <6>;
- compatible = "sirf,marco-bt-uart", "sirf,marco-uart";
+ compatible = "sirf,atlas7-bt-uart", "sirf,atlas7-uart";
reg = <0x11000000 0x1000>;
interrupts = <0 100 0>;
clocks = <&clks 138>, <&clks 140>, <&clks 141>;
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index b1df0ad..2c67a07d 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -35,6 +35,7 @@
chunghwa Chunghwa Picture Tubes Ltd.
cirrus Cirrus Logic, Inc.
cnm Chips&Media, Inc.
+cnxt Conexant Systems, Inc.
cortina Cortina Systems, Inc.
crystalfontz Crystalfontz America, Inc.
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index d9f85f9..b2d7600 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -931,7 +931,7 @@
struct serial_state *info = tty->driver_data;
unsigned long flags;
- if (serial_paranoia_check(info, tty->name, "rs_send_char"))
+ if (serial_paranoia_check(info, tty->name, "rs_send_xchar"))
return;
info->x_char = ch;
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index 383c4c7..c8dd8dc 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -1390,7 +1390,7 @@
tty->ldisc.chars_in_buffer(tty));
#endif
- if (rocket_paranoia_check(info, "rp_throttle"))
+ if (rocket_paranoia_check(info, "rp_unthrottle"))
return;
if (I_IXOFF(tty))
@@ -1458,7 +1458,7 @@
orig_jiffies = jiffies;
#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
- printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout,
+ printk(KERN_INFO "In %s(%d) (jiff=%lu)...\n", __func__, timeout,
jiffies);
printk(KERN_INFO "cps=%d...\n", info->cps);
#endif
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 11c6685..689169c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -329,6 +329,17 @@
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+/* tx_loadsz is set to 63-bytes instead of 64-bytes to implement
+workaround of errata A-008006 which states that tx_loadsz should be
+configured less than Maximum supported fifo bytes */
+ [PORT_16550A_FSL64] = {
+ .name = "16550A_FSL64",
+ .fifo_size = 64,
+ .tx_loadsz = 63,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+ UART_FCR7_64BYTE,
+ .flags = UART_CAP_FIFO,
+ },
};
/* Uart divisor latch read */
@@ -956,7 +967,17 @@
up->port.type = PORT_16650;
up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
} else {
- DEBUG_AUTOCONF("Motorola 8xxx DUART ");
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR7_64BYTE);
+ status1 = serial_in(up, UART_IIR) >> 5;
+ serial_out(up, UART_FCR, 0);
+ serial_out(up, UART_LCR, 0);
+
+ if (status1 == 7)
+ up->port.type = PORT_16550A_FSL64;
+ else
+ DEBUG_AUTOCONF("Motorola 8xxx DUART ");
}
serial_out(up, UART_EFR, 0);
return;
@@ -1924,7 +1945,7 @@
return ret;
}
-static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned char mcr = 0;
@@ -1944,6 +1965,14 @@
serial_port_out(port, UART_MCR, mcr);
}
+EXPORT_SYMBOL_GPL(serial8250_do_set_mctrl);
+
+static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ if (port->set_mctrl)
+ return port->set_mctrl(port, mctrl);
+ return serial8250_do_set_mctrl(port, mctrl);
+}
static void serial8250_break_ctl(struct uart_port *port, int break_state)
{
@@ -3605,6 +3634,8 @@
/* Possibly override set_termios call */
if (up->port.set_termios)
uart->port.set_termios = up->port.set_termios;
+ if (up->port.set_mctrl)
+ uart->port.set_mctrl = up->port.set_mctrl;
if (up->port.startup)
uart->port.startup = up->port.startup;
if (up->port.shutdown)
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index fcd7ac6..e508939 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -59,7 +59,6 @@
dma->rx_running = 0;
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
- dmaengine_terminate_all(dma->rxchan);
count = dma->rx_size - state.residue;
@@ -131,6 +130,7 @@
if (dma->rx_running) {
dmaengine_pause(dma->rxchan);
__dma_rx_complete(p);
+ dmaengine_terminate_all(dma->rxchan);
}
return -ETIMEDOUT;
default:
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 555de07..e601162 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -351,10 +351,20 @@
static int dw8250_probe_acpi(struct uart_8250_port *up,
struct dw8250_data *data)
{
+ 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;
+
+ if (!p->uartclk)
+ if (device_property_read_u32(p->dev, "clock-frequency",
+ &p->uartclk))
+ return -EINVAL;
+
p->iotype = UPIO_MEM32;
p->serial_in = dw8250_serial_in32;
p->serial_out = dw8250_serial_out32;
@@ -577,6 +587,7 @@
{ "INT3435", 0 },
{ "80860F0A", 0 },
{ "8086228A", 0 },
+ { "APMC0D08", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index 4858b8a..ce2a8ab 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -95,13 +95,16 @@
/* Save the IER and disable interrupts */
ier = serial8250_early_in(port, UART_IER);
- serial8250_early_out(port, UART_IER, 0);
+ if (ier)
+ serial8250_early_out(port, UART_IER, 0);
uart_console_write(port, s, count, serial_putc);
/* Wait for transmitter to become empty and restore the IER */
wait_for_xmitr(port);
- serial8250_early_out(port, UART_IER, ier);
+
+ if (ier)
+ serial8250_early_out(port, UART_IER, ier);
}
static unsigned int __init probe_baud(struct uart_port *port)
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 96b69bf..9191d05 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -106,6 +106,27 @@
return readl(up->port.membase + (reg << up->port.regshift));
}
+static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct uart_8250_port *up = up_to_u8250p(port);
+ struct omap8250_priv *priv = up->port.private_data;
+ u8 lcr;
+
+ serial8250_do_set_mctrl(port, mctrl);
+
+ /*
+ * Turn off autoRTS if RTS is lowered and restore autoRTS setting
+ * if RTS is raised
+ */
+ lcr = serial_in(up, UART_LCR);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ if (mctrl & TIOCM_RTS)
+ serial_out(up, UART_EFR, priv->efr);
+ else
+ serial_out(up, UART_EFR, priv->efr & ~UART_EFR_RTS);
+ serial_out(up, UART_LCR, lcr);
+}
+
/*
* Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
* The access to uart register after MDR1 Access
@@ -251,7 +272,10 @@
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_dl_write(up, priv->quot);
- serial_out(up, UART_EFR, priv->efr);
+ if (up->port.mctrl & TIOCM_RTS)
+ serial_out(up, UART_EFR, priv->efr);
+ else
+ serial_out(up, UART_EFR, priv->efr & ~UART_EFR_RTS);
/* Configure flow control */
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
@@ -400,9 +424,6 @@
if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
/* Enable AUTORTS and AUTOCTS */
priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
-
- /* Ensure MCR RTS is asserted */
- up->mcr |= UART_MCR_RTS;
} else if (up->port.flags & UPF_SOFT_FLOW) {
/*
* IXON Flag:
@@ -450,18 +471,18 @@
static void omap_8250_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
- struct omap8250_priv *priv = up->port.private_data;
+ struct uart_8250_port *up = up_to_u8250p(port);
+ u8 efr;
pm_runtime_get_sync(port->dev);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB);
+ efr = serial_in(up, UART_EFR);
+ serial_out(up, UART_EFR, efr | UART_EFR_ECB);
serial_out(up, UART_LCR, 0);
serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- serial_out(up, UART_EFR, priv->efr);
+ serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
pm_runtime_mark_last_busy(port->dev);
@@ -1007,6 +1028,7 @@
up.capabilities |= UART_CAP_RPM;
#endif
up.port.set_termios = omap_8250_set_termios;
+ up.port.set_mctrl = omap8250_set_mctrl;
up.port.pm = omap_8250_pm;
up.port.startup = omap_8250_startup;
up.port.shutdown = omap_8250_shutdown;
@@ -1248,6 +1270,46 @@
}
#endif
+#ifdef CONFIG_SERIAL_8250_OMAP_TTYO_FIXUP
+static int __init omap8250_console_fixup(void)
+{
+ char *omap_str;
+ char *options;
+ u8 idx;
+
+ if (strstr(boot_command_line, "console=ttyS"))
+ /* user set a ttyS based name for the console */
+ return 0;
+
+ omap_str = strstr(boot_command_line, "console=ttyO");
+ if (!omap_str)
+ /* user did not set ttyO based console, so we don't care */
+ return 0;
+
+ omap_str += 12;
+ if ('0' <= *omap_str && *omap_str <= '9')
+ idx = *omap_str - '0';
+ else
+ return 0;
+
+ omap_str++;
+ if (omap_str[0] == ',') {
+ omap_str++;
+ options = omap_str;
+ } else {
+ options = NULL;
+ }
+
+ add_preferred_console("ttyS", idx, options);
+ pr_err("WARNING: Your 'console=ttyO%d' has been replaced by 'ttyS%d'\n",
+ idx, idx);
+ pr_err("This ensures that you still see kernel messages. Please\n");
+ pr_err("update your kernel commandline.\n");
+ return 0;
+}
+console_initcall(omap8250_console_fixup);
+#endif
+
static const struct dev_pm_ops omap8250_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
@@ -1269,7 +1331,6 @@
.name = "omap8250",
.pm = &omap8250_dev_pm_ops,
.of_match_table = omap8250_dt_ids,
- .owner = THIS_MODULE,
},
.probe = omap8250_probe,
.remove = omap8250_remove,
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 0fcbcd2..6f7f2d7 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -308,6 +308,25 @@
This driver uses ttyS instead of ttyO.
+config SERIAL_8250_OMAP_TTYO_FIXUP
+ bool "Replace ttyO with ttyS"
+ depends on SERIAL_8250_OMAP=y && SERIAL_8250_CONSOLE
+ default y
+ help
+ This option replaces the "console=ttyO" argument with the matching
+ ttyS argument if the user did not specified it on the command line.
+ This ensures that the user can see the kernel output during boot
+ which he wouldn't see otherwise. The getty has still to be configured
+ for ttyS instead of ttyO regardless of this option.
+ This option is intended for people who "automatically" enable this
+ driver without knowing that this driver requires a different console=
+ argument. If you read this, please keep this option disabled and
+ instead update your kernel command line. If you prepare a kernel for a
+ distribution or other kind of larger user base then you probably want
+ to keep this option enabled. Otherwise people might complain about a
+ not booting kernel because the serial console remains silent in case
+ they forgot to update the command line.
+
config SERIAL_8250_FINTEK
tristate "Support for Fintek F81216A LPC to 4 UART"
depends on SERIAL_8250 && PNP
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index c79b43c..96ec6cf 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1549,6 +1549,21 @@
If you have enabled the lpuart serial port on the Freescale SoCs,
you can make it the console by answering Y to this option.
+config SERIAL_CONEXANT_DIGICOLOR
+ tristate "Conexant Digicolor CX92xxx USART serial port support"
+ depends on OF
+ select SERIAL_CORE
+ help
+ Support for the on-chip USART on Conexant Digicolor SoCs.
+
+config SERIAL_CONEXANT_DIGICOLOR_CONSOLE
+ bool "Console on Conexant Digicolor serial port"
+ depends on SERIAL_CONEXANT_DIGICOLOR=y
+ select SERIAL_CORE_CONSOLE
+ help
+ If you have enabled the USART serial port on Conexant Digicolor
+ SoCs, you can make it the console by answering Y to this option.
+
config SERIAL_ST_ASC
tristate "ST ASC serial port support"
select SERIAL_CORE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 9a548ac..770a19b 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -92,6 +92,7 @@
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
+obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
# GPIOLIB helpers for modem control lines
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index 192d043..0fefdd8 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -441,6 +441,7 @@
port->iotype = SERIAL_IO_MEM;
port->ops = &altera_jtaguart_ops;
port->flags = UPF_BOOT_AUTOCONF;
+ port->dev = &pdev->dev;
uart_add_one_port(&altera_jtaguart_driver, port);
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index eb15a50..b2859fe 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -589,6 +589,7 @@
port->iotype = SERIAL_IO_MEM;
port->ops = &altera_uart_ops;
port->flags = UPF_BOOT_AUTOCONF;
+ port->dev = &pdev->dev;
platform_set_drvdata(pdev, port);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 4d848a2..846552b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -341,13 +341,37 @@
static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
{
unsigned int control = 0;
- unsigned int mode;
+ unsigned int mode = UART_GET_MR(port);
+ unsigned int rts_paused, rts_ready;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ /* override mode to RS485 if needed, otherwise keep the current mode */
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ if ((port->rs485.delay_rts_after_send) > 0)
+ UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
+ mode &= ~ATMEL_US_USMODE;
+ mode |= ATMEL_US_USMODE_RS485;
+ }
+
+ /* set the RTS line state according to the mode */
+ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+ /* force RTS line to high level */
+ rts_paused = ATMEL_US_RTSEN;
+
+ /* give the control of the RTS line back to the hardware */
+ rts_ready = ATMEL_US_RTSDIS;
+ } else {
+ /* force RTS line to high level */
+ rts_paused = ATMEL_US_RTSDIS;
+
+ /* force RTS line to low level */
+ rts_ready = ATMEL_US_RTSEN;
+ }
+
if (mctrl & TIOCM_RTS)
- control |= ATMEL_US_RTSEN;
+ control |= rts_ready;
else
- control |= ATMEL_US_RTSDIS;
+ control |= rts_paused;
if (mctrl & TIOCM_DTR)
control |= ATMEL_US_DTREN;
@@ -359,23 +383,12 @@
mctrl_gpio_set(atmel_port->gpios, mctrl);
/* Local loopback mode? */
- mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
+ mode &= ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
mode |= ATMEL_US_CHMODE_LOC_LOOP;
else
mode |= ATMEL_US_CHMODE_NORMAL;
- /* Resetting serial mode to RS232 (0x0) */
- mode &= ~ATMEL_US_USMODE;
-
- if (port->rs485.flags & SER_RS485_ENABLED) {
- dev_dbg(port->dev, "Setting UART to RS485\n");
- if ((port->rs485.delay_rts_after_send) > 0)
- UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
- mode |= ATMEL_US_USMODE_RS485;
- } else {
- dev_dbg(port->dev, "Setting UART to RS232\n");
- }
UART_PUT_MR(port, mode);
}
@@ -725,7 +738,11 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- /* Do we really need this? */
+ /*
+ * xmit is a circular buffer so, if we have just send data from
+ * xmit->tail to the end of xmit->buf, now we have to transmit the
+ * remaining data from the beginning of xmit->buf to xmit->head.
+ */
if (!uart_circ_empty(xmit))
tasklet_schedule(&atmel_port->tasklet);
@@ -784,17 +801,17 @@
BUG_ON(!sg_dma_len(sg));
desc = dmaengine_prep_slave_sg(chan,
- sg,
- 1,
- DMA_MEM_TO_DEV,
- DMA_PREP_INTERRUPT |
- DMA_CTRL_ACK);
+ sg,
+ 1,
+ DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT |
+ DMA_CTRL_ACK);
if (!desc) {
dev_err(port->dev, "Failed to send via dma!\n");
return;
}
- dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV);
+ dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
atmel_port->desc_tx = desc;
desc->callback = atmel_complete_tx_dma;
@@ -927,7 +944,7 @@
dma_sync_sg_for_cpu(port->dev,
&atmel_port->sg_rx,
1,
- DMA_DEV_TO_MEM);
+ DMA_FROM_DEVICE);
/*
* ring->head points to the end of data already written by the DMA.
@@ -974,7 +991,7 @@
dma_sync_sg_for_device(port->dev,
&atmel_port->sg_rx,
1,
- DMA_DEV_TO_MEM);
+ DMA_FROM_DEVICE);
/*
* Drop the lock here since it might end up calling
@@ -1012,13 +1029,13 @@
/* UART circular rx buffer is an aligned page. */
BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
sg_set_page(&atmel_port->sg_rx,
- virt_to_page(ring->buf),
- ATMEL_SERIAL_RINGSIZE,
- (int)ring->buf & ~PAGE_MASK);
- nent = dma_map_sg(port->dev,
- &atmel_port->sg_rx,
- 1,
- DMA_FROM_DEVICE);
+ virt_to_page(ring->buf),
+ ATMEL_SERIAL_RINGSIZE,
+ (int)ring->buf & ~PAGE_MASK);
+ nent = dma_map_sg(port->dev,
+ &atmel_port->sg_rx,
+ 1,
+ DMA_FROM_DEVICE);
if (!nent) {
dev_dbg(port->dev, "need to release resource of dma\n");
@@ -1047,11 +1064,11 @@
* each one is half ring buffer size
*/
desc = dmaengine_prep_dma_cyclic(atmel_port->chan_rx,
- sg_dma_address(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx),
- sg_dma_len(&atmel_port->sg_rx)/2,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
+ sg_dma_address(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx),
+ sg_dma_len(&atmel_port->sg_rx)/2,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
desc->callback = atmel_complete_rx_dma;
desc->callback_param = port;
atmel_port->desc_rx = desc;
@@ -1921,12 +1938,14 @@
struct ktermios *old)
{
unsigned long flags;
- unsigned int mode, imr, quot, baud;
+ unsigned int old_mode, mode, imr, quot, baud;
- /* Get current mode register */
- mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
- | ATMEL_US_NBSTOP | ATMEL_US_PAR
- | ATMEL_US_USMODE);
+ /* save the current mode register */
+ mode = old_mode = UART_GET_MR(port);
+
+ /* reset the mode, clock divisor, parity, stop bits and data size */
+ mode &= ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP |
+ ATMEL_US_PAR | ATMEL_US_USMODE);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
quot = uart_get_divisor(port, baud);
@@ -1971,12 +1990,6 @@
} else
mode |= ATMEL_US_PAR_NONE;
- /* hardware handshake (RTS/CTS) */
- if (termios->c_cflag & CRTSCTS)
- mode |= ATMEL_US_USMODE_HWHS;
- else
- mode |= ATMEL_US_USMODE_NORMAL;
-
spin_lock_irqsave(&port->lock, flags);
port->read_status_mask = ATMEL_US_OVRE;
@@ -2020,18 +2033,40 @@
/* disable receiver and transmitter */
UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
- /* Resetting serial mode to RS232 (0x0) */
- mode &= ~ATMEL_US_USMODE;
-
+ /* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
if ((port->rs485.delay_rts_after_send) > 0)
UART_PUT_TTGR(port, port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
+ } else if (termios->c_cflag & CRTSCTS) {
+ /* RS232 with hardware handshake (RTS/CTS) */
+ mode |= ATMEL_US_USMODE_HWHS;
+ } else {
+ /* RS232 without hadware handshake */
+ mode |= ATMEL_US_USMODE_NORMAL;
}
- /* set the parity, stop bits and data size */
+ /* set the mode, clock divisor, parity, stop bits and data size */
UART_PUT_MR(port, mode);
+ /*
+ * when switching the mode, set the RTS line state according to the
+ * new mode, otherwise keep the former state
+ */
+ if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
+ unsigned int rts_state;
+
+ if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
+ /* let the hardware control the RTS line */
+ rts_state = ATMEL_US_RTSDIS;
+ } else {
+ /* force RTS line to low level */
+ rts_state = ATMEL_US_RTSEN;
+ }
+
+ UART_PUT_CR(port, rts_state);
+ }
+
/* set the baud rate */
UART_PUT_BRGR(port, quot);
UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
@@ -2565,7 +2600,7 @@
ret = atmel_init_port(port, pdev);
if (ret)
- goto err;
+ goto err_clear_bit;
if (!atmel_use_pdc_rx(&port->uart)) {
ret = -ENOMEM;
@@ -2596,6 +2631,12 @@
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, port);
+ /*
+ * The peripheral clock has been disabled by atmel_init_port():
+ * enable it before accessing I/O registers
+ */
+ clk_prepare_enable(port->clk);
+
if (rs485_enabled) {
UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
@@ -2606,6 +2647,12 @@
*/
atmel_get_ip_name(&port->uart);
+ /*
+ * The peripheral clock can now safely be disabled till the port
+ * is used
+ */
+ clk_disable_unprepare(port->clk);
+
return 0;
err_add_port:
@@ -2616,6 +2663,8 @@
clk_put(port->clk);
port->clk = NULL;
}
+err_clear_bit:
+ clear_bit(port->uart.line, atmel_ports_in_use);
err:
return ret;
}
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
new file mode 100644
index 0000000..09ce0b3
--- /dev/null
+++ b/drivers/tty/serial/digicolor-usart.c
@@ -0,0 +1,563 @@
+/*
+ * Driver for Conexant Digicolor serial ports (USART)
+ *
+ * Author: Baruch Siach <baruch@tkos.co.il>
+ *
+ * Copyright (C) 2014 Paradox Innovation Ltd.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#define UA_ENABLE 0x00
+#define UA_ENABLE_ENABLE BIT(0)
+
+#define UA_CONTROL 0x01
+#define UA_CONTROL_RX_ENABLE BIT(0)
+#define UA_CONTROL_TX_ENABLE BIT(1)
+#define UA_CONTROL_SOFT_RESET BIT(2)
+
+#define UA_STATUS 0x02
+#define UA_STATUS_PARITY_ERR BIT(0)
+#define UA_STATUS_FRAME_ERR BIT(1)
+#define UA_STATUS_OVERRUN_ERR BIT(2)
+#define UA_STATUS_TX_READY BIT(6)
+
+#define UA_CONFIG 0x03
+#define UA_CONFIG_CHAR_LEN BIT(0)
+#define UA_CONFIG_STOP_BITS BIT(1)
+#define UA_CONFIG_PARITY BIT(2)
+#define UA_CONFIG_ODD_PARITY BIT(4)
+
+#define UA_EMI_REC 0x04
+
+#define UA_HBAUD_LO 0x08
+#define UA_HBAUD_HI 0x09
+
+#define UA_STATUS_FIFO 0x0a
+#define UA_STATUS_FIFO_RX_EMPTY BIT(2)
+#define UA_STATUS_FIFO_RX_INT_ALMOST BIT(3)
+#define UA_STATUS_FIFO_TX_FULL BIT(4)
+#define UA_STATUS_FIFO_TX_INT_ALMOST BIT(7)
+
+#define UA_CONFIG_FIFO 0x0b
+#define UA_CONFIG_FIFO_RX_THRESH 7
+#define UA_CONFIG_FIFO_RX_FIFO_MODE BIT(3)
+#define UA_CONFIG_FIFO_TX_FIFO_MODE BIT(7)
+
+#define UA_INTFLAG_CLEAR 0x1c
+#define UA_INTFLAG_SET 0x1d
+#define UA_INT_ENABLE 0x1e
+#define UA_INT_STATUS 0x1f
+
+#define UA_INT_TX BIT(0)
+#define UA_INT_RX BIT(1)
+
+#define DIGICOLOR_USART_NR 3
+
+/*
+ * We use the 16 bytes hardware FIFO to buffer Rx traffic. Rx interrupt is
+ * only produced when the FIFO is filled more than a certain configurable
+ * threshold. Unfortunately, there is no way to set this threshold below half
+ * FIFO. This means that we must periodically poll the FIFO status register to
+ * see whether there are waiting Rx bytes.
+ */
+
+struct digicolor_port {
+ struct uart_port port;
+ struct delayed_work rx_poll_work;
+};
+
+static struct uart_port *digicolor_ports[DIGICOLOR_USART_NR];
+
+static bool digicolor_uart_tx_full(struct uart_port *port)
+{
+ return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
+ UA_STATUS_FIFO_TX_FULL);
+}
+
+static bool digicolor_uart_rx_empty(struct uart_port *port)
+{
+ return !!(readb_relaxed(port->membase + UA_STATUS_FIFO) &
+ UA_STATUS_FIFO_RX_EMPTY);
+}
+
+static void digicolor_uart_stop_tx(struct uart_port *port)
+{
+ u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+ int_enable &= ~UA_INT_TX;
+ writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_uart_start_tx(struct uart_port *port)
+{
+ u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+ int_enable |= UA_INT_TX;
+ writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_uart_stop_rx(struct uart_port *port)
+{
+ u8 int_enable = readb_relaxed(port->membase + UA_INT_ENABLE);
+
+ int_enable &= ~UA_INT_RX;
+ writeb_relaxed(int_enable, port->membase + UA_INT_ENABLE);
+}
+
+static void digicolor_rx_poll(struct work_struct *work)
+{
+ struct digicolor_port *dp =
+ container_of(to_delayed_work(work),
+ struct digicolor_port, rx_poll_work);
+
+ if (!digicolor_uart_rx_empty(&dp->port))
+ /* force RX interrupt */
+ writeb_relaxed(UA_INT_RX, dp->port.membase + UA_INTFLAG_SET);
+
+ schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
+}
+
+static void digicolor_uart_rx(struct uart_port *port)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ while (1) {
+ u8 status, ch;
+ unsigned int ch_flag;
+
+ if (digicolor_uart_rx_empty(port))
+ break;
+
+ ch = readb_relaxed(port->membase + UA_EMI_REC);
+ status = readb_relaxed(port->membase + UA_STATUS);
+
+ port->icount.rx++;
+ ch_flag = TTY_NORMAL;
+
+ if (status) {
+ if (status & UA_STATUS_PARITY_ERR)
+ port->icount.parity++;
+ else if (status & UA_STATUS_FRAME_ERR)
+ port->icount.frame++;
+ else if (status & UA_STATUS_OVERRUN_ERR)
+ port->icount.overrun++;
+
+ status &= port->read_status_mask;
+
+ if (status & UA_STATUS_PARITY_ERR)
+ ch_flag = TTY_PARITY;
+ else if (status & UA_STATUS_FRAME_ERR)
+ ch_flag = TTY_FRAME;
+ else if (status & UA_STATUS_OVERRUN_ERR)
+ ch_flag = TTY_OVERRUN;
+ }
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (status & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, status, UA_STATUS_OVERRUN_ERR, ch,
+ ch_flag);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static void digicolor_uart_tx(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+
+ if (digicolor_uart_tx_full(port))
+ return;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (port->x_char) {
+ writeb_relaxed(port->x_char, port->membase + UA_EMI_REC);
+ port->icount.tx++;
+ port->x_char = 0;
+ goto out;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ digicolor_uart_stop_tx(port);
+ goto out;
+ }
+
+ while (!uart_circ_empty(xmit)) {
+ writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+
+ if (digicolor_uart_tx_full(port))
+ break;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+out:
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static irqreturn_t digicolor_uart_int(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ u8 int_status = readb_relaxed(port->membase + UA_INT_STATUS);
+
+ writeb_relaxed(UA_INT_RX | UA_INT_TX,
+ port->membase + UA_INTFLAG_CLEAR);
+
+ if (int_status & UA_INT_RX)
+ digicolor_uart_rx(port);
+ if (int_status & UA_INT_TX)
+ digicolor_uart_tx(port);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int digicolor_uart_tx_empty(struct uart_port *port)
+{
+ u8 status = readb_relaxed(port->membase + UA_STATUS);
+
+ return (status & UA_STATUS_TX_READY) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int digicolor_uart_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CTS;
+}
+
+static void digicolor_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void digicolor_uart_break_ctl(struct uart_port *port, int state)
+{
+}
+
+static int digicolor_uart_startup(struct uart_port *port)
+{
+ struct digicolor_port *dp =
+ container_of(port, struct digicolor_port, port);
+
+ writeb_relaxed(UA_ENABLE_ENABLE, port->membase + UA_ENABLE);
+ writeb_relaxed(UA_CONTROL_SOFT_RESET, port->membase + UA_CONTROL);
+ writeb_relaxed(0, port->membase + UA_CONTROL);
+
+ writeb_relaxed(UA_CONFIG_FIFO_RX_FIFO_MODE
+ | UA_CONFIG_FIFO_TX_FIFO_MODE | UA_CONFIG_FIFO_RX_THRESH,
+ port->membase + UA_CONFIG_FIFO);
+ writeb_relaxed(UA_STATUS_FIFO_RX_INT_ALMOST,
+ port->membase + UA_STATUS_FIFO);
+ writeb_relaxed(UA_CONTROL_RX_ENABLE | UA_CONTROL_TX_ENABLE,
+ port->membase + UA_CONTROL);
+ writeb_relaxed(UA_INT_TX | UA_INT_RX,
+ port->membase + UA_INT_ENABLE);
+
+ schedule_delayed_work(&dp->rx_poll_work, msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static void digicolor_uart_shutdown(struct uart_port *port)
+{
+ struct digicolor_port *dp =
+ container_of(port, struct digicolor_port, port);
+
+ writeb_relaxed(0, port->membase + UA_ENABLE);
+ cancel_delayed_work_sync(&dp->rx_poll_work);
+}
+
+static void digicolor_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned int baud, divisor;
+ u8 config = 0;
+ unsigned long flags;
+
+ /* Mask termios capabilities we don't support */
+ termios->c_cflag &= ~CMSPAR;
+ termios->c_iflag &= ~(BRKINT | IGNBRK);
+
+ /* Limit baud rates so that we don't need the fractional divider */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / (0x10000*16),
+ port->uartclk / 256);
+ divisor = uart_get_divisor(port, baud) - 1;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS7:
+ break;
+ case CS8:
+ default:
+ config |= UA_CONFIG_CHAR_LEN;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ config |= UA_CONFIG_STOP_BITS;
+
+ if (termios->c_cflag & PARENB) {
+ config |= UA_CONFIG_PARITY;
+ if (termios->c_cflag & PARODD)
+ config |= UA_CONFIG_ODD_PARITY;
+ }
+
+ /* Set read status mask */
+ port->read_status_mask = UA_STATUS_OVERRUN_ERR;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UA_STATUS_PARITY_ERR
+ | UA_STATUS_FRAME_ERR;
+
+ /* Set status ignore mask */
+ port->ignore_status_mask = 0;
+ if (!(termios->c_cflag & CREAD))
+ port->ignore_status_mask |= UA_STATUS_OVERRUN_ERR
+ | UA_STATUS_PARITY_ERR | UA_STATUS_FRAME_ERR;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ writeb_relaxed(config, port->membase + UA_CONFIG);
+ writeb_relaxed(divisor & 0xff, port->membase + UA_HBAUD_LO);
+ writeb_relaxed(divisor >> 8, port->membase + UA_HBAUD_HI);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *digicolor_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_DIGICOLOR) ? "DIGICOLOR USART" : NULL;
+}
+
+static void digicolor_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_DIGICOLOR;
+}
+
+static void digicolor_uart_release_port(struct uart_port *port)
+{
+}
+
+static int digicolor_uart_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static const struct uart_ops digicolor_uart_ops = {
+ .tx_empty = digicolor_uart_tx_empty,
+ .set_mctrl = digicolor_uart_set_mctrl,
+ .get_mctrl = digicolor_uart_get_mctrl,
+ .stop_tx = digicolor_uart_stop_tx,
+ .start_tx = digicolor_uart_start_tx,
+ .stop_rx = digicolor_uart_stop_rx,
+ .break_ctl = digicolor_uart_break_ctl,
+ .startup = digicolor_uart_startup,
+ .shutdown = digicolor_uart_shutdown,
+ .set_termios = digicolor_uart_set_termios,
+ .type = digicolor_uart_type,
+ .config_port = digicolor_uart_config_port,
+ .release_port = digicolor_uart_release_port,
+ .request_port = digicolor_uart_request_port,
+};
+
+static void digicolor_uart_console_putchar(struct uart_port *port, int ch)
+{
+ while (digicolor_uart_tx_full(port))
+ cpu_relax();
+
+ writeb_relaxed(ch, port->membase + UA_EMI_REC);
+}
+
+static void digicolor_uart_console_write(struct console *co, const char *c,
+ unsigned n)
+{
+ struct uart_port *port = digicolor_ports[co->index];
+ u8 status;
+ unsigned long flags;
+ int locked = 1;
+
+ if (port->sysrq || oops_in_progress)
+ locked = spin_trylock_irqsave(&port->lock, flags);
+ else
+ spin_lock_irqsave(&port->lock, flags);
+
+ uart_console_write(port, c, n, digicolor_uart_console_putchar);
+
+ if (locked)
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ /* Wait for transmitter to become empty */
+ do {
+ status = readb_relaxed(port->membase + UA_STATUS);
+ } while ((status & UA_STATUS_TX_READY) == 0);
+}
+
+static int digicolor_uart_console_setup(struct console *co, char *options)
+{
+ int baud = 115200, bits = 8, parity = 'n', flow = 'n';
+ struct uart_port *port;
+
+ if (co->index < 0 || co->index >= DIGICOLOR_USART_NR)
+ return -EINVAL;
+
+ port = digicolor_ports[co->index];
+ if (!port)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console digicolor_console = {
+ .name = "ttyS",
+ .device = uart_console_device,
+ .write = digicolor_uart_console_write,
+ .setup = digicolor_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static struct uart_driver digicolor_uart = {
+ .driver_name = "digicolor-usart",
+ .dev_name = "ttyS",
+ .nr = DIGICOLOR_USART_NR,
+};
+
+static int digicolor_uart_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret, index;
+ struct digicolor_port *dp;
+ struct resource *res;
+ struct clk *uart_clk;
+
+ if (!np) {
+ dev_err(&pdev->dev, "Missing device tree node\n");
+ return -ENXIO;
+ }
+
+ index = of_alias_get_id(np, "serial");
+ if (index < 0 || index >= DIGICOLOR_USART_NR)
+ return -EINVAL;
+
+ dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ uart_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(uart_clk))
+ return PTR_ERR(uart_clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dp->port.mapbase = res->start;
+ dp->port.membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dp->port.membase))
+ return PTR_ERR(dp->port.membase);
+
+ dp->port.irq = platform_get_irq(pdev, 0);
+ if (IS_ERR_VALUE(dp->port.irq))
+ return dp->port.irq;
+
+ dp->port.iotype = UPIO_MEM;
+ dp->port.uartclk = clk_get_rate(uart_clk);
+ dp->port.fifosize = 16;
+ dp->port.dev = &pdev->dev;
+ dp->port.ops = &digicolor_uart_ops;
+ dp->port.line = index;
+ dp->port.type = PORT_DIGICOLOR;
+ spin_lock_init(&dp->port.lock);
+
+ digicolor_ports[index] = &dp->port;
+ platform_set_drvdata(pdev, &dp->port);
+
+ INIT_DELAYED_WORK(&dp->rx_poll_work, digicolor_rx_poll);
+
+ ret = devm_request_irq(&pdev->dev, dp->port.irq, digicolor_uart_int, 0,
+ dev_name(&pdev->dev), &dp->port);
+ if (ret)
+ return ret;
+
+ return uart_add_one_port(&digicolor_uart, &dp->port);
+}
+
+static int digicolor_uart_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&digicolor_uart, port);
+
+ return 0;
+}
+
+static const struct of_device_id digicolor_uart_dt_ids[] = {
+ { .compatible = "cnxt,cx92755-usart", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, digicolor_uart_dt_ids);
+
+static struct platform_driver digicolor_uart_platform = {
+ .driver = {
+ .name = "digicolor-usart",
+ .of_match_table = of_match_ptr(digicolor_uart_dt_ids),
+ },
+ .probe = digicolor_uart_probe,
+ .remove = digicolor_uart_remove,
+};
+
+static int __init digicolor_uart_init(void)
+{
+ int ret;
+
+ if (IS_ENABLED(CONFIG_SERIAL_CONEXANT_DIGICOLOR_CONSOLE)) {
+ digicolor_uart.cons = &digicolor_console;
+ digicolor_console.data = &digicolor_uart;
+ }
+
+ ret = uart_register_driver(&digicolor_uart);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&digicolor_uart_platform);
+}
+module_init(digicolor_uart_init);
+
+static void __exit digicolor_uart_exit(void)
+{
+ platform_driver_unregister(&digicolor_uart_platform);
+ uart_unregister_driver(&digicolor_uart);
+}
+module_exit(digicolor_uart_exit);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Conexant Digicolor USART serial driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 4c5e909..59d9ef1 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -74,6 +74,7 @@
#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
/* UART Control Register Bit Fields.*/
+#define URXD_DUMMY_READ (1<<16)
#define URXD_CHARRDY (1<<15)
#define URXD_ERR (1<<14)
#define URXD_OVRRUN (1<<13)
@@ -463,13 +464,17 @@
mod_timer(&sport->timer, jiffies);
}
+static void imx_dma_tx(struct imx_port *sport);
static inline void imx_transmit_buffer(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
+ unsigned long temp;
if (sport->port.x_char) {
/* Send next char */
writel(sport->port.x_char, sport->port.membase + URTX0);
+ sport->port.icount.tx++;
+ sport->port.x_char = 0;
return;
}
@@ -478,6 +483,22 @@
return;
}
+ if (sport->dma_is_enabled) {
+ /*
+ * We've just sent a X-char Ensure the TX DMA is enabled
+ * and the TX IRQ is disabled.
+ **/
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~UCR1_TXMPTYEN;
+ if (sport->dma_is_txing) {
+ temp |= UCR1_TDMAEN;
+ writel(temp, sport->port.membase + UCR1);
+ } else {
+ writel(temp, sport->port.membase + UCR1);
+ imx_dma_tx(sport);
+ }
+ }
+
while (!uart_circ_empty(xmit) &&
!(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
@@ -500,26 +521,39 @@
struct scatterlist *sgl = &sport->tx_sgl[0];
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
+ unsigned long temp;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
- sport->dma_is_txing = 0;
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~UCR1_TDMAEN;
+ writel(temp, sport->port.membase + UCR1);
/* update the stat */
- spin_lock_irqsave(&sport->port.lock, flags);
xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx += sport->tx_bytes;
- spin_unlock_irqrestore(&sport->port.lock, flags);
dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
- uart_write_wakeup(&sport->port);
+ sport->dma_is_txing = 0;
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
if (waitqueue_active(&sport->dma_wait)) {
wake_up(&sport->dma_wait);
dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
return;
}
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+ imx_dma_tx(sport);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
static void imx_dma_tx(struct imx_port *sport)
@@ -529,24 +563,23 @@
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
struct device *dev = sport->port.dev;
- enum dma_status status;
+ unsigned long temp;
int ret;
- status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
- if (DMA_IN_PROGRESS == status)
+ if (sport->dma_is_txing)
return;
sport->tx_bytes = uart_circ_chars_pending(xmit);
- if (xmit->tail > xmit->head && xmit->head > 0) {
+ if (xmit->tail < xmit->head) {
+ sport->dma_tx_nents = 1;
+ sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+ } else {
sport->dma_tx_nents = 2;
sg_init_table(sgl, 2);
sg_set_buf(sgl, xmit->buf + xmit->tail,
UART_XMIT_SIZE - xmit->tail);
sg_set_buf(sgl + 1, xmit->buf, xmit->head);
- } else {
- sport->dma_tx_nents = 1;
- sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
}
ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
@@ -557,6 +590,8 @@
desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!desc) {
+ dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
+ DMA_TO_DEVICE);
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
return;
}
@@ -565,6 +600,11 @@
dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
uart_circ_chars_pending(xmit));
+
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_TDMAEN;
+ writel(temp, sport->port.membase + UCR1);
+
/* fire it */
sport->dma_is_txing = 1;
dmaengine_submit(desc);
@@ -590,13 +630,6 @@
temp &= ~(UCR1_RRDYEN);
writel(temp, sport->port.membase + UCR1);
}
- /* Clear any pending ORE flag before enabling interrupt */
- temp = readl(sport->port.membase + USR2);
- writel(temp | USR2_ORE, sport->port.membase + USR2);
-
- temp = readl(sport->port.membase + UCR4);
- temp |= UCR4_OREN;
- writel(temp, sport->port.membase + UCR4);
if (!sport->dma_is_enabled) {
temp = readl(sport->port.membase + UCR1);
@@ -614,15 +647,21 @@
}
if (sport->dma_is_enabled) {
- /* FIXME: port->x_char must be transmitted if != 0 */
+ if (sport->port.x_char) {
+ /* We have X-char to send, so enable TX IRQ and
+ * disable TX DMA to let TX interrupt to send X-char */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~UCR1_TDMAEN;
+ temp |= UCR1_TXMPTYEN;
+ writel(temp, sport->port.membase + UCR1);
+ return;
+ }
+
if (!uart_circ_empty(&port->state->xmit) &&
!uart_tx_stopped(port))
imx_dma_tx(sport);
return;
}
-
- if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
- imx_transmit_buffer(sport);
}
static irqreturn_t imx_rtsint(int irq, void *dev_id)
@@ -694,7 +733,7 @@
continue;
}
- rx &= sport->port.read_status_mask;
+ rx &= (sport->port.read_status_mask | 0xFF);
if (rx & URXD_BRK)
flg = TTY_BREAK;
@@ -710,6 +749,9 @@
#endif
}
+ if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
+ goto out;
+
tty_insert_flip_char(port, rx, flg);
}
@@ -727,6 +769,9 @@
static void imx_dma_rxint(struct imx_port *sport)
{
unsigned long temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + USR2);
if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
@@ -740,6 +785,8 @@
/* tell the DMA to receive the data. */
start_rx_dma(sport);
}
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
static irqreturn_t imx_int(int irq, void *dev_id)
@@ -869,6 +916,9 @@
static void imx_rx_dma_done(struct imx_port *sport)
{
unsigned long temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
/* Enable this interrupt when the RXFIFO is empty. */
temp = readl(sport->port.membase + UCR1);
@@ -880,6 +930,8 @@
/* Is the shutdown waiting for us? */
if (waitqueue_active(&sport->dma_wait))
wake_up(&sport->dma_wait);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
@@ -910,12 +962,26 @@
dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
if (count) {
- tty_insert_flip_string(port, sport->rx_buf, count);
+ if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ))
+ tty_insert_flip_string(port, sport->rx_buf, count);
tty_flip_buffer_push(port);
start_rx_dma(sport);
- } else
+ } else if (readl(sport->port.membase + USR2) & USR2_RDR) {
+ /*
+ * start rx_dma directly once data in RXFIFO, more efficient
+ * than before:
+ * 1. call imx_rx_dma_done to stop dma if no data received
+ * 2. wait next RDR interrupt to start dma transfer.
+ */
+ start_rx_dma(sport);
+ } else {
+ /*
+ * stop dma to prevent too many IDLE event trigged if no data
+ * in RXFIFO
+ */
imx_rx_dma_done(sport);
+ }
}
static int start_rx_dma(struct imx_port *sport)
@@ -935,6 +1001,7 @@
desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc) {
+ dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
return -EINVAL;
}
@@ -1108,12 +1175,20 @@
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
+ /* Can we enable the DMA support? */
+ if (is_imx6q_uart(sport) && !uart_console(port) &&
+ !sport->dma_is_inited)
+ imx_uart_dma_init(sport);
+
spin_lock_irqsave(&sport->port.lock, flags);
/*
* Finally, clear and enable interrupts
*/
writel(USR1_RTSD, sport->port.membase + USR1);
+ if (sport->dma_is_inited && !sport->dma_is_enabled)
+ imx_enable_dma(sport);
+
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
@@ -1124,6 +1199,14 @@
writel(temp, sport->port.membase + UCR1);
+ /* Clear any pending ORE flag before enabling interrupt */
+ temp = readl(sport->port.membase + USR2);
+ writel(temp | USR2_ORE, sport->port.membase + USR2);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_OREN;
+ writel(temp, sport->port.membase + UCR4);
+
temp = readl(sport->port.membase + UCR2);
temp |= (UCR2_RXEN | UCR2_TXEN);
if (!sport->have_rtscts)
@@ -1189,9 +1272,11 @@
dmaengine_terminate_all(sport->dma_chan_tx);
dmaengine_terminate_all(sport->dma_chan_rx);
}
+ spin_lock_irqsave(&sport->port.lock, flags);
imx_stop_tx(port);
imx_stop_rx(port);
imx_disable_dma(sport);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
}
@@ -1233,10 +1318,21 @@
static void imx_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
+ unsigned long temp;
- if (sport->dma_is_enabled) {
- sport->tx_bytes = 0;
- dmaengine_terminate_all(sport->dma_chan_tx);
+ if (!sport->dma_chan_tx)
+ return;
+
+ sport->tx_bytes = 0;
+ dmaengine_terminate_all(sport->dma_chan_tx);
+ if (sport->dma_is_txing) {
+ dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
+ DMA_TO_DEVICE);
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~UCR1_TDMAEN;
+ writel(temp, sport->port.membase + UCR1);
+ sport->dma_is_txing = false;
}
}
@@ -1280,11 +1376,6 @@
if (sport->have_rtscts) {
ucr2 &= ~UCR2_IRTS;
ucr2 |= UCR2_CTSC;
-
- /* Can we enable the DMA support? */
- if (is_imx6q_uart(sport) && !uart_console(port)
- && !sport->dma_is_inited)
- imx_uart_dma_init(sport);
} else {
termios->c_cflag &= ~CRTSCTS;
}
@@ -1319,7 +1410,7 @@
*/
sport->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
- sport->port.ignore_status_mask |= URXD_PRERR;
+ sport->port.ignore_status_mask |= URXD_PRERR | URXD_FRMERR;
if (termios->c_iflag & IGNBRK) {
sport->port.ignore_status_mask |= URXD_BRK;
/*
@@ -1330,6 +1421,9 @@
sport->port.ignore_status_mask |= URXD_OVRRUN;
}
+ if ((termios->c_cflag & CREAD) == 0)
+ sport->port.ignore_status_mask |= URXD_DUMMY_READ;
+
/*
* Update the per-port timeout.
*/
@@ -1403,8 +1497,6 @@
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_enable_ms(&sport->port);
- if (sport->dma_is_inited && !sport->dma_is_enabled)
- imx_enable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c
index 1049667..a9b0ab3 100644
--- a/drivers/tty/serial/mcf.c
+++ b/drivers/tty/serial/mcf.c
@@ -3,7 +3,7 @@
/*
* mcf.c -- Freescale ColdFire UART driver
*
- * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
+ * (C) Copyright 2003-2007, Greg Ungerer <gerg@uclinux.org>
*
* 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
@@ -198,7 +198,6 @@
static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
- struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned long flags;
unsigned int baud, baudclk;
#if defined(CONFIG_M5272)
@@ -441,7 +440,6 @@
/* Enable or disable the RS485 support */
static int mcf_config_rs485(struct uart_port *port, struct serial_rs485 *rs485)
{
- struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
unsigned char mr1, mr2;
/* Get mode registers */
@@ -631,6 +629,7 @@
port->mapbase = platp[i].mapbase;
port->membase = (platp[i].membase) ? platp[i].membase :
(unsigned char __iomem *) platp[i].mapbase;
+ port->dev = &pdev->dev;
port->iotype = SERIAL_IO_MEM;
port->irq = platp[i].irq;
port->uartclk = MCF_BUSCLK;
@@ -702,7 +701,7 @@
module_init(mcf_init);
module_exit(mcf_exit);
-MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
+MODULE_AUTHOR("Greg Ungerer <gerg@uclinux.org>");
MODULE_DESCRIPTION("Freescale ColdFire UART driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:mcfuart");
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index ec553f8..1e9fb37 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -1231,34 +1231,29 @@
int ret = 0;
struct resource *r;
- s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
- if (!s) {
- ret = -ENOMEM;
- goto out;
- }
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
ret = serial_mxs_probe_dt(s, pdev);
if (ret > 0)
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
- goto out_free;
+ return ret;
if (of_id) {
pdev->id_entry = of_id->data;
s->devtype = pdev->id_entry->driver_data;
}
- s->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(s->clk)) {
- ret = PTR_ERR(s->clk);
- goto out_free;
- }
+ s->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(s->clk))
+ return PTR_ERR(s->clk);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- ret = -ENXIO;
- goto out_free_clk;
- }
+ if (!r)
+ return -ENXIO;
+
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
@@ -1273,9 +1268,10 @@
s->irq = platform_get_irq(pdev, 0);
s->port.irq = s->irq;
- ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s);
+ ret = devm_request_irq(&pdev->dev, s->irq, mxs_auart_irq_handle, 0,
+ dev_name(&pdev->dev), s);
if (ret)
- goto out_free_clk;
+ return ret;
platform_set_drvdata(pdev, s);
@@ -1288,7 +1284,7 @@
*/
ret = mxs_auart_request_gpio_irq(s);
if (ret)
- goto out_free_irq;
+ return ret;
auart_port[s->port.line] = s;
@@ -1307,14 +1303,7 @@
out_free_gpio_irq:
mxs_auart_free_gpio_irq(s);
-out_free_irq:
auart_port[pdev->id] = NULL;
- free_irq(s->irq, s);
-out_free_clk:
- clk_put(s->clk);
-out_free:
- kfree(s);
-out:
return ret;
}
@@ -1323,13 +1312,8 @@
struct mxs_auart_port *s = platform_get_drvdata(pdev);
uart_remove_one_port(&auart_driver, &s->port);
-
auart_port[pdev->id] = NULL;
-
mxs_auart_free_gpio_irq(s);
- clk_put(s->clk);
- free_irq(s->irq, s);
- kfree(s);
return 0;
}
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 2e1073d..b1cf9a3 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -63,7 +63,7 @@
#define UART_ERRATA_i202_MDR1_ACCESS BIT(0)
#define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1)
-#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
+#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz */
/* SCR register bitmasks */
#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7)
@@ -93,7 +93,7 @@
/* WER = 0x7F
* Enable module level wakeup in WER reg
*/
-#define OMAP_UART_WER_MOD_WKUP 0X7F
+#define OMAP_UART_WER_MOD_WKUP 0x7F
/* Enable XON/XOFF flow control on output */
#define OMAP_UART_SW_TX 0x08
@@ -114,7 +114,7 @@
dma_addr_t tx_buf_dma_phys;
unsigned int uart_base;
/*
- * Buffer for rx dma.It is not required for tx because the buffer
+ * Buffer for rx dma. It is not required for tx because the buffer
* comes from port structure.
*/
unsigned char *rx_buf;
@@ -151,7 +151,7 @@
int use_dma;
/*
* Some bits in registers are cleared on a read, so they must
- * be saved whenever the register is read but the bits will not
+ * be saved whenever the register is read, but the bits will not
* be immediately processed.
*/
unsigned int lsr_break_flag;
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 107e807..024e5ec 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -28,6 +28,9 @@
#define SUPPORT_SYSRQ
#endif
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
@@ -78,6 +81,10 @@
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
+#define S3C24XX_TX_PIO 1
+#define S3C24XX_TX_DMA 2
+#define S3C24XX_RX_PIO 1
+#define S3C24XX_RX_DMA 2
/* macros to change one thing to another */
#define tx_enabled(port) ((port)->unused[0])
@@ -154,39 +161,279 @@
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_tx_state state;
+ int count;
- if (tx_enabled(port)) {
- if (s3c24xx_serial_has_interrupt_mask(port))
- __set_bit(S3C64XX_UINTM_TXD,
- portaddrl(port, S3C64XX_UINTM));
- else
- disable_irq_nosync(ourport->tx_irq);
- tx_enabled(port) = 0;
- if (port->flags & UPF_CONS_FLOW)
- s3c24xx_serial_rx_enable(port);
+ if (!tx_enabled(port))
+ return;
+
+ if (s3c24xx_serial_has_interrupt_mask(port))
+ __set_bit(S3C64XX_UINTM_TXD,
+ portaddrl(port, S3C64XX_UINTM));
+ else
+ disable_irq_nosync(ourport->tx_irq);
+
+ if (dma && dma->tx_chan && ourport->tx_in_progress == S3C24XX_TX_DMA) {
+ dmaengine_pause(dma->tx_chan);
+ dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
+ dmaengine_terminate_all(dma->tx_chan);
+ dma_sync_single_for_cpu(ourport->port.dev,
+ dma->tx_transfer_addr, dma->tx_size, DMA_TO_DEVICE);
+ async_tx_ack(dma->tx_desc);
+ count = dma->tx_bytes_requested - state.residue;
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += count;
}
+
+ tx_enabled(port) = 0;
+ ourport->tx_in_progress = 0;
+
+ if (port->flags & UPF_CONS_FLOW)
+ s3c24xx_serial_rx_enable(port);
+
+ ourport->tx_mode = 0;
}
-static void s3c24xx_serial_start_tx(struct uart_port *port)
+static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport);
+
+static void s3c24xx_serial_tx_dma_complete(void *args)
+{
+ struct s3c24xx_uart_port *ourport = args;
+ struct uart_port *port = &ourport->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ struct dma_tx_state state;
+ unsigned long flags;
+ int count;
+
+
+ dmaengine_tx_status(dma->tx_chan, dma->tx_cookie, &state);
+ count = dma->tx_bytes_requested - state.residue;
+ async_tx_ack(dma->tx_desc);
+
+ dma_sync_single_for_cpu(ourport->port.dev, dma->tx_transfer_addr,
+ dma->tx_size, DMA_TO_DEVICE);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += count;
+ ourport->tx_in_progress = 0;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ s3c24xx_serial_start_next_tx(ourport);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void enable_tx_dma(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ u32 ucon;
+
+ /* Mask Tx interrupt */
+ if (s3c24xx_serial_has_interrupt_mask(port))
+ __set_bit(S3C64XX_UINTM_TXD,
+ portaddrl(port, S3C64XX_UINTM));
+ else
+ disable_irq_nosync(ourport->tx_irq);
+
+ /* Enable tx dma mode */
+ ucon = rd_regl(port, S3C2410_UCON);
+ ucon &= ~(S3C64XX_UCON_TXBURST_MASK | S3C64XX_UCON_TXMODE_MASK);
+ ucon |= (dma_get_cache_alignment() >= 16) ?
+ S3C64XX_UCON_TXBURST_16 : S3C64XX_UCON_TXBURST_1;
+ ucon |= S3C64XX_UCON_TXMODE_DMA;
+ wr_regl(port, S3C2410_UCON, ucon);
+
+ ourport->tx_mode = S3C24XX_TX_DMA;
+}
+
+static void enable_tx_pio(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ u32 ucon, ufcon;
+
+ /* Set ufcon txtrig */
+ ourport->tx_in_progress = S3C24XX_TX_PIO;
+ ufcon = rd_regl(port, S3C2410_UFCON);
+ wr_regl(port, S3C2410_UFCON, ufcon);
+
+ /* Enable tx pio mode */
+ ucon = rd_regl(port, S3C2410_UCON);
+ ucon &= ~(S3C64XX_UCON_TXMODE_MASK);
+ ucon |= S3C64XX_UCON_TXMODE_CPU;
+ wr_regl(port, S3C2410_UCON, ucon);
+
+ /* Unmask Tx interrupt */
+ if (s3c24xx_serial_has_interrupt_mask(port))
+ __clear_bit(S3C64XX_UINTM_TXD,
+ portaddrl(port, S3C64XX_UINTM));
+ else
+ enable_irq(ourport->tx_irq);
+
+ ourport->tx_mode = S3C24XX_TX_PIO;
+}
+
+static void s3c24xx_serial_start_tx_pio(struct s3c24xx_uart_port *ourport)
+{
+ if (ourport->tx_mode != S3C24XX_TX_PIO)
+ enable_tx_pio(ourport);
+}
+
+static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport,
+ unsigned int count)
+{
+ struct uart_port *port = &ourport->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+
+
+ if (ourport->tx_mode != S3C24XX_TX_DMA)
+ enable_tx_dma(ourport);
+
+ while (xmit->tail & (dma_get_cache_alignment() - 1)) {
+ if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
+ return 0;
+ wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ count--;
+ }
+
+ dma->tx_size = count & ~(dma_get_cache_alignment() - 1);
+ dma->tx_transfer_addr = dma->tx_addr + xmit->tail;
+
+ dma_sync_single_for_device(ourport->port.dev, dma->tx_transfer_addr,
+ dma->tx_size, DMA_TO_DEVICE);
+
+ dma->tx_desc = dmaengine_prep_slave_single(dma->tx_chan,
+ dma->tx_transfer_addr, dma->tx_size,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+ if (!dma->tx_desc) {
+ dev_err(ourport->port.dev, "Unable to get desc for Tx\n");
+ return -EIO;
+ }
+
+ dma->tx_desc->callback = s3c24xx_serial_tx_dma_complete;
+ dma->tx_desc->callback_param = ourport;
+ dma->tx_bytes_requested = dma->tx_size;
+
+ ourport->tx_in_progress = S3C24XX_TX_DMA;
+ dma->tx_cookie = dmaengine_submit(dma->tx_desc);
+ dma_async_issue_pending(dma->tx_chan);
+ return 0;
+}
+
+static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long count;
+
+ /* Get data size up to the end of buffer */
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ if (!count) {
+ s3c24xx_serial_stop_tx(port);
+ return;
+ }
+
+ if (!ourport->dma || !ourport->dma->tx_chan || count < port->fifosize)
+ s3c24xx_serial_start_tx_pio(ourport);
+ else
+ s3c24xx_serial_start_tx_dma(ourport, count);
+}
+
+void s3c24xx_serial_start_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ struct circ_buf *xmit = &port->state->xmit;
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
- if (s3c24xx_serial_has_interrupt_mask(port))
- __clear_bit(S3C64XX_UINTM_TXD,
- portaddrl(port, S3C64XX_UINTM));
- else
- enable_irq(ourport->tx_irq);
tx_enabled(port) = 1;
+ if (!ourport->dma || !ourport->dma->tx_chan) {
+ if (s3c24xx_serial_has_interrupt_mask(port))
+ __clear_bit(S3C64XX_UINTM_TXD,
+ portaddrl(port, S3C64XX_UINTM));
+ else
+ enable_irq(ourport->tx_irq);
+
+ s3c24xx_serial_start_tx_pio(ourport);
+ }
}
+
+ if (ourport->dma && ourport->dma->tx_chan) {
+ if (!uart_circ_empty(xmit) && !ourport->tx_in_progress)
+ s3c24xx_serial_start_next_tx(ourport);
+ }
+}
+
+static void s3c24xx_uart_copy_rx_to_tty(struct s3c24xx_uart_port *ourport,
+ struct tty_port *tty, int count)
+{
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ int copied;
+
+ if (!count)
+ return;
+
+ dma_sync_single_for_cpu(ourport->port.dev, dma->rx_addr,
+ dma->rx_size, DMA_FROM_DEVICE);
+
+ ourport->port.icount.rx += count;
+ if (!tty) {
+ dev_err(ourport->port.dev, "No tty port\n");
+ return;
+ }
+ copied = tty_insert_flip_string(tty,
+ ((unsigned char *)(ourport->dma->rx_buf)), count);
+ if (copied != count) {
+ WARN_ON(1);
+ dev_err(ourport->port.dev, "RxData copy to tty layer failed\n");
+ }
+}
+
+static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
+ unsigned long ufstat);
+
+static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ struct tty_port *tty = &port->state->port;
+ unsigned int ch, ufstat;
+ unsigned int count;
+
+ ufstat = rd_regl(port, S3C2410_UFSTAT);
+ count = s3c24xx_serial_rx_fifocnt(ourport, ufstat);
+
+ if (!count)
+ return;
+
+ while (count-- > 0) {
+ ch = rd_regb(port, S3C2410_URXH);
+
+ ourport->port.icount.rx++;
+ tty_insert_flip_char(tty, ch, TTY_NORMAL);
+ }
+
+ tty_flip_buffer_push(tty);
}
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ struct tty_port *t = &port->state->port;
+ struct dma_tx_state state;
+ enum dma_status dma_status;
+ unsigned int received;
if (rx_enabled(port)) {
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
@@ -197,6 +444,17 @@
disable_irq_nosync(ourport->rx_irq);
rx_enabled(port) = 0;
}
+ if (dma && dma->rx_chan) {
+ dmaengine_pause(dma->tx_chan);
+ dma_status = dmaengine_tx_status(dma->rx_chan,
+ dma->rx_cookie, &state);
+ if (dma_status == DMA_IN_PROGRESS ||
+ dma_status == DMA_PAUSED) {
+ received = dma->rx_bytes_requested - state.residue;
+ dmaengine_terminate_all(dma->rx_chan);
+ s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+ }
+ }
}
static inline struct s3c24xx_uart_info
@@ -228,12 +486,157 @@
return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
}
+static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport);
+static void s3c24xx_serial_rx_dma_complete(void *args)
+{
+ struct s3c24xx_uart_port *ourport = args;
+ struct uart_port *port = &ourport->port;
+
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ struct tty_port *t = &port->state->port;
+ struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
+
+ struct dma_tx_state state;
+ unsigned long flags;
+ int received;
+
+ dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state);
+ received = dma->rx_bytes_requested - state.residue;
+ async_tx_ack(dma->rx_desc);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (received)
+ s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+
+ if (tty) {
+ tty_flip_buffer_push(t);
+ tty_kref_put(tty);
+ }
+
+ s3c64xx_start_rx_dma(ourport);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport)
+{
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+
+ dma_sync_single_for_device(ourport->port.dev, dma->rx_addr,
+ dma->rx_size, DMA_FROM_DEVICE);
+
+ dma->rx_desc = dmaengine_prep_slave_single(dma->rx_chan,
+ dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!dma->rx_desc) {
+ dev_err(ourport->port.dev, "Unable to get desc for Rx\n");
+ return;
+ }
+
+ dma->rx_desc->callback = s3c24xx_serial_rx_dma_complete;
+ dma->rx_desc->callback_param = ourport;
+ dma->rx_bytes_requested = dma->rx_size;
+
+ dma->rx_cookie = dmaengine_submit(dma->rx_desc);
+ dma_async_issue_pending(dma->rx_chan);
+}
/* ? - where has parity gone?? */
#define S3C2410_UERSTAT_PARITY (0x1000)
-static irqreturn_t
-s3c24xx_serial_rx_chars(int irq, void *dev_id)
+static void enable_rx_dma(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ unsigned int ucon;
+
+ /* set Rx mode to DMA mode */
+ ucon = rd_regl(port, S3C2410_UCON);
+ ucon &= ~(S3C64XX_UCON_RXBURST_MASK |
+ S3C64XX_UCON_TIMEOUT_MASK |
+ S3C64XX_UCON_EMPTYINT_EN |
+ S3C64XX_UCON_DMASUS_EN |
+ S3C64XX_UCON_TIMEOUT_EN |
+ S3C64XX_UCON_RXMODE_MASK);
+ ucon |= S3C64XX_UCON_RXBURST_16 |
+ 0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
+ S3C64XX_UCON_EMPTYINT_EN |
+ S3C64XX_UCON_TIMEOUT_EN |
+ S3C64XX_UCON_RXMODE_DMA;
+ wr_regl(port, S3C2410_UCON, ucon);
+
+ ourport->rx_mode = S3C24XX_RX_DMA;
+}
+
+static void enable_rx_pio(struct s3c24xx_uart_port *ourport)
+{
+ struct uart_port *port = &ourport->port;
+ unsigned int ucon;
+
+ /* set Rx mode to DMA mode */
+ ucon = rd_regl(port, S3C2410_UCON);
+ ucon &= ~(S3C64XX_UCON_TIMEOUT_MASK |
+ S3C64XX_UCON_EMPTYINT_EN |
+ S3C64XX_UCON_DMASUS_EN |
+ S3C64XX_UCON_TIMEOUT_EN |
+ S3C64XX_UCON_RXMODE_MASK);
+ ucon |= 0xf << S3C64XX_UCON_TIMEOUT_SHIFT |
+ S3C64XX_UCON_TIMEOUT_EN |
+ S3C64XX_UCON_RXMODE_CPU;
+ wr_regl(port, S3C2410_UCON, ucon);
+
+ ourport->rx_mode = S3C24XX_RX_PIO;
+}
+
+static irqreturn_t s3c24xx_serial_rx_chars_dma(int irq, void *dev_id)
+{
+ unsigned int utrstat, ufstat, received;
+ struct s3c24xx_uart_port *ourport = dev_id;
+ struct uart_port *port = &ourport->port;
+ struct s3c24xx_uart_dma *dma = ourport->dma;
+ struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port);
+ struct tty_port *t = &port->state->port;
+ unsigned long flags;
+ struct dma_tx_state state;
+
+ utrstat = rd_regl(port, S3C2410_UTRSTAT);
+ ufstat = rd_regl(port, S3C2410_UFSTAT);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (!(utrstat & S3C2410_UTRSTAT_TIMEOUT)) {
+ s3c64xx_start_rx_dma(ourport);
+ if (ourport->rx_mode == S3C24XX_RX_PIO)
+ enable_rx_dma(ourport);
+ goto finish;
+ }
+
+ if (ourport->rx_mode == S3C24XX_RX_DMA) {
+ dmaengine_pause(dma->rx_chan);
+ dmaengine_tx_status(dma->rx_chan, dma->rx_cookie, &state);
+ dmaengine_terminate_all(dma->rx_chan);
+ received = dma->rx_bytes_requested - state.residue;
+ s3c24xx_uart_copy_rx_to_tty(ourport, t, received);
+
+ enable_rx_pio(ourport);
+ }
+
+ uart_rx_drain_fifo(ourport);
+
+ if (tty) {
+ tty_flip_buffer_push(t);
+ tty_kref_put(tty);
+ }
+
+ wr_regl(port, S3C2410_UTRSTAT, S3C2410_UTRSTAT_TIMEOUT);
+
+finish:
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c24xx_serial_rx_chars_pio(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
@@ -324,16 +727,33 @@
return IRQ_HANDLED;
}
+
+static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
+{
+ struct s3c24xx_uart_port *ourport = dev_id;
+
+ if (ourport->dma && ourport->dma->rx_chan)
+ return s3c24xx_serial_rx_chars_dma(irq, dev_id);
+ return s3c24xx_serial_rx_chars_pio(irq, dev_id);
+}
+
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
- int count = port->fifosize;
+ int count;
spin_lock_irqsave(&port->lock, flags);
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ if (ourport->dma && ourport->dma->tx_chan && count >= port->fifosize) {
+ s3c24xx_serial_start_tx_dma(ourport, count);
+ goto out;
+ }
+
if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
@@ -352,6 +772,7 @@
/* try and drain the buffer... */
+ count = port->fifosize;
while (!uart_circ_empty(xmit) && count-- > 0) {
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
@@ -453,6 +874,93 @@
spin_unlock_irqrestore(&port->lock, flags);
}
+static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p)
+{
+ struct s3c24xx_uart_dma *dma = p->dma;
+ dma_cap_mask_t mask;
+ unsigned long flags;
+
+ /* Default slave configuration parameters */
+ dma->rx_conf.direction = DMA_DEV_TO_MEM;
+ dma->rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->rx_conf.src_addr = p->port.mapbase + S3C2410_URXH;
+ dma->rx_conf.src_maxburst = 16;
+
+ dma->tx_conf.direction = DMA_MEM_TO_DEV;
+ dma->tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ dma->tx_conf.dst_addr = p->port.mapbase + S3C2410_UTXH;
+ if (dma_get_cache_alignment() >= 16)
+ dma->tx_conf.dst_maxburst = 16;
+ else
+ dma->tx_conf.dst_maxburst = 1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ dma->rx_chan = dma_request_slave_channel_compat(mask, dma->fn,
+ dma->rx_param, p->port.dev, "rx");
+ if (!dma->rx_chan)
+ return -ENODEV;
+
+ dmaengine_slave_config(dma->rx_chan, &dma->rx_conf);
+
+ dma->tx_chan = dma_request_slave_channel_compat(mask, dma->fn,
+ dma->tx_param, p->port.dev, "tx");
+ if (!dma->tx_chan) {
+ dma_release_channel(dma->rx_chan);
+ return -ENODEV;
+ }
+
+ dmaengine_slave_config(dma->tx_chan, &dma->tx_conf);
+
+ /* RX buffer */
+ dma->rx_size = PAGE_SIZE;
+
+ dma->rx_buf = kmalloc(dma->rx_size, GFP_KERNEL);
+
+ if (!dma->rx_buf) {
+ dma_release_channel(dma->rx_chan);
+ dma_release_channel(dma->tx_chan);
+ return -ENOMEM;
+ }
+
+ dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf,
+ dma->rx_size, DMA_FROM_DEVICE);
+
+ spin_lock_irqsave(&p->port.lock, flags);
+
+ /* TX buffer */
+ dma->tx_addr = dma_map_single(dma->tx_chan->device->dev,
+ p->port.state->xmit.buf,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ spin_unlock_irqrestore(&p->port.lock, flags);
+
+ return 0;
+}
+
+static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p)
+{
+ struct s3c24xx_uart_dma *dma = p->dma;
+
+ if (dma->rx_chan) {
+ dmaengine_terminate_all(dma->rx_chan);
+ dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr,
+ dma->rx_size, DMA_FROM_DEVICE);
+ kfree(dma->rx_buf);
+ dma_release_channel(dma->rx_chan);
+ dma->rx_chan = NULL;
+ }
+
+ if (dma->tx_chan) {
+ dmaengine_terminate_all(dma->tx_chan);
+ dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+ dma_release_channel(dma->tx_chan);
+ dma->tx_chan = NULL;
+ }
+}
+
static void s3c24xx_serial_shutdown(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
@@ -478,6 +986,11 @@
wr_regl(port, S3C64XX_UINTP, 0xf);
wr_regl(port, S3C64XX_UINTM, 0xf);
}
+
+ if (ourport->dma)
+ s3c24xx_serial_release_dma(ourport);
+
+ ourport->tx_in_progress = 0;
}
static int s3c24xx_serial_startup(struct uart_port *port)
@@ -529,12 +1042,21 @@
static int s3c64xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
+ unsigned long flags;
+ unsigned int ufcon;
int ret;
dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
port, (unsigned long long)port->mapbase, port->membase);
wr_regl(port, S3C64XX_UINTM, 0xf);
+ if (ourport->dma) {
+ ret = s3c24xx_serial_request_dma(ourport);
+ if (ret < 0) {
+ dev_warn(port->dev, "DMA request failed\n");
+ return ret;
+ }
+ }
ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
s3c24xx_serial_portname(port), ourport);
@@ -549,8 +1071,20 @@
tx_enabled(port) = 0;
ourport->tx_claimed = 1;
+ spin_lock_irqsave(&port->lock, flags);
+
+ ufcon = rd_regl(port, S3C2410_UFCON);
+ ufcon |= S3C2410_UFCON_RESETRX | S3C2410_UFCON_RESETTX |
+ S5PV210_UFCON_RXTRIG8;
+ wr_regl(port, S3C2410_UFCON, ufcon);
+
+ enable_rx_pio(ourport);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
/* Enable Rx Interrupt */
__clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
+
dbg("s3c64xx_serial_startup ok\n");
return ret;
}
@@ -1209,6 +1743,18 @@
ret = platform_get_irq(platdev, 1);
if (ret > 0)
ourport->tx_irq = ret;
+ /*
+ * DMA is currently supported only on DT platforms, if DMA properties
+ * are specified.
+ */
+ if (platdev->dev.of_node && of_find_property(platdev->dev.of_node,
+ "dmas", NULL)) {
+ ourport->dma = devm_kzalloc(port->dev,
+ sizeof(*ourport->dma),
+ GFP_KERNEL);
+ if (!ourport->dma)
+ return -ENOMEM;
+ }
ourport->clk = clk_get(&platdev->dev, "uart");
if (IS_ERR(ourport->clk)) {
diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h
index eb071dd..d275032 100644
--- a/drivers/tty/serial/samsung.h
+++ b/drivers/tty/serial/samsung.h
@@ -12,6 +12,8 @@
* published by the Free Software Foundation.
*/
+#include <linux/dmaengine.h>
+
struct s3c24xx_uart_info {
char *name;
unsigned int type;
@@ -41,6 +43,40 @@
unsigned int fifosize[CONFIG_SERIAL_SAMSUNG_UARTS];
};
+struct s3c24xx_uart_dma {
+ dma_filter_fn fn;
+ void *rx_param;
+ void *tx_param;
+
+ unsigned int rx_chan_id;
+ unsigned int tx_chan_id;
+
+ struct dma_slave_config rx_conf;
+ struct dma_slave_config tx_conf;
+
+ struct dma_chan *rx_chan;
+ struct dma_chan *tx_chan;
+
+ dma_addr_t rx_addr;
+ dma_addr_t tx_addr;
+
+ dma_cookie_t rx_cookie;
+ dma_cookie_t tx_cookie;
+
+ char *rx_buf;
+
+ dma_addr_t tx_transfer_addr;
+
+ size_t rx_size;
+ size_t tx_size;
+
+ struct dma_async_tx_descriptor *tx_desc;
+ struct dma_async_tx_descriptor *rx_desc;
+
+ int tx_bytes_requested;
+ int rx_bytes_requested;
+};
+
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
@@ -50,6 +86,10 @@
unsigned int rx_irq;
unsigned int tx_irq;
+ unsigned int tx_in_progress;
+ unsigned int tx_mode;
+ unsigned int rx_mode;
+
struct s3c24xx_uart_info *info;
struct clk *clk;
struct clk *baudclk;
@@ -59,6 +99,8 @@
/* reference to platform data */
struct s3c2410_uartcfg *cfg;
+ struct s3c24xx_uart_dma *dma;
+
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index b269f6b..27ed0e9 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -177,7 +177,7 @@
dmaengine_pause(sirfport->tx_dma_chan);
sirfport->tx_dma_state = TX_DMA_PAUSE;
} else {
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~uint_en->sirfsoc_txfifo_empty_en);
@@ -186,7 +186,7 @@
uint_en->sirfsoc_txfifo_empty_en);
}
} else {
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~uint_en->sirfsoc_txfifo_empty_en);
@@ -217,7 +217,7 @@
}
if (sirfport->tx_dma_state == TX_DMA_RUNNING)
return;
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)&
~(uint_en->sirfsoc_txfifo_empty_en));
@@ -244,7 +244,7 @@
}
if (tran_size < 4)
sirfsoc_uart_pio_tx_chars(sirfport, tran_size);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)|
uint_en->sirfsoc_txfifo_empty_en);
@@ -293,7 +293,7 @@
sirfsoc_uart_pio_tx_chars(sirfport,
SIRFSOC_UART_IO_TX_REASONABLE_CNT);
wr_regl(port, ureg->sirfsoc_tx_fifo_op, SIRFUART_FIFO_START);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)|
uint_en->sirfsoc_txfifo_empty_en);
@@ -311,7 +311,7 @@
wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
if (sirfport->rx_dma_chan) {
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~(SIRFUART_RX_DMA_INT_EN(port, uint_en) |
@@ -322,7 +322,7 @@
uint_en->sirfsoc_rx_done_en);
dmaengine_terminate_all(sirfport->rx_dma_chan);
} else {
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)&
~(SIRFUART_RX_IO_INT_EN(port, uint_en)));
@@ -344,7 +344,7 @@
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
wr_regl(port, ureg->sirfsoc_afc_ctrl,
rd_regl(port, ureg->sirfsoc_afc_ctrl) & ~0x3FF);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)&
~uint_en->sirfsoc_cts_en);
@@ -380,7 +380,7 @@
wr_regl(port, ureg->sirfsoc_afc_ctrl,
rd_regl(port, ureg->sirfsoc_afc_ctrl) |
SIRFUART_AFC_TX_EN | SIRFUART_AFC_RX_EN);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg)
| uint_en->sirfsoc_cts_en);
@@ -544,7 +544,7 @@
sirfport->rx_io_count = 0;
wr_regl(port, ureg->sirfsoc_int_st_reg,
uint_st->sirfsoc_rx_done);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~(uint_en->sirfsoc_rx_done_en));
@@ -555,7 +555,7 @@
} else {
wr_regl(port, ureg->sirfsoc_int_st_reg,
uint_st->sirfsoc_rx_done);
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) |
(uint_en->sirfsoc_rx_done_en));
@@ -578,7 +578,7 @@
dmaengine_terminate_all(sirfport->rx_dma_chan);
sirfport->rx_dma_items[sirfport->rx_issued].xmit.head =
SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~(uint_en->sirfsoc_rx_timeout_en));
@@ -598,7 +598,7 @@
sirfsoc_uart_pio_rx_chars(port, 4 - sirfport->rx_io_count);
if (sirfport->rx_io_count == 4) {
sirfport->rx_io_count = 0;
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) &
~(uint_en->sirfsoc_rx_done_en));
@@ -748,7 +748,7 @@
for (i = 0; i < SIRFSOC_RX_LOOP_BUF_CNT; i++)
sirfsoc_rx_submit_one_dma_desc(port, i);
sirfport->rx_completed = sirfport->rx_issued = 0;
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) |
SIRFUART_RX_DMA_INT_EN(port, uint_en));
@@ -770,7 +770,7 @@
if (sirfport->rx_dma_chan)
sirfsoc_uart_start_next_rx_dma(port);
else {
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg,
rd_regl(port, ureg->sirfsoc_int_en_reg) |
SIRFUART_RX_IO_INT_EN(port, uint_en));
@@ -1124,7 +1124,7 @@
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
- if (!sirfport->is_marco)
+ if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
else
wr_regl(port, SIRFUART_INT_EN_CLR, ~0UL);
@@ -1271,7 +1271,7 @@
static struct of_device_id sirfsoc_uart_ids[] = {
{ .compatible = "sirf,prima2-uart", .data = &sirfsoc_uart,},
- { .compatible = "sirf,marco-uart", .data = &sirfsoc_uart},
+ { .compatible = "sirf,atlas7-uart", .data = &sirfsoc_uart},
{ .compatible = "sirf,prima2-usp-uart", .data = &sirfsoc_usp},
{}
};
@@ -1350,8 +1350,8 @@
gpio_direction_output(sirfport->rts_gpio, 1);
}
usp_no_flow_control:
- if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-uart"))
- sirfport->is_marco = true;
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-uart"))
+ sirfport->is_atlas7 = true;
if (of_property_read_u32(pdev->dev.of_node,
"fifosize",
@@ -1393,7 +1393,7 @@
goto err;
}
port->uartclk = clk_get_rate(sirfport->clk);
- if (of_device_is_compatible(pdev->dev.of_node, "sirf,marco-bt-uart")) {
+ if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas7-bt-uart")) {
sirfport->clk_general = devm_clk_get(&pdev->dev, "general");
if (IS_ERR(sirfport->clk_general)) {
ret = PTR_ERR(sirfport->clk_general);
diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h
index 275d038..727eb6b 100644
--- a/drivers/tty/serial/sirfsoc_uart.h
+++ b/drivers/tty/serial/sirfsoc_uart.h
@@ -421,8 +421,8 @@
bool is_bt_uart;
struct clk *clk_general;
struct clk *clk_noc;
- /* for SiRFmarco, there are SET/CLR for UART_INT_EN */
- bool is_marco;
+ /* for SiRFatlas7, there are SET/CLR for UART_INT_EN */
+ bool is_atlas7;
struct sirfsoc_uart_register *uart_reg;
struct dma_chan *rx_dma_chan;
struct dma_chan *tx_dma_chan;
diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c
index 4486741..a872389 100644
--- a/drivers/tty/tty_mutex.c
+++ b/drivers/tty/tty_mutex.c
@@ -46,12 +46,8 @@
void __lockfunc tty_lock_slave(struct tty_struct *tty)
{
- if (tty && tty != tty->link) {
- WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) ||
- !tty->driver->type == TTY_DRIVER_TYPE_PTY ||
- !tty->driver->type == PTY_TYPE_SLAVE);
+ if (tty && tty != tty->link)
tty_lock(tty);
- }
}
void __lockfunc tty_unlock_slave(struct tty_struct *tty)
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index f3fbbbc..5d36c23 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -3318,11 +3318,8 @@
if (first == 0 && last == MAX_NR_CONSOLES -1)
deflt = 1;
- if (first != -1) {
- console_lock();
+ if (first != -1)
do_bind_con_driver(csw, first, last, deflt);
- console_unlock();
- }
first = -1;
last = -1;
@@ -3362,9 +3359,7 @@
deflt = 1;
if (first != -1) {
- console_lock();
ret = do_unbind_con_driver(csw, first, last, deflt);
- console_unlock();
if (ret != 0)
return ret;
}
@@ -3394,11 +3389,15 @@
struct con_driver *con = dev_get_drvdata(dev);
int bind = simple_strtoul(buf, NULL, 0);
+ console_lock();
+
if (bind)
vt_bind(con);
else
vt_unbind(con);
+ console_unlock();
+
return count;
}
@@ -3665,8 +3664,7 @@
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
struct con_driver *con_driver = ®istered_con_driver[i];
- if (con_driver->con == csw &&
- con_driver->flag & CON_DRIVER_FLAG_INIT) {
+ if (con_driver->con == csw) {
vtconsole_deinit_device(con_driver);
device_destroy(vtconsole_class,
MKDEV(0, con_driver->node));
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index e02acf0..245b959 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -126,6 +126,7 @@
extern void serial8250_do_shutdown(struct uart_port *port);
extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate);
+extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
extern int fsl8250_handle_irq(struct uart_port *port);
int serial8250_handle_irq(struct uart_port *port, unsigned int iir);
unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 057038c..a0c7033 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -123,6 +123,7 @@
void (*set_termios)(struct uart_port *,
struct ktermios *new,
struct ktermios *old);
+ void (*set_mctrl)(struct uart_port *, unsigned int);
int (*startup)(struct uart_port *port);
void (*shutdown)(struct uart_port *port);
void (*throttle)(struct uart_port *port);
diff --git a/include/linux/serial_s3c.h b/include/linux/serial_s3c.h
index e6fc956..a7f004a 100644
--- a/include/linux/serial_s3c.h
+++ b/include/linux/serial_s3c.h
@@ -104,6 +104,31 @@
S3C2410_UCON_RXIRQMODE | \
S3C2410_UCON_RXFIFO_TOI)
+#define S3C64XX_UCON_TXBURST_1 (0<<20)
+#define S3C64XX_UCON_TXBURST_4 (1<<20)
+#define S3C64XX_UCON_TXBURST_8 (2<<20)
+#define S3C64XX_UCON_TXBURST_16 (3<<20)
+#define S3C64XX_UCON_TXBURST_MASK (0xf<<20)
+#define S3C64XX_UCON_RXBURST_1 (0<<16)
+#define S3C64XX_UCON_RXBURST_4 (1<<16)
+#define S3C64XX_UCON_RXBURST_8 (2<<16)
+#define S3C64XX_UCON_RXBURST_16 (3<<16)
+#define S3C64XX_UCON_RXBURST_MASK (0xf<<16)
+#define S3C64XX_UCON_TIMEOUT_SHIFT (12)
+#define S3C64XX_UCON_TIMEOUT_MASK (0xf<<12)
+#define S3C64XX_UCON_EMPTYINT_EN (1<<11)
+#define S3C64XX_UCON_DMASUS_EN (1<<10)
+#define S3C64XX_UCON_TXINT_LEVEL (1<<9)
+#define S3C64XX_UCON_RXINT_LEVEL (1<<8)
+#define S3C64XX_UCON_TIMEOUT_EN (1<<7)
+#define S3C64XX_UCON_ERRINT_EN (1<<6)
+#define S3C64XX_UCON_TXMODE_DMA (2<<2)
+#define S3C64XX_UCON_TXMODE_CPU (1<<2)
+#define S3C64XX_UCON_TXMODE_MASK (3<<2)
+#define S3C64XX_UCON_RXMODE_DMA (2<<0)
+#define S3C64XX_UCON_RXMODE_CPU (1<<0)
+#define S3C64XX_UCON_RXMODE_MASK (3<<0)
+
#define S3C2410_UFCON_FIFOMODE (1<<0)
#define S3C2410_UFCON_TXTRIG0 (0<<6)
#define S3C2410_UFCON_RXTRIG8 (1<<4)
@@ -155,6 +180,7 @@
#define S3C2440_UFSTAT_TXMASK (63<<8)
#define S3C2440_UFSTAT_RXMASK (63)
+#define S3C2410_UTRSTAT_TIMEOUT (1<<3)
#define S3C2410_UTRSTAT_TXE (1<<2)
#define S3C2410_UTRSTAT_TXFE (1<<1)
#define S3C2410_UTRSTAT_RXDR (1<<0)
@@ -179,8 +205,10 @@
#define S3C64XX_UINTM 0x38
#define S3C64XX_UINTM_RXD (0)
+#define S3C64XX_UINTM_ERROR (1)
#define S3C64XX_UINTM_TXD (2)
#define S3C64XX_UINTM_RXD_MSK (1 << S3C64XX_UINTM_RXD)
+#define S3C64XX_UINTM_ERR_MSK (1 << S3C64XX_UINTM_ERROR)
#define S3C64XX_UINTM_TXD_MSK (1 << S3C64XX_UINTM_TXD)
/* Following are specific to S5PV210 */
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index c172180..ee628c4 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -55,7 +55,8 @@
#define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */
#define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
#define PORT_RT2880 29 /* Ralink RT2880 internal UART */
-#define PORT_MAX_8250 29 /* max port ID */
+#define PORT_16550A_FSL64 30 /* Freescale 16550 UART with 64 FIFOs */
+#define PORT_MAX_8250 30 /* max port ID */
/*
* ARM specific type numbers. These are not currently guaranteed
@@ -248,4 +249,7 @@
/* MESON */
#define PORT_MESON 109
+/* Conexant Digicolor */
+#define PORT_DIGICOLOR 110
+
#endif /* _UAPILINUX_SERIAL_CORE_H */
diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h
index 53af3b7..00adb01 100644
--- a/include/uapi/linux/serial_reg.h
+++ b/include/uapi/linux/serial_reg.h
@@ -86,7 +86,8 @@
#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */
#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */
#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
-#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750) */
+#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode (TI16C750 and
+ some Freescale UARTs) */
#define UART_FCR_R_TRIG_SHIFT 6
#define UART_FCR_R_TRIG_BITS(x) \