TTY/Serial driver patches for 4.21-rc1

Here is the large TTY/Serial driver set of patches for 4.21-rc1.
 
 A number of small serial driver changes along with some good tty core
 fixes for long-reported issues with locking.  There is also a new
 console font added to the tree, for high-res screens, so that should be
 helpful for many.
 
 The last patch in the series is a revert of an older one in the tree, it
 came late but it resolves a reported issue that linux-next was having
 for some people.
 
 Full details are in the shortlog, and all of these, with the exception
 of the revert, have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iGwEABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXCY+1w8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yl9PgCXckFuSe66uNMzdq2jsVgLKtMR+ACbBzrLQeMZ
 LU8UpaVqDJrrPxVURGY=
 =a/Xk
 -----END PGP SIGNATURE-----

Merge tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial driver updates from Greg KH:
 "Here is the large TTY/Serial driver set of patches for 4.21-rc1.

  A number of small serial driver changes along with some good tty core
  fixes for long-reported issues with locking. There is also a new
  console font added to the tree, for high-res screens, so that should
  be helpful for many.

  The last patch in the series is a revert of an older one in the tree,
  it came late but it resolves a reported issue that linux-next was
  having for some people.

  Full details are in the shortlog, and all of these, with the exception
  of the revert, have been in linux-next for a while with no reported
  issues"

* tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (85 commits)
  Revert "serial: 8250: Default SERIAL_OF_PLATFORM to SERIAL_8250"
  serial: sccnxp: Allow to use non-standard baud rates
  serial: sccnxp: Adds a delay between sequential read/write cycles
  tty: serial: qcom_geni_serial: Fix UART hang
  tty: serial: qcom_geni_serial: Fix wrap around of TX buffer
  serial: max310x: Fix tx_empty() callback
  dt-bindings: serial: sh-sci: Document r8a774c0 bindings
  dt-bindings: serial: sh-sci: Document r8a774a1 bindings
  Fonts: New Terminus large console font
  dt-bindings: serial: lpuart: add imx8qxp compatible string
  serial: uartps: Fix interrupt mask issue to handle the RX interrupts properly
  serial: uartps: Fix error path when alloc failed
  serial: uartps: Check if the device is a console
  serial: uartps: Add the device_init_wakeup
  tty: serial: samsung: Increase maximum baudrate
  tty: serial: samsung: Properly set flags in autoCTS mode
  tty: Use of_node_name_{eq,prefix} for node name comparisons
  tty/serial: do not free trasnmit buffer page under port lock
  serial: 8250: Rate limit serial port rx interrupts during input overruns
  dt-bindings: serial: 8250: Add rate limit for serial port input overruns
  ...
This commit is contained in:
Linus Torvalds 2018-12-28 20:33:54 -08:00
commit 117eda8f71
55 changed files with 3040 additions and 314 deletions

View File

@ -0,0 +1,33 @@
* Mediatek UART APDMA Controller
Required properties:
- compatible should contain:
* "mediatek,mt2712-uart-dma" for MT2712 compatible APDMA
* "mediatek,mt6577-uart-dma" for MT6577 and all of the above
- reg: The base address of the APDMA register bank.
- interrupts: A single interrupt specifier.
- clocks : Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: The APDMA clock for register accesses
Examples:
apdma: dma-controller@11000380 {
compatible = "mediatek,mt2712-uart-dma";
reg = <0 0x11000380 0 0x400>;
interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 65 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 66 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 68 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 69 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 70 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_AP_DMA>;
clock-names = "apdma";
#dma-cells = <1>;
};

View File

@ -51,6 +51,7 @@ Optional properties:
- tx-threshold: Specify the TX FIFO low water indication for parts with - tx-threshold: Specify the TX FIFO low water indication for parts with
programmable TX FIFO thresholds. programmable TX FIFO thresholds.
- resets : phandle + reset specifier pairs - resets : phandle + reset specifier pairs
- overrun-throttle-ms : how long to pause uart rx when input overrun is encountered.
Note: Note:
* fsl,ns16550: * fsl,ns16550:

View File

@ -8,6 +8,8 @@ Required properties:
on LS1021A SoC with 32-bit big-endian register organization on LS1021A SoC with 32-bit big-endian register organization
- "fsl,imx7ulp-lpuart" for lpuart compatible with the one integrated - "fsl,imx7ulp-lpuart" for lpuart compatible with the one integrated
on i.MX7ULP SoC with 32-bit little-endian register organization on i.MX7ULP SoC with 32-bit little-endian register organization
- "fsl,imx8qxp-lpuart" for lpuart compatible with the one integrated
on i.MX8QXP SoC with 32-bit little-endian register organization
- reg : Address and length of the register set for the device - reg : Address and length of the register set for the device
- interrupts : Should contain uart interrupt - interrupts : Should contain uart interrupt
- clocks : phandle + clock specifier pairs, one for each entry in clock-names - clocks : phandle + clock specifier pairs, one for each entry in clock-names

View File

@ -6,8 +6,23 @@ Required properties:
- interrupts: the 3 (tx rx err) interrupt numbers. The interrupt specifier - interrupts: the 3 (tx rx err) interrupt numbers. The interrupt specifier
depends on the interrupt-parent interrupt controller. depends on the interrupt-parent interrupt controller.
Optional properties:
- clocks: Should contain frequency clock and gate clock
- clock-names: Should be "freq" and "asc"
Example: Example:
asc0: serial@16600000 {
compatible = "lantiq,asc";
reg = <0x16600000 0x100000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SHARED 103 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 105 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SHARED 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cgu CLK_SSX4>, <&cgu GCLK_UART>;
clock-names = "freq", "asc";
};
asc1: serial@e100c00 { asc1: serial@e100c00 {
compatible = "lantiq,asc"; compatible = "lantiq,asc";
reg = <0xE100C00 0x400>; reg = <0xE100C00 0x400>;

View File

@ -24,6 +24,10 @@ Required properties:
- "renesas,hscif-r8a7745" for R8A7745 (RZ/G1E) HSCIF compatible UART. - "renesas,hscif-r8a7745" for R8A7745 (RZ/G1E) HSCIF compatible UART.
- "renesas,scif-r8a77470" for R8A77470 (RZ/G1C) SCIF compatible UART. - "renesas,scif-r8a77470" for R8A77470 (RZ/G1C) SCIF compatible UART.
- "renesas,hscif-r8a77470" for R8A77470 (RZ/G1C) HSCIF compatible UART. - "renesas,hscif-r8a77470" for R8A77470 (RZ/G1C) HSCIF compatible UART.
- "renesas,scif-r8a774a1" for R8A774A1 (RZ/G2M) SCIF compatible UART.
- "renesas,hscif-r8a774a1" for R8A774A1 (RZ/G2M) HSCIF compatible UART.
- "renesas,scif-r8a774c0" for R8A774C0 (RZ/G2E) SCIF compatible UART.
- "renesas,hscif-r8a774c0" for R8A774C0 (RZ/G2E) HSCIF compatible UART.
- "renesas,scif-r8a7778" for R8A7778 (R-Car M1) SCIF compatible UART. - "renesas,scif-r8a7778" for R8A7778 (R-Car M1) SCIF compatible UART.
- "renesas,scif-r8a7779" for R8A7779 (R-Car H1) SCIF compatible UART. - "renesas,scif-r8a7779" for R8A7779 (R-Car H1) SCIF compatible UART.
- "renesas,scif-r8a7790" for R8A7790 (R-Car H2) SCIF compatible UART. - "renesas,scif-r8a7790" for R8A7790 (R-Car H2) SCIF compatible UART.
@ -61,13 +65,13 @@ Required properties:
- "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART.
- "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART. - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART.
- "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART, - "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART,
- "renesas,rcar-gen2-scif" for R-Car Gen2 SCIF compatible UART, - "renesas,rcar-gen2-scif" for R-Car Gen2 and RZ/G1 SCIF compatible UART,
- "renesas,rcar-gen3-scif" for R-Car Gen3 SCIF compatible UART, - "renesas,rcar-gen3-scif" for R-Car Gen3 and RZ/G2 SCIF compatible UART,
- "renesas,rcar-gen2-scifa" for R-Car Gen2 SCIFA compatible UART, - "renesas,rcar-gen2-scifa" for R-Car Gen2 and RZ/G1 SCIFA compatible UART,
- "renesas,rcar-gen2-scifb" for R-Car Gen2 SCIFB compatible UART, - "renesas,rcar-gen2-scifb" for R-Car Gen2 and RZ/G1 SCIFB compatible UART,
- "renesas,rcar-gen1-hscif" for R-Car Gen1 HSCIF compatible UART, - "renesas,rcar-gen1-hscif" for R-Car Gen1 HSCIF compatible UART,
- "renesas,rcar-gen2-hscif" for R-Car Gen2 HSCIF compatible UART, - "renesas,rcar-gen2-hscif" for R-Car Gen2 and RZ/G1 HSCIF compatible UART,
- "renesas,rcar-gen3-hscif" for R-Car Gen3 HSCIF compatible UART, - "renesas,rcar-gen3-hscif" for R-Car Gen3 and RZ/G2 HSCIF compatible UART,
- "renesas,scif" for generic SCIF compatible UART. - "renesas,scif" for generic SCIF compatible UART.
- "renesas,scifa" for generic SCIFA compatible UART. - "renesas,scifa" for generic SCIFA compatible UART.
- "renesas,scifb" for generic SCIFB compatible UART. - "renesas,scifb" for generic SCIFB compatible UART.

View File

@ -66,6 +66,14 @@ config TTY_PRINTK
If unsure, say N. If unsure, say N.
config TTY_PRINTK_LEVEL
depends on TTY_PRINTK
int "ttyprintk log level (1-7)"
range 1 7
default "6"
help
Printk log level to use for ttyprintk messages.
config PRINTER config PRINTER
tristate "Parallel printer support" tristate "Parallel printer support"
depends on PARPORT depends on PARPORT

View File

@ -37,6 +37,8 @@ static struct ttyprintk_port tpk_port;
*/ */
#define TPK_STR_SIZE 508 /* should be bigger then max expected line length */ #define TPK_STR_SIZE 508 /* should be bigger then max expected line length */
#define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */ #define TPK_MAX_ROOM 4096 /* we could assume 4K for instance */
#define TPK_PREFIX KERN_SOH __stringify(CONFIG_TTY_PRINTK_LEVEL)
static int tpk_curr; static int tpk_curr;
static char tpk_buffer[TPK_STR_SIZE + 4]; static char tpk_buffer[TPK_STR_SIZE + 4];
@ -45,7 +47,7 @@ static void tpk_flush(void)
{ {
if (tpk_curr > 0) { if (tpk_curr > 0) {
tpk_buffer[tpk_curr] = '\0'; tpk_buffer[tpk_curr] = '\0';
pr_info("[U] %s\n", tpk_buffer); printk(TPK_PREFIX "[U] %s\n", tpk_buffer);
tpk_curr = 0; tpk_curr = 0;
} }
} }

View File

@ -65,7 +65,7 @@ static int gnss_serial_write_raw(struct gnss_device *gdev,
/* write is only buffered synchronously */ /* write is only buffered synchronously */
ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT); ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
if (ret < 0) if (ret < 0 || ret < count)
return ret; return ret;
/* FIXME: determine if interrupted? */ /* FIXME: determine if interrupted? */

View File

@ -85,7 +85,7 @@ static int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf,
/* write is only buffered synchronously */ /* write is only buffered synchronously */
ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT); ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT);
if (ret < 0) if (ret < 0 || ret < count)
return ret; return ret;
/* FIXME: determine if interrupted? */ /* FIXME: determine if interrupted? */

View File

@ -353,7 +353,7 @@ void __init hvc_opal_init_early(void)
if (!opal) if (!opal)
return; return;
for_each_child_of_node(opal, np) { for_each_child_of_node(opal, np) {
if (!strcmp(np->name, "serial")) { if (of_node_name_eq(np, "serial")) {
stdout_node = np; stdout_node = np;
break; break;
} }

View File

@ -371,20 +371,11 @@ device_initcall(hvc_vio_init); /* after drivers/tty/hvc/hvc_console.c */
void __init hvc_vio_init_early(void) void __init hvc_vio_init_early(void)
{ {
const __be32 *termno; const __be32 *termno;
const char *name;
const struct hv_ops *ops; const struct hv_ops *ops;
/* find the boot console from /chosen/stdout */ /* find the boot console from /chosen/stdout */
if (!of_stdout)
return;
name = of_get_property(of_stdout, "name", NULL);
if (!name) {
printk(KERN_WARNING "stdout node missing 'name' property!\n");
return;
}
/* Check if it's a virtual terminal */ /* Check if it's a virtual terminal */
if (strncmp(name, "vty", 3) != 0) if (!of_node_name_prefix(of_stdout, "vty"))
return; return;
termno = of_get_property(of_stdout, "reg", NULL); termno = of_get_property(of_stdout, "reg", NULL);
if (termno == NULL) if (termno == NULL)

View File

@ -612,7 +612,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
} }
/* no data */ /* no data */
if (file->f_flags & O_NONBLOCK) { if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN; ret = -EAGAIN;
break; break;
} }
@ -679,7 +679,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
if (tbuf) if (tbuf)
break; break;
if (file->f_flags & O_NONBLOCK) { if (tty_io_nonblock(tty, file)) {
error = -EAGAIN; error = -EAGAIN;
break; break;
} }

View File

@ -1085,7 +1085,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
pMsg = remove_msg(pInfo, pClient); pMsg = remove_msg(pInfo, pClient);
if (pMsg == NULL) { if (pMsg == NULL) {
/* no messages available. */ /* no messages available. */
if (file->f_flags & O_NONBLOCK) { if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN; ret = -EAGAIN;
goto unlock; goto unlock;
} }

View File

@ -1702,7 +1702,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
down_read(&tty->termios_rwsem); down_read(&tty->termios_rwsem);
while (1) { do {
/* /*
* When PARMRK is set, each input char may take up to 3 chars * When PARMRK is set, each input char may take up to 3 chars
* in the read buf; reduce the buffer space avail by 3x * in the read buf; reduce the buffer space avail by 3x
@ -1744,7 +1744,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
fp += n; fp += n;
count -= n; count -= n;
rcvd += n; rcvd += n;
} } while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));
tty->receive_room = room; tty->receive_room = room;
@ -2211,7 +2211,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
break; break;
if (!timeout) if (!timeout)
break; break;
if (file->f_flags & O_NONBLOCK) { if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN; retval = -EAGAIN;
break; break;
} }
@ -2365,7 +2365,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
} }
if (!nr) if (!nr)
break; break;
if (file->f_flags & O_NONBLOCK) { if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN; retval = -EAGAIN;
break; break;
} }

View File

@ -15,6 +15,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/sched.h>
#include <linux/serdev.h> #include <linux/serdev.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -216,6 +217,21 @@ void serdev_device_write_wakeup(struct serdev_device *serdev)
} }
EXPORT_SYMBOL_GPL(serdev_device_write_wakeup); EXPORT_SYMBOL_GPL(serdev_device_write_wakeup);
/**
* serdev_device_write_buf() - write data asynchronously
* @serdev: serdev device
* @buf: data to be written
* @count: number of bytes to write
*
* Write data to the device asynchronously.
*
* Note that any accepted data has only been buffered by the controller; use
* serdev_device_wait_until_sent() to make sure the controller write buffer
* has actually been emptied.
*
* Return: The number of bytes written (less than count if not enough room in
* the write buffer), or a negative errno on errors.
*/
int serdev_device_write_buf(struct serdev_device *serdev, int serdev_device_write_buf(struct serdev_device *serdev,
const unsigned char *buf, size_t count) const unsigned char *buf, size_t count)
{ {
@ -228,17 +244,42 @@ int serdev_device_write_buf(struct serdev_device *serdev,
} }
EXPORT_SYMBOL_GPL(serdev_device_write_buf); EXPORT_SYMBOL_GPL(serdev_device_write_buf);
/**
* serdev_device_write() - write data synchronously
* @serdev: serdev device
* @buf: data to be written
* @count: number of bytes to write
* @timeout: timeout in jiffies, or 0 to wait indefinitely
*
* Write data to the device synchronously by repeatedly calling
* serdev_device_write() until the controller has accepted all data (unless
* interrupted by a timeout or a signal).
*
* Note that any accepted data has only been buffered by the controller; use
* serdev_device_wait_until_sent() to make sure the controller write buffer
* has actually been emptied.
*
* Note that this function depends on serdev_device_write_wakeup() being
* called in the serdev driver write_wakeup() callback.
*
* Return: The number of bytes written (less than count if interrupted),
* -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or
* a negative errno on errors.
*/
int serdev_device_write(struct serdev_device *serdev, int serdev_device_write(struct serdev_device *serdev,
const unsigned char *buf, size_t count, const unsigned char *buf, size_t count,
unsigned long timeout) long timeout)
{ {
struct serdev_controller *ctrl = serdev->ctrl; struct serdev_controller *ctrl = serdev->ctrl;
int written = 0;
int ret; int ret;
if (!ctrl || !ctrl->ops->write_buf || if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup)
(timeout && !serdev->ops->write_wakeup))
return -EINVAL; return -EINVAL;
if (timeout == 0)
timeout = MAX_SCHEDULE_TIMEOUT;
mutex_lock(&serdev->write_lock); mutex_lock(&serdev->write_lock);
do { do {
reinit_completion(&serdev->write_comp); reinit_completion(&serdev->write_comp);
@ -247,14 +288,29 @@ int serdev_device_write(struct serdev_device *serdev,
if (ret < 0) if (ret < 0)
break; break;
written += ret;
buf += ret; buf += ret;
count -= ret; count -= ret;
} while (count && if (count == 0)
(timeout = wait_for_completion_timeout(&serdev->write_comp, break;
timeout)));
timeout = wait_for_completion_interruptible_timeout(&serdev->write_comp,
timeout);
} while (timeout > 0);
mutex_unlock(&serdev->write_lock); mutex_unlock(&serdev->write_lock);
return ret < 0 ? ret : (count ? -ETIMEDOUT : 0);
if (ret < 0)
return ret;
if (timeout <= 0 && written == 0) {
if (timeout == -ERESTARTSYS)
return -ERESTARTSYS;
else
return -ETIMEDOUT;
}
return written;
} }
EXPORT_SYMBOL_GPL(serdev_device_write); EXPORT_SYMBOL_GPL(serdev_device_write);

View File

@ -5,6 +5,10 @@
* Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
* Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
*/ */
#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
@ -293,7 +297,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port)
if (lsr & UART_LSR_THRE) if (lsr & UART_LSR_THRE)
serial8250_tx_chars(up); serial8250_tx_chars(up);
spin_unlock_irqrestore(&port->lock, flags); uart_unlock_and_check_sysrq(port, flags);
return 1; return 1;
} }

View File

@ -942,6 +942,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
return NULL; return NULL;
} }
static void serial_8250_overrun_backoff_work(struct work_struct *work)
{
struct uart_8250_port *up =
container_of(to_delayed_work(work), struct uart_8250_port,
overrun_backoff);
struct uart_port *port = &up->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
up->port.read_status_mask |= UART_LSR_DR;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&port->lock, flags);
}
/** /**
* serial8250_register_8250_port - register a serial port * serial8250_register_8250_port - register a serial port
* @up: serial port template * @up: serial port template
@ -1056,6 +1071,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
ret = 0; ret = 0;
} }
} }
/* Initialise interrupt backoff work if required */
if (up->overrun_backoff_time_ms > 0) {
uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms;
INIT_DELAYED_WORK(&uart->overrun_backoff,
serial_8250_overrun_backoff_work);
} else {
uart->overrun_backoff_time_ms = 0;
}
mutex_unlock(&serial_mutex); mutex_unlock(&serial_mutex);
return ret; return ret;

View File

@ -1,4 +1,8 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/serial_reg.h> #include <linux/serial_reg.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
@ -45,8 +49,29 @@ int fsl8250_handle_irq(struct uart_port *port)
lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
if (lsr & (UART_LSR_DR | UART_LSR_BI)) /* Process incoming characters first */
if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
(up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
lsr = serial8250_rx_chars(up, lsr); lsr = serial8250_rx_chars(up, lsr);
}
/* Stop processing interrupts on input overrun */
if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
unsigned long delay;
up->ier = port->serial_in(port, UART_IER);
if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
port->ops->stop_rx(port);
} else {
/* Keep restarting the timer until
* the input overrun subsides.
*/
cancel_delayed_work(&up->overrun_backoff);
}
delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
schedule_delayed_work(&up->overrun_backoff, delay);
}
serial8250_modem_status(up); serial8250_modem_status(up);
@ -54,7 +79,7 @@ int fsl8250_handle_irq(struct uart_port *port)
serial8250_tx_chars(up); serial8250_tx_chars(up);
up->lsr_saved_flags = orig_lsr; up->lsr_saved_flags = orig_lsr;
spin_unlock_irqrestore(&up->port.lock, flags); uart_unlock_and_check_sysrq(&up->port, flags);
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(fsl8250_handle_irq); EXPORT_SYMBOL_GPL(fsl8250_handle_irq);

View File

@ -14,6 +14,10 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
#include <linux/serial_reg.h> #include <linux/serial_reg.h>
#include <linux/console.h>
#include <linux/dma-mapping.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include "8250.h" #include "8250.h"
@ -22,12 +26,172 @@
#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ #define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */
#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ #define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */
#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */
#define MTK_UART_DMA_EN_TX 0x2
#define MTK_UART_DMA_EN_RX 0x5
#define MTK_UART_TX_SIZE UART_XMIT_SIZE
#define MTK_UART_RX_SIZE 0x8000
#define MTK_UART_TX_TRIGGER 1
#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE
#ifdef CONFIG_SERIAL_8250_DMA
enum dma_rx_status {
DMA_RX_START = 0,
DMA_RX_RUNNING = 1,
DMA_RX_SHUTDOWN = 2,
};
#endif
struct mtk8250_data { struct mtk8250_data {
int line; int line;
unsigned int rx_pos;
struct clk *uart_clk; struct clk *uart_clk;
struct clk *bus_clk; struct clk *bus_clk;
struct uart_8250_dma *dma;
#ifdef CONFIG_SERIAL_8250_DMA
enum dma_rx_status rx_status;
#endif
}; };
#ifdef CONFIG_SERIAL_8250_DMA
static void mtk8250_rx_dma(struct uart_8250_port *up);
static void mtk8250_dma_rx_complete(void *param)
{
struct uart_8250_port *up = param;
struct uart_8250_dma *dma = up->dma;
struct mtk8250_data *data = up->port.private_data;
struct tty_port *tty_port = &up->port.state->port;
struct dma_tx_state state;
unsigned char *ptr;
int copied;
dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
dma->rx_size, DMA_FROM_DEVICE);
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
if (data->rx_status == DMA_RX_SHUTDOWN)
return;
if ((data->rx_pos + state.residue) <= dma->rx_size) {
ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
copied = tty_insert_flip_string(tty_port, ptr, state.residue);
} else {
ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
copied = tty_insert_flip_string(tty_port, ptr,
dma->rx_size - data->rx_pos);
ptr = (unsigned char *)(dma->rx_buf);
copied += tty_insert_flip_string(tty_port, ptr,
data->rx_pos + state.residue - dma->rx_size);
}
up->port.icount.rx += copied;
tty_flip_buffer_push(tty_port);
mtk8250_rx_dma(up);
}
static void mtk8250_rx_dma(struct uart_8250_port *up)
{
struct uart_8250_dma *dma = up->dma;
struct mtk8250_data *data = up->port.private_data;
struct dma_async_tx_descriptor *desc;
struct dma_tx_state state;
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
dma->rx_size, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
pr_err("failed to prepare rx slave single\n");
return;
}
desc->callback = mtk8250_dma_rx_complete;
desc->callback_param = up;
dma->rx_cookie = dmaengine_submit(desc);
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
data->rx_pos = state.residue;
dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
dma->rx_size, DMA_FROM_DEVICE);
dma_async_issue_pending(dma->rxchan);
}
static void mtk8250_dma_enable(struct uart_8250_port *up)
{
struct uart_8250_dma *dma = up->dma;
struct mtk8250_data *data = up->port.private_data;
int lcr = serial_in(up, UART_LCR);
if (data->rx_status != DMA_RX_START)
return;
dma->rxconf.direction = DMA_DEV_TO_MEM;
dma->rxconf.src_addr_width = dma->rx_size / 1024;
dma->rxconf.src_addr = dma->rx_addr;
dma->txconf.direction = DMA_MEM_TO_DEV;
dma->txconf.dst_addr_width = MTK_UART_TX_SIZE / 1024;
dma->txconf.dst_addr = dma->tx_addr;
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT);
serial_out(up, MTK_UART_DMA_EN,
MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
serial_out(up, UART_EFR, UART_EFR_ECB);
serial_out(up, UART_LCR, lcr);
if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0)
pr_err("failed to configure rx dma channel\n");
if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0)
pr_err("failed to configure tx dma channel\n");
data->rx_status = DMA_RX_RUNNING;
data->rx_pos = 0;
mtk8250_rx_dma(up);
}
#endif
static int mtk8250_startup(struct uart_port *port)
{
#ifdef CONFIG_SERIAL_8250_DMA
struct uart_8250_port *up = up_to_u8250p(port);
struct mtk8250_data *data = port->private_data;
/* disable DMA for console */
if (uart_console(port))
up->dma = NULL;
if (up->dma) {
data->rx_status = DMA_RX_START;
uart_circ_clear(&port->state->xmit);
}
#endif
memset(&port->icount, 0, sizeof(port->icount));
return serial8250_do_startup(port);
}
static void mtk8250_shutdown(struct uart_port *port)
{
#ifdef CONFIG_SERIAL_8250_DMA
struct uart_8250_port *up = up_to_u8250p(port);
struct mtk8250_data *data = port->private_data;
if (up->dma)
data->rx_status = DMA_RX_SHUTDOWN;
#endif
return serial8250_do_shutdown(port);
}
static void static void
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old) struct ktermios *old)
@ -36,6 +200,17 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
unsigned long flags; unsigned long flags;
unsigned int baud, quot; unsigned int baud, quot;
#ifdef CONFIG_SERIAL_8250_DMA
if (up->dma) {
if (uart_console(port)) {
devm_kfree(up->port.dev, up->dma);
up->dma = NULL;
} else {
mtk8250_dma_enable(up);
}
}
#endif
serial8250_do_set_termios(port, termios, old); serial8250_do_set_termios(port, termios, old);
/* /*
@ -143,9 +318,20 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
pm_runtime_put_sync_suspend(port->dev); pm_runtime_put_sync_suspend(port->dev);
} }
#ifdef CONFIG_SERIAL_8250_DMA
static bool mtk8250_dma_filter(struct dma_chan *chan, void *param)
{
return false;
}
#endif
static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
struct mtk8250_data *data) struct mtk8250_data *data)
{ {
#ifdef CONFIG_SERIAL_8250_DMA
int dmacnt;
#endif
data->uart_clk = devm_clk_get(&pdev->dev, "baud"); data->uart_clk = devm_clk_get(&pdev->dev, "baud");
if (IS_ERR(data->uart_clk)) { if (IS_ERR(data->uart_clk)) {
/* /*
@ -162,7 +348,23 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
} }
data->bus_clk = devm_clk_get(&pdev->dev, "bus"); data->bus_clk = devm_clk_get(&pdev->dev, "bus");
return PTR_ERR_OR_ZERO(data->bus_clk); if (IS_ERR(data->bus_clk))
return PTR_ERR(data->bus_clk);
data->dma = NULL;
#ifdef CONFIG_SERIAL_8250_DMA
dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
if (dmacnt == 2) {
data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
GFP_KERNEL);
data->dma->fn = mtk8250_dma_filter;
data->dma->rx_size = MTK_UART_RX_SIZE;
data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
}
#endif
return 0;
} }
static int mtk8250_probe(struct platform_device *pdev) static int mtk8250_probe(struct platform_device *pdev)
@ -204,8 +406,14 @@ static int mtk8250_probe(struct platform_device *pdev)
uart.port.iotype = UPIO_MEM32; uart.port.iotype = UPIO_MEM32;
uart.port.regshift = 2; uart.port.regshift = 2;
uart.port.private_data = data; uart.port.private_data = data;
uart.port.shutdown = mtk8250_shutdown;
uart.port.startup = mtk8250_startup;
uart.port.set_termios = mtk8250_set_termios; uart.port.set_termios = mtk8250_set_termios;
uart.port.uartclk = clk_get_rate(data->uart_clk); uart.port.uartclk = clk_get_rate(data->uart_clk);
#ifdef CONFIG_SERIAL_8250_DMA
if (data->dma)
uart.dma = data->dma;
#endif
/* Disable Rate Fix function */ /* Disable Rate Fix function */
writel(0x0, uart.port.membase + writel(0x0, uart.port.membase +

View File

@ -240,6 +240,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
port8250.capabilities |= UART_CAP_AFE; port8250.capabilities |= UART_CAP_AFE;
if (of_property_read_u32(ofdev->dev.of_node,
"overrun-throttle-ms",
&port8250.overrun_backoff_time_ms) != 0)
port8250.overrun_backoff_time_ms = 0;
ret = serial8250_register_8250_port(&port8250); ret = serial8250_register_8250_port(&port8250);
if (ret < 0) if (ret < 0)
goto err_dispose; goto err_dispose;

View File

@ -8,6 +8,10 @@
* *
*/ */
#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
@ -1085,7 +1089,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
} }
} }
spin_unlock_irqrestore(&port->lock, flags); uart_unlock_and_check_sysrq(port, flags);
serial8250_rpm_put(up); serial8250_rpm_put(up);
return 1; return 1;
} }

View File

@ -1736,7 +1736,7 @@ void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr)
else if (lsr & UART_LSR_FE) else if (lsr & UART_LSR_FE)
flag = TTY_FRAME; flag = TTY_FRAME;
} }
if (uart_handle_sysrq_char(port, ch)) if (uart_prepare_sysrq_char(port, ch))
return; return;
uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
@ -1878,7 +1878,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE))
serial8250_tx_chars(up); serial8250_tx_chars(up);
spin_unlock_irqrestore(&port->lock, flags); uart_unlock_and_check_sysrq(port, flags);
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(serial8250_handle_irq); EXPORT_SYMBOL_GPL(serial8250_handle_irq);
@ -3239,9 +3239,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
serial8250_rpm_get(up); serial8250_rpm_get(up);
if (port->sysrq) if (oops_in_progress)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock_irqsave(&port->lock, flags); locked = spin_trylock_irqsave(&port->lock, flags);
else else
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);

View File

@ -2780,6 +2780,7 @@ static struct platform_driver arm_sbsa_uart_platform_driver = {
.name = "sbsa-uart", .name = "sbsa-uart",
.of_match_table = of_match_ptr(sbsa_uart_of_match), .of_match_table = of_match_ptr(sbsa_uart_of_match),
.acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match), .acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match),
.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
}, },
}; };
@ -2808,6 +2809,7 @@ static struct amba_driver pl011_driver = {
.drv = { .drv = {
.name = "uart-pl011", .name = "uart-pl011",
.pm = &pl011_dev_pm_ops, .pm = &pl011_dev_pm_ops,
.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
}, },
.id_table = pl011_ids, .id_table = pl011_ids,
.probe = pl011_probe, .probe = pl011_probe,

View File

@ -1479,6 +1479,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios,
else else
cr1 &= ~UARTCR1_PT; cr1 &= ~UARTCR1_PT;
} }
} else {
cr1 &= ~UARTCR1_PE;
} }
/* ask the core to calculate the divisor */ /* ask the core to calculate the divisor */
@ -1682,7 +1684,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
ctrl &= ~UARTCTRL_PE; ctrl &= ~UARTCTRL_PE;
ctrl |= UARTCTRL_M; ctrl |= UARTCTRL_M;
} else { } else {
ctrl |= UARTCR1_PE; ctrl |= UARTCTRL_PE;
if ((termios->c_cflag & CSIZE) == CS8) if ((termios->c_cflag & CSIZE) == CS8)
ctrl |= UARTCTRL_M; ctrl |= UARTCTRL_M;
if (termios->c_cflag & PARODD) if (termios->c_cflag & PARODD)
@ -1690,6 +1692,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios,
else else
ctrl &= ~UARTCTRL_PT; ctrl &= ~UARTCTRL_PT;
} }
} else {
ctrl &= ~UARTCTRL_PE;
} }
/* ask the core to calculate the divisor */ /* ask the core to calculate the divisor */

View File

@ -2064,7 +2064,7 @@ imx_uart_console_setup(struct console *co, char *options)
retval = clk_prepare(sport->clk_per); retval = clk_prepare(sport->clk_per);
if (retval) if (retval)
clk_disable_unprepare(sport->clk_ipg); clk_unprepare(sport->clk_ipg);
error_console: error_console:
return retval; return retval;

View File

@ -8,24 +8,23 @@
* Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com> * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com>
*/ */
#include <linux/slab.h> #include <linux/clk.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/tty.h> #include <linux/gpio.h>
#include <linux/tty_flip.h> #include <linux/init.h>
#include <linux/serial_core.h> #include <linux/io.h>
#include <linux/serial.h> #include <linux/ioport.h>
#include <linux/of_platform.h> #include <linux/lantiq.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/io.h> #include <linux/of_platform.h>
#include <linux/clk.h> #include <linux/serial.h>
#include <linux/gpio.h> #include <linux/serial_core.h>
#include <linux/slab.h>
#include <lantiq_soc.h> #include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#define PORT_LTQ_ASC 111 #define PORT_LTQ_ASC 111
#define MAXPORTS 2 #define MAXPORTS 2
@ -105,7 +104,7 @@ static DEFINE_SPINLOCK(ltq_asc_lock);
struct ltq_uart_port { struct ltq_uart_port {
struct uart_port port; struct uart_port port;
/* clock used to derive divider */ /* clock used to derive divider */
struct clk *fpiclk; struct clk *freqclk;
/* clock gating of the ASC core */ /* clock gating of the ASC core */
struct clk *clk; struct clk *clk;
unsigned int tx_irq; unsigned int tx_irq;
@ -113,6 +112,13 @@ struct ltq_uart_port {
unsigned int err_irq; unsigned int err_irq;
}; };
static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg)
{
u32 tmp = readl(reg);
writel((tmp & ~clear) | set, reg);
}
static inline struct static inline struct
ltq_uart_port *to_ltq_uart_port(struct uart_port *port) ltq_uart_port *to_ltq_uart_port(struct uart_port *port)
{ {
@ -138,7 +144,7 @@ lqasc_start_tx(struct uart_port *port)
static void static void
lqasc_stop_rx(struct uart_port *port) lqasc_stop_rx(struct uart_port *port)
{ {
ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE); writel(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
} }
static int static int
@ -147,11 +153,11 @@ lqasc_rx_chars(struct uart_port *port)
struct tty_port *tport = &port->state->port; struct tty_port *tport = &port->state->port;
unsigned int ch = 0, rsr = 0, fifocnt; unsigned int ch = 0, rsr = 0, fifocnt;
fifocnt = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; fifocnt = readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
while (fifocnt--) { while (fifocnt--) {
u8 flag = TTY_NORMAL; u8 flag = TTY_NORMAL;
ch = ltq_r8(port->membase + LTQ_ASC_RBUF); ch = readb(port->membase + LTQ_ASC_RBUF);
rsr = (ltq_r32(port->membase + LTQ_ASC_STATE) rsr = (readl(port->membase + LTQ_ASC_STATE)
& ASCSTATE_ANY) | UART_DUMMY_UER_RX; & ASCSTATE_ANY) | UART_DUMMY_UER_RX;
tty_flip_buffer_push(tport); tty_flip_buffer_push(tport);
port->icount.rx++; port->icount.rx++;
@ -163,16 +169,16 @@ lqasc_rx_chars(struct uart_port *port)
if (rsr & ASCSTATE_ANY) { if (rsr & ASCSTATE_ANY) {
if (rsr & ASCSTATE_PE) { if (rsr & ASCSTATE_PE) {
port->icount.parity++; port->icount.parity++;
ltq_w32_mask(0, ASCWHBSTATE_CLRPE, asc_update_bits(0, ASCWHBSTATE_CLRPE,
port->membase + LTQ_ASC_WHBSTATE); port->membase + LTQ_ASC_WHBSTATE);
} else if (rsr & ASCSTATE_FE) { } else if (rsr & ASCSTATE_FE) {
port->icount.frame++; port->icount.frame++;
ltq_w32_mask(0, ASCWHBSTATE_CLRFE, asc_update_bits(0, ASCWHBSTATE_CLRFE,
port->membase + LTQ_ASC_WHBSTATE); port->membase + LTQ_ASC_WHBSTATE);
} }
if (rsr & ASCSTATE_ROE) { if (rsr & ASCSTATE_ROE) {
port->icount.overrun++; port->icount.overrun++;
ltq_w32_mask(0, ASCWHBSTATE_CLRROE, asc_update_bits(0, ASCWHBSTATE_CLRROE,
port->membase + LTQ_ASC_WHBSTATE); port->membase + LTQ_ASC_WHBSTATE);
} }
@ -211,10 +217,10 @@ lqasc_tx_chars(struct uart_port *port)
return; return;
} }
while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) & while (((readl(port->membase + LTQ_ASC_FSTAT) &
ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) { ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) {
if (port->x_char) { if (port->x_char) {
ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF); writeb(port->x_char, port->membase + LTQ_ASC_TBUF);
port->icount.tx++; port->icount.tx++;
port->x_char = 0; port->x_char = 0;
continue; continue;
@ -223,7 +229,7 @@ lqasc_tx_chars(struct uart_port *port)
if (uart_circ_empty(xmit)) if (uart_circ_empty(xmit))
break; break;
ltq_w8(port->state->xmit.buf[port->state->xmit.tail], writeb(port->state->xmit.buf[port->state->xmit.tail],
port->membase + LTQ_ASC_TBUF); port->membase + LTQ_ASC_TBUF);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++; port->icount.tx++;
@ -239,7 +245,7 @@ lqasc_tx_int(int irq, void *_port)
unsigned long flags; unsigned long flags;
struct uart_port *port = (struct uart_port *)_port; struct uart_port *port = (struct uart_port *)_port;
spin_lock_irqsave(&ltq_asc_lock, flags); spin_lock_irqsave(&ltq_asc_lock, flags);
ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR); writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
spin_unlock_irqrestore(&ltq_asc_lock, flags); spin_unlock_irqrestore(&ltq_asc_lock, flags);
lqasc_start_tx(port); lqasc_start_tx(port);
return IRQ_HANDLED; return IRQ_HANDLED;
@ -252,7 +258,7 @@ lqasc_err_int(int irq, void *_port)
struct uart_port *port = (struct uart_port *)_port; struct uart_port *port = (struct uart_port *)_port;
spin_lock_irqsave(&ltq_asc_lock, flags); spin_lock_irqsave(&ltq_asc_lock, flags);
/* clear any pending interrupts */ /* clear any pending interrupts */
ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE); ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
spin_unlock_irqrestore(&ltq_asc_lock, flags); spin_unlock_irqrestore(&ltq_asc_lock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
@ -264,7 +270,7 @@ lqasc_rx_int(int irq, void *_port)
unsigned long flags; unsigned long flags;
struct uart_port *port = (struct uart_port *)_port; struct uart_port *port = (struct uart_port *)_port;
spin_lock_irqsave(&ltq_asc_lock, flags); spin_lock_irqsave(&ltq_asc_lock, flags);
ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR); writel(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
lqasc_rx_chars(port); lqasc_rx_chars(port);
spin_unlock_irqrestore(&ltq_asc_lock, flags); spin_unlock_irqrestore(&ltq_asc_lock, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
@ -274,7 +280,7 @@ static unsigned int
lqasc_tx_empty(struct uart_port *port) lqasc_tx_empty(struct uart_port *port)
{ {
int status; int status;
status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK; status = readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK;
return status ? 0 : TIOCSER_TEMT; return status ? 0 : TIOCSER_TEMT;
} }
@ -301,18 +307,18 @@ lqasc_startup(struct uart_port *port)
int retval; int retval;
if (!IS_ERR(ltq_port->clk)) if (!IS_ERR(ltq_port->clk))
clk_enable(ltq_port->clk); clk_prepare_enable(ltq_port->clk);
port->uartclk = clk_get_rate(ltq_port->fpiclk); port->uartclk = clk_get_rate(ltq_port->freqclk);
ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET), asc_update_bits(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
port->membase + LTQ_ASC_CLC); port->membase + LTQ_ASC_CLC);
ltq_w32(0, port->membase + LTQ_ASC_PISEL); writel(0, port->membase + LTQ_ASC_PISEL);
ltq_w32( writel(
((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) | ((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU, ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON); port->membase + LTQ_ASC_TXFCON);
ltq_w32( writel(
((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK) ((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
| ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU, | ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
port->membase + LTQ_ASC_RXFCON); port->membase + LTQ_ASC_RXFCON);
@ -320,7 +326,7 @@ lqasc_startup(struct uart_port *port)
* setting enable bits * setting enable bits
*/ */
wmb(); wmb();
ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN | asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
ASCCON_ROEN, port->membase + LTQ_ASC_CON); ASCCON_ROEN, port->membase + LTQ_ASC_CON);
retval = request_irq(ltq_port->tx_irq, lqasc_tx_int, retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
@ -344,7 +350,7 @@ lqasc_startup(struct uart_port *port)
goto err2; goto err2;
} }
ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX, writel(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
port->membase + LTQ_ASC_IRNREN); port->membase + LTQ_ASC_IRNREN);
return 0; return 0;
@ -363,13 +369,13 @@ lqasc_shutdown(struct uart_port *port)
free_irq(ltq_port->rx_irq, port); free_irq(ltq_port->rx_irq, port);
free_irq(ltq_port->err_irq, port); free_irq(ltq_port->err_irq, port);
ltq_w32(0, port->membase + LTQ_ASC_CON); writel(0, port->membase + LTQ_ASC_CON);
ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU, asc_update_bits(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
port->membase + LTQ_ASC_RXFCON); port->membase + LTQ_ASC_RXFCON);
ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, asc_update_bits(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON); port->membase + LTQ_ASC_TXFCON);
if (!IS_ERR(ltq_port->clk)) if (!IS_ERR(ltq_port->clk))
clk_disable(ltq_port->clk); clk_disable_unprepare(ltq_port->clk);
} }
static void static void
@ -438,7 +444,7 @@ lqasc_set_termios(struct uart_port *port,
spin_lock_irqsave(&ltq_asc_lock, flags); spin_lock_irqsave(&ltq_asc_lock, flags);
/* set up CON */ /* set up CON */
ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON); asc_update_bits(0, con, port->membase + LTQ_ASC_CON);
/* Set baud rate - take a divider of 2 into account */ /* Set baud rate - take a divider of 2 into account */
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
@ -446,22 +452,22 @@ lqasc_set_termios(struct uart_port *port,
divisor = divisor / 2 - 1; divisor = divisor / 2 - 1;
/* disable the baudrate generator */ /* disable the baudrate generator */
ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON); asc_update_bits(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
/* make sure the fractional divider is off */ /* make sure the fractional divider is off */
ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON); asc_update_bits(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
/* set up to use divisor of 2 */ /* set up to use divisor of 2 */
ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON); asc_update_bits(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
/* now we can write the new baudrate into the register */ /* now we can write the new baudrate into the register */
ltq_w32(divisor, port->membase + LTQ_ASC_BG); writel(divisor, port->membase + LTQ_ASC_BG);
/* turn the baudrate generator back on */ /* turn the baudrate generator back on */
ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON); asc_update_bits(0, ASCCON_R, port->membase + LTQ_ASC_CON);
/* enable rx */ /* enable rx */
ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE); writel(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
spin_unlock_irqrestore(&ltq_asc_lock, flags); spin_unlock_irqrestore(&ltq_asc_lock, flags);
@ -572,10 +578,10 @@ lqasc_console_putchar(struct uart_port *port, int ch)
return; return;
do { do {
fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT) fifofree = (readl(port->membase + LTQ_ASC_FSTAT)
& ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF; & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF;
} while (fifofree == 0); } while (fifofree == 0);
ltq_w8(ch, port->membase + LTQ_ASC_TBUF); writeb(ch, port->membase + LTQ_ASC_TBUF);
} }
static void lqasc_serial_port_write(struct uart_port *port, const char *s, static void lqasc_serial_port_write(struct uart_port *port, const char *s,
@ -623,9 +629,9 @@ lqasc_console_setup(struct console *co, char *options)
port = &ltq_port->port; port = &ltq_port->port;
if (!IS_ERR(ltq_port->clk)) if (!IS_ERR(ltq_port->clk))
clk_enable(ltq_port->clk); clk_prepare_enable(ltq_port->clk);
port->uartclk = clk_get_rate(ltq_port->fpiclk); port->uartclk = clk_get_rate(ltq_port->freqclk);
if (options) if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow); uart_parse_options(options, &baud, &parity, &bits, &flow);
@ -688,7 +694,7 @@ lqasc_probe(struct platform_device *pdev)
struct ltq_uart_port *ltq_port; struct ltq_uart_port *ltq_port;
struct uart_port *port; struct uart_port *port;
struct resource *mmres, irqres[3]; struct resource *mmres, irqres[3];
int line = 0; int line;
int ret; int ret;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -699,9 +705,20 @@ lqasc_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
/* check if this is the console port */ /* get serial id */
if (mmres->start != CPHYSADDR(LTQ_EARLY_ASC)) line = of_alias_get_id(node, "serial");
line = 1; if (line < 0) {
if (IS_ENABLED(CONFIG_LANTIQ)) {
if (mmres->start == CPHYSADDR(LTQ_EARLY_ASC))
line = 0;
else
line = 1;
} else {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
line);
return line;
}
}
if (lqasc_port[line]) { if (lqasc_port[line]) {
dev_err(&pdev->dev, "port %d already allocated\n", line); dev_err(&pdev->dev, "port %d already allocated\n", line);
@ -726,14 +743,22 @@ lqasc_probe(struct platform_device *pdev)
port->irq = irqres[0].start; port->irq = irqres[0].start;
port->mapbase = mmres->start; port->mapbase = mmres->start;
ltq_port->fpiclk = clk_get_fpi(); if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
if (IS_ERR(ltq_port->fpiclk)) { ltq_port->freqclk = clk_get_fpi();
else
ltq_port->freqclk = devm_clk_get(&pdev->dev, "freq");
if (IS_ERR(ltq_port->freqclk)) {
pr_err("failed to get fpi clk\n"); pr_err("failed to get fpi clk\n");
return -ENOENT; return -ENOENT;
} }
/* not all asc ports have clock gates, lets ignore the return code */ /* not all asc ports have clock gates, lets ignore the return code */
ltq_port->clk = clk_get(&pdev->dev, NULL); if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
ltq_port->clk = clk_get(&pdev->dev, NULL);
else
ltq_port->clk = devm_clk_get(&pdev->dev, "asc");
ltq_port->tx_irq = irqres[0].start; ltq_port->tx_irq = irqres[0].start;
ltq_port->rx_irq = irqres[1].start; ltq_port->rx_irq = irqres[1].start;
@ -759,7 +784,7 @@ static struct platform_driver lqasc_driver = {
}, },
}; };
int __init static int __init
init_lqasc(void) init_lqasc(void)
{ {
int ret; int ret;

View File

@ -833,12 +833,9 @@ static void max310x_wq_proc(struct work_struct *ws)
static unsigned int max310x_tx_empty(struct uart_port *port) static unsigned int max310x_tx_empty(struct uart_port *port)
{ {
unsigned int lvl, sts; u8 lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); return lvl ? 0 : TIOCSER_TEMT;
sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
} }
static unsigned int max310x_get_mctrl(struct uart_port *port) static unsigned int max310x_get_mctrl(struct uart_port *port)

View File

@ -72,6 +72,8 @@
#define BRDV_BAUD_MASK 0x3FF #define BRDV_BAUD_MASK 0x3FF
#define UART_OSAMP 0x14 #define UART_OSAMP 0x14
#define OSAMP_DEFAULT_DIVISOR 16
#define OSAMP_DIVISORS_MASK 0x3F3F3F3F
#define MVEBU_NR_UARTS 2 #define MVEBU_NR_UARTS 2
@ -444,25 +446,34 @@ static void mvebu_uart_shutdown(struct uart_port *port)
static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
{ {
struct mvebu_uart *mvuart = to_mvuart(port); struct mvebu_uart *mvuart = to_mvuart(port);
unsigned int baud_rate_div; unsigned int d_divisor, m_divisor;
u32 brdv; u32 brdv, osamp;
if (IS_ERR(mvuart->clk)) if (IS_ERR(mvuart->clk))
return -PTR_ERR(mvuart->clk); return -PTR_ERR(mvuart->clk);
/* /*
* The UART clock is divided by the value of the divisor to generate * The baudrate is derived from the UART clock thanks to two divisors:
* UCLK_OUT clock, which is 16 times faster than the baudrate. * > D ("baud generator"): can divide the clock from 2 to 2^10 - 1.
* This prescaler can achieve all standard baudrates until 230400. * > M ("fractional divisor"): allows a better accuracy for
* Higher baudrates could be achieved for the extended UART by using the * baudrates higher than 230400.
* programmable oversampling stack (also called fractional divisor). *
* As the derivation of M is rather complicated, the code sticks to its
* default value (x16) when all the prescalers are zeroed, and only
* makes use of D to configure the desired baudrate.
*/ */
baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16); m_divisor = OSAMP_DEFAULT_DIVISOR;
d_divisor = DIV_ROUND_UP(port->uartclk, baud * m_divisor);
brdv = readl(port->membase + UART_BRDV); brdv = readl(port->membase + UART_BRDV);
brdv &= ~BRDV_BAUD_MASK; brdv &= ~BRDV_BAUD_MASK;
brdv |= baud_rate_div; brdv |= d_divisor;
writel(brdv, port->membase + UART_BRDV); writel(brdv, port->membase + UART_BRDV);
osamp = readl(port->membase + UART_OSAMP);
osamp &= ~OSAMP_DIVISORS_MASK;
writel(osamp, port->membase + UART_OSAMP);
return 0; return 0;
} }

View File

@ -933,7 +933,6 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
struct scatterlist *sg; struct scatterlist *sg;
int nent; int nent;
int fifo_size; int fifo_size;
int tx_empty;
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
int num; int num;
int i; int i;
@ -958,11 +957,9 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv)
} }
fifo_size = max(priv->fifo_size, 1); fifo_size = max(priv->fifo_size, 1);
tx_empty = 1;
if (pop_tx_x(priv, xmit->buf)) { if (pop_tx_x(priv, xmit->buf)) {
pch_uart_hal_write(priv, xmit->buf, 1); pch_uart_hal_write(priv, xmit->buf, 1);
port->icount.tx++; port->icount.tx++;
tx_empty = 0;
fifo_size--; fifo_size--;
} }

View File

@ -919,6 +919,7 @@ static struct platform_driver pic32_uart_platform_driver = {
.driver = { .driver = {
.name = PIC32_DEV_NAME, .name = PIC32_DEV_NAME,
.of_match_table = of_match_ptr(pic32_serial_dt_ids), .of_match_table = of_match_ptr(pic32_serial_dt_ids),
.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_PIC32),
}, },
}; };

View File

@ -1648,9 +1648,9 @@ static int __init pmz_probe(void)
*/ */
node_a = node_b = NULL; node_a = node_b = NULL;
for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) {
if (strncmp(np->name, "ch-a", 4) == 0) if (of_node_name_prefix(np, "ch-a"))
node_a = of_node_get(np); node_a = of_node_get(np);
else if (strncmp(np->name, "ch-b", 4) == 0) else if (of_node_name_prefix(np, "ch-b"))
node_b = of_node_get(np); node_b = of_node_get(np);
} }
if (!node_a && !node_b) { if (!node_a && !node_b) {

View File

@ -1,6 +1,10 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved. // Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
#if defined(CONFIG_SERIAL_QCOM_GENI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
#endif
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/io.h> #include <linux/io.h>
@ -89,9 +93,9 @@
#define MAX_LOOPBACK_CFG 3 #define MAX_LOOPBACK_CFG 3
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
#define RX_BYTES_PW 1 #define CONSOLE_RX_BYTES_PW 1
#else #else
#define RX_BYTES_PW 4 #define CONSOLE_RX_BYTES_PW 4
#endif #endif
struct qcom_geni_serial_port { struct qcom_geni_serial_port {
@ -113,6 +117,8 @@ struct qcom_geni_serial_port {
u32 *rx_fifo; u32 *rx_fifo;
u32 loopback; u32 loopback;
bool brk; bool brk;
unsigned int tx_remaining;
}; };
static const struct uart_ops qcom_geni_console_pops; static const struct uart_ops qcom_geni_console_pops;
@ -162,8 +168,7 @@ static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = {
static ssize_t loopback_show(struct device *dev, static ssize_t loopback_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct platform_device *pdev = to_platform_device(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
return snprintf(buf, sizeof(u32), "%d\n", port->loopback); return snprintf(buf, sizeof(u32), "%d\n", port->loopback);
} }
@ -172,8 +177,7 @@ static ssize_t loopback_store(struct device *dev,
struct device_attribute *attr, const char *buf, struct device_attribute *attr, const char *buf,
size_t size) size_t size)
{ {
struct platform_device *pdev = to_platform_device(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
u32 loopback; u32 loopback;
if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) { if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) {
@ -435,6 +439,8 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
struct qcom_geni_serial_port *port; struct qcom_geni_serial_port *port;
bool locked = true; bool locked = true;
unsigned long flags; unsigned long flags;
u32 geni_status;
u32 irq_en;
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
@ -448,6 +454,8 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
else else
spin_lock_irqsave(&uport->lock, flags); spin_lock_irqsave(&uport->lock, flags);
geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
/* Cancel the current write to log the fault */ /* Cancel the current write to log the fault */
if (!locked) { if (!locked) {
geni_se_cancel_m_cmd(&port->se); geni_se_cancel_m_cmd(&port->se);
@ -461,9 +469,26 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
} }
writel_relaxed(M_CMD_CANCEL_EN, uport->membase + writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR); SE_GENI_M_IRQ_CLEAR);
} else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) {
/*
* It seems we can't interrupt existing transfers if all data
* has been sent, in which case we need to look for done first.
*/
qcom_geni_serial_poll_tx_done(uport);
if (uart_circ_chars_pending(&uport->state->xmit)) {
irq_en = readl_relaxed(uport->membase +
SE_GENI_M_IRQ_EN);
writel_relaxed(irq_en | M_TX_FIFO_WATERMARK_EN,
uport->membase + SE_GENI_M_IRQ_EN);
}
} }
__qcom_geni_serial_console_write(uport, s, count); __qcom_geni_serial_console_write(uport, s, count);
if (port->tx_remaining)
qcom_geni_serial_setup_tx(uport, port->tx_remaining);
if (locked) if (locked)
spin_unlock_irqrestore(&uport->lock, flags); spin_unlock_irqrestore(&uport->lock, flags);
} }
@ -495,7 +520,8 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
continue; continue;
} }
sysrq = uart_handle_sysrq_char(uport, buf[c]); sysrq = uart_prepare_sysrq_char(uport, buf[c]);
if (!sysrq) if (!sysrq)
tty_insert_flip_char(tport, buf[c], TTY_NORMAL); tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
} }
@ -694,40 +720,51 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
port->handle_rx(uport, total_bytes, drop); port->handle_rx(uport, total_bytes, drop);
} }
static void qcom_geni_serial_handle_tx(struct uart_port *uport) static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
bool active)
{ {
struct qcom_geni_serial_port *port = to_dev_port(uport, uport); struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
struct circ_buf *xmit = &uport->state->xmit; struct circ_buf *xmit = &uport->state->xmit;
size_t avail; size_t avail;
size_t remaining; size_t remaining;
size_t pending;
int i; int i;
u32 status; u32 status;
u32 irq_en;
unsigned int chunk; unsigned int chunk;
int tail; int tail;
u32 irq_en;
chunk = uart_circ_chars_pending(xmit);
status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
/* Both FIFO and framework buffer are drained */
if (!chunk && !status) { /* Complete the current tx command before taking newly added data */
if (active)
pending = port->tx_remaining;
else
pending = uart_circ_chars_pending(xmit);
/* All data has been transmitted and acknowledged as received */
if (!pending && !status && done) {
qcom_geni_serial_stop_tx(uport); qcom_geni_serial_stop_tx(uport);
goto out_write_wakeup; goto out_write_wakeup;
} }
if (!uart_console(uport)) { avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); avail *= port->tx_bytes_pw;
irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = xmit->tail; tail = xmit->tail;
chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail); chunk = min(avail, pending);
if (!chunk) if (!chunk)
goto out_write_wakeup; goto out_write_wakeup;
qcom_geni_serial_setup_tx(uport, chunk); if (!port->tx_remaining) {
qcom_geni_serial_setup_tx(uport, pending);
port->tx_remaining = pending;
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
writel_relaxed(irq_en | M_TX_FIFO_WATERMARK_EN,
uport->membase + SE_GENI_M_IRQ_EN);
}
remaining = chunk; remaining = chunk;
for (i = 0; i < chunk; ) { for (i = 0; i < chunk; ) {
@ -737,21 +774,38 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport)
memset(buf, 0, ARRAY_SIZE(buf)); memset(buf, 0, ARRAY_SIZE(buf));
tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw); tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw);
for (c = 0; c < tx_bytes ; c++)
buf[c] = xmit->buf[tail + c]; for (c = 0; c < tx_bytes ; c++) {
buf[c] = xmit->buf[tail++];
tail &= UART_XMIT_SIZE - 1;
}
iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);
i += tx_bytes; i += tx_bytes;
tail += tx_bytes;
uport->icount.tx += tx_bytes; uport->icount.tx += tx_bytes;
remaining -= tx_bytes; remaining -= tx_bytes;
port->tx_remaining -= tx_bytes;
} }
xmit->tail = tail & (UART_XMIT_SIZE - 1); xmit->tail = tail;
if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport); /*
* The tx fifo watermark is level triggered and latched. Though we had
* cleared it in qcom_geni_serial_isr it will have already reasserted
* so we must clear it again here after our writes.
*/
writel_relaxed(M_TX_FIFO_WATERMARK_EN,
uport->membase + SE_GENI_M_IRQ_CLEAR);
out_write_wakeup: out_write_wakeup:
if (!port->tx_remaining) {
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
if (irq_en & M_TX_FIFO_WATERMARK_EN)
writel_relaxed(irq_en & ~M_TX_FIFO_WATERMARK_EN,
uport->membase + SE_GENI_M_IRQ_EN);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport); uart_write_wakeup(uport);
} }
@ -760,6 +814,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
{ {
unsigned int m_irq_status; unsigned int m_irq_status;
unsigned int s_irq_status; unsigned int s_irq_status;
unsigned int geni_status;
struct uart_port *uport = dev; struct uart_port *uport = dev;
unsigned long flags; unsigned long flags;
unsigned int m_irq_en; unsigned int m_irq_en;
@ -773,6 +828,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
spin_lock_irqsave(&uport->lock, flags); spin_lock_irqsave(&uport->lock, flags);
m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS);
m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
@ -785,9 +841,9 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
tty_insert_flip_char(tport, 0, TTY_OVERRUN); tty_insert_flip_char(tport, 0, TTY_OVERRUN);
} }
if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) && if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN,
qcom_geni_serial_handle_tx(uport); geni_status & M_GENI_CMD_ACTIVE);
if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) { if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) {
if (s_irq_status & S_GP_IRQ_0_EN) if (s_irq_status & S_GP_IRQ_0_EN)
@ -804,7 +860,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
qcom_geni_serial_handle_rx(uport, drop_rx); qcom_geni_serial_handle_rx(uport, drop_rx);
out_unlock: out_unlock:
spin_unlock_irqrestore(&uport->lock, flags); uart_unlock_and_check_sysrq(uport, flags);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -853,11 +910,13 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
u32 proto; u32 proto;
if (uart_console(uport)) if (uart_console(uport)) {
port->tx_bytes_pw = 1; port->tx_bytes_pw = 1;
else port->rx_bytes_pw = CONSOLE_RX_BYTES_PW;
} else {
port->tx_bytes_pw = 4; port->tx_bytes_pw = 4;
port->rx_bytes_pw = RX_BYTES_PW; port->rx_bytes_pw = 4;
}
proto = geni_se_read_proto(&port->se); proto = geni_se_read_proto(&port->se);
if (proto != GENI_SE_UART) { if (proto != GENI_SE_UART) {
@ -1322,49 +1381,25 @@ static int qcom_geni_serial_remove(struct platform_device *pdev)
return 0; return 0;
} }
static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev)
{ {
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport; struct uart_port *uport = &port->uport;
if (uart_console(uport)) { return uart_suspend_port(uport->private_data, uport);
uart_suspend_port(uport->private_data, uport);
} else {
struct uart_state *state = uport->state;
/*
* If the port is open, deny system suspend.
*/
if (state->pm_state == UART_PM_STATE_ON)
return -EBUSY;
}
return 0;
} }
static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev)
{ {
struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport; struct uart_port *uport = &port->uport;
if (uart_console(uport) && return uart_resume_port(uport->private_data, uport);
console_suspend_enabled && uport->suspended) {
uart_resume_port(uport->private_data, uport);
/*
* uart_suspend_port() invokes port shutdown which in turn
* frees the irq. uart_resume_port invokes port startup which
* performs request_irq. The request_irq auto-enables the IRQ.
* In addition, resume_noirq implicitly enables the IRQ and
* leads to an unbalanced IRQ enable warning. Disable the IRQ
* before returning so that the warning is suppressed.
*/
disable_irq(uport->irq);
}
return 0;
} }
static const struct dev_pm_ops qcom_geni_serial_pm_ops = { static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq, SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend,
qcom_geni_serial_sys_resume_noirq) qcom_geni_serial_sys_resume)
}; };
static const struct of_device_id qcom_geni_serial_match_table[] = { static const struct of_device_id qcom_geni_serial_match_table[] = {

View File

@ -1287,7 +1287,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
* Ask the core to calculate the divisor for us. * Ask the core to calculate the divisor for us.
*/ */
baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); baud = uart_get_baud_rate(port, termios, old, 0, 3000000);
quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel); quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor; quot = port->custom_divisor;
@ -1365,11 +1365,14 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot); wr_regl(port, S3C2410_UBRDIV, quot);
port->status &= ~UPSTAT_AUTOCTS;
umcon = rd_regl(port, S3C2410_UMCON); umcon = rd_regl(port, S3C2410_UMCON);
if (termios->c_cflag & CRTSCTS) { if (termios->c_cflag & CRTSCTS) {
umcon |= S3C2410_UMCOM_AFC; umcon |= S3C2410_UMCOM_AFC;
/* Disable RTS when RX FIFO contains 63 bytes */ /* Disable RTS when RX FIFO contains 63 bytes */
umcon &= ~S3C2412_UMCON_AFC_8; umcon &= ~S3C2412_UMCON_AFC_8;
port->status = UPSTAT_AUTOCTS;
} else { } else {
umcon &= ~S3C2410_UMCOM_AFC; umcon &= ~S3C2410_UMCOM_AFC;
} }

View File

@ -12,6 +12,7 @@
#endif #endif
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
@ -47,7 +48,6 @@
# define MR2_STOP1 (7 << 0) # define MR2_STOP1 (7 << 0)
# define MR2_STOP2 (0xf << 0) # define MR2_STOP2 (0xf << 0)
#define SCCNXP_SR_REG (0x01) #define SCCNXP_SR_REG (0x01)
#define SCCNXP_CSR_REG SCCNXP_SR_REG
# define SR_RXRDY (1 << 0) # define SR_RXRDY (1 << 0)
# define SR_FULL (1 << 1) # define SR_FULL (1 << 1)
# define SR_TXRDY (1 << 2) # define SR_TXRDY (1 << 2)
@ -56,6 +56,8 @@
# define SR_PE (1 << 5) # define SR_PE (1 << 5)
# define SR_FE (1 << 6) # define SR_FE (1 << 6)
# define SR_BRK (1 << 7) # define SR_BRK (1 << 7)
#define SCCNXP_CSR_REG (SCCNXP_SR_REG)
# define CSR_TIMER_MODE (0x0d)
#define SCCNXP_CR_REG (0x02) #define SCCNXP_CR_REG (0x02)
# define CR_RX_ENABLE (1 << 0) # define CR_RX_ENABLE (1 << 0)
# define CR_RX_DISABLE (1 << 1) # define CR_RX_DISABLE (1 << 1)
@ -82,9 +84,12 @@
# define IMR_RXRDY (1 << 1) # define IMR_RXRDY (1 << 1)
# define ISR_TXRDY(x) (1 << ((x * 4) + 0)) # define ISR_TXRDY(x) (1 << ((x * 4) + 0))
# define ISR_RXRDY(x) (1 << ((x * 4) + 1)) # define ISR_RXRDY(x) (1 << ((x * 4) + 1))
#define SCCNXP_CTPU_REG (0x06)
#define SCCNXP_CTPL_REG (0x07)
#define SCCNXP_IPR_REG (0x0d) #define SCCNXP_IPR_REG (0x0d)
#define SCCNXP_OPCR_REG SCCNXP_IPR_REG #define SCCNXP_OPCR_REG SCCNXP_IPR_REG
#define SCCNXP_SOP_REG (0x0e) #define SCCNXP_SOP_REG (0x0e)
#define SCCNXP_START_COUNTER_REG SCCNXP_SOP_REG
#define SCCNXP_ROP_REG (0x0f) #define SCCNXP_ROP_REG (0x0f)
/* Route helpers */ /* Route helpers */
@ -103,6 +108,8 @@ struct sccnxp_chip {
unsigned long freq_max; unsigned long freq_max;
unsigned int flags; unsigned int flags;
unsigned int fifosize; unsigned int fifosize;
/* Time between read/write cycles */
unsigned int trwd;
}; };
struct sccnxp_port { struct sccnxp_port {
@ -137,6 +144,7 @@ static const struct sccnxp_chip sc2681 = {
.freq_max = 4000000, .freq_max = 4000000,
.flags = SCCNXP_HAVE_IO, .flags = SCCNXP_HAVE_IO,
.fifosize = 3, .fifosize = 3,
.trwd = 200,
}; };
static const struct sccnxp_chip sc2691 = { static const struct sccnxp_chip sc2691 = {
@ -147,6 +155,7 @@ static const struct sccnxp_chip sc2691 = {
.freq_max = 4000000, .freq_max = 4000000,
.flags = 0, .flags = 0,
.fifosize = 3, .fifosize = 3,
.trwd = 150,
}; };
static const struct sccnxp_chip sc2692 = { static const struct sccnxp_chip sc2692 = {
@ -157,6 +166,7 @@ static const struct sccnxp_chip sc2692 = {
.freq_max = 4000000, .freq_max = 4000000,
.flags = SCCNXP_HAVE_IO, .flags = SCCNXP_HAVE_IO,
.fifosize = 3, .fifosize = 3,
.trwd = 30,
}; };
static const struct sccnxp_chip sc2891 = { static const struct sccnxp_chip sc2891 = {
@ -167,6 +177,7 @@ static const struct sccnxp_chip sc2891 = {
.freq_max = 8000000, .freq_max = 8000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 16, .fifosize = 16,
.trwd = 27,
}; };
static const struct sccnxp_chip sc2892 = { static const struct sccnxp_chip sc2892 = {
@ -177,6 +188,7 @@ static const struct sccnxp_chip sc2892 = {
.freq_max = 8000000, .freq_max = 8000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 16, .fifosize = 16,
.trwd = 17,
}; };
static const struct sccnxp_chip sc28202 = { static const struct sccnxp_chip sc28202 = {
@ -187,6 +199,7 @@ static const struct sccnxp_chip sc28202 = {
.freq_max = 50000000, .freq_max = 50000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 256, .fifosize = 256,
.trwd = 10,
}; };
static const struct sccnxp_chip sc68681 = { static const struct sccnxp_chip sc68681 = {
@ -197,6 +210,7 @@ static const struct sccnxp_chip sc68681 = {
.freq_max = 4000000, .freq_max = 4000000,
.flags = SCCNXP_HAVE_IO, .flags = SCCNXP_HAVE_IO,
.fifosize = 3, .fifosize = 3,
.trwd = 200,
}; };
static const struct sccnxp_chip sc68692 = { static const struct sccnxp_chip sc68692 = {
@ -207,24 +221,36 @@ static const struct sccnxp_chip sc68692 = {
.freq_max = 4000000, .freq_max = 4000000,
.flags = SCCNXP_HAVE_IO, .flags = SCCNXP_HAVE_IO,
.fifosize = 3, .fifosize = 3,
.trwd = 200,
}; };
static inline u8 sccnxp_read(struct uart_port *port, u8 reg) static u8 sccnxp_read(struct uart_port *port, u8 reg)
{ {
return readb(port->membase + (reg << port->regshift)); struct sccnxp_port *s = dev_get_drvdata(port->dev);
u8 ret;
ret = readb(port->membase + (reg << port->regshift));
ndelay(s->chip->trwd);
return ret;
} }
static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) static void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
{ {
struct sccnxp_port *s = dev_get_drvdata(port->dev);
writeb(v, port->membase + (reg << port->regshift)); writeb(v, port->membase + (reg << port->regshift));
ndelay(s->chip->trwd);
} }
static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) static u8 sccnxp_port_read(struct uart_port *port, u8 reg)
{ {
return sccnxp_read(port, (port->line << 3) + reg); return sccnxp_read(port, (port->line << 3) + reg);
} }
static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
{ {
sccnxp_write(port, (port->line << 3) + reg, v); sccnxp_write(port, (port->line << 3) + reg, v);
} }
@ -233,7 +259,7 @@ static int sccnxp_update_best_err(int a, int b, int *besterr)
{ {
int err = abs(a - b); int err = abs(a - b);
if ((*besterr < 0) || (*besterr > err)) { if (*besterr > err) {
*besterr = err; *besterr = err;
return 0; return 0;
} }
@ -281,10 +307,22 @@ static const struct {
static int sccnxp_set_baud(struct uart_port *port, int baud) static int sccnxp_set_baud(struct uart_port *port, int baud)
{ {
struct sccnxp_port *s = dev_get_drvdata(port->dev); struct sccnxp_port *s = dev_get_drvdata(port->dev);
int div_std, tmp_baud, bestbaud = baud, besterr = -1; int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;
struct sccnxp_chip *chip = s->chip; struct sccnxp_chip *chip = s->chip;
u8 i, acr = 0, csr = 0, mr0 = 0; u8 i, acr = 0, csr = 0, mr0 = 0;
/* Find divisor to load to the timer preset registers */
div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud);
if ((div_std >= 2) && (div_std <= 0xffff)) {
bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std);
sccnxp_update_best_err(baud, bestbaud, &besterr);
csr = CSR_TIMER_MODE;
sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8);
sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std);
/* Issue start timer/counter command */
sccnxp_port_read(port, SCCNXP_START_COUNTER_REG);
}
/* Find best baud from table */ /* Find best baud from table */
for (i = 0; baud_std[i].baud && besterr; i++) { for (i = 0; baud_std[i].baud && besterr; i++) {
if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0)) if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))

View File

@ -746,7 +746,7 @@ static void tegra_uart_stop_rx(struct uart_port *u)
if (!tup->rx_in_progress) if (!tup->rx_in_progress)
return; return;
tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ tegra_uart_wait_sym_time(tup, 1); /* wait one character interval */
ier = tup->ier_shadow; ier = tup->ier_shadow;
ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE |
@ -887,7 +887,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
* *
* EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when
* the DATA is sitting in the FIFO and couldn't be transferred to the * the DATA is sitting in the FIFO and couldn't be transferred to the
* DMA as the DMA size alignment(4 bytes) is not met. EORD will be * DMA as the DMA size alignment (4 bytes) is not met. EORD will be
* triggered when there is a pause of the incomming data stream for 4 * triggered when there is a pause of the incomming data stream for 4
* characters long. * characters long.
* *
@ -1079,7 +1079,7 @@ static void tegra_uart_set_termios(struct uart_port *u,
if (tup->rts_active) if (tup->rts_active)
set_rts(tup, false); set_rts(tup, false);
/* Clear all interrupts as configuration is going to be change */ /* Clear all interrupts as configuration is going to be changed */
tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER); tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER);
tegra_uart_read(tup, UART_IER); tegra_uart_read(tup, UART_IER);
tegra_uart_write(tup, 0, UART_IER); tegra_uart_write(tup, 0, UART_IER);
@ -1165,10 +1165,10 @@ static void tegra_uart_set_termios(struct uart_port *u,
/* update the port timeout based on new settings */ /* update the port timeout based on new settings */
uart_update_timeout(u, termios->c_cflag, baud); uart_update_timeout(u, termios->c_cflag, baud);
/* Make sure all write has completed */ /* Make sure all writes have completed */
tegra_uart_read(tup, UART_IER); tegra_uart_read(tup, UART_IER);
/* Reenable interrupt */ /* Re-enable interrupt */
tegra_uart_write(tup, tup->ier_shadow, UART_IER); tegra_uart_write(tup, tup->ier_shadow, UART_IER);
tegra_uart_read(tup, UART_IER); tegra_uart_read(tup, UART_IER);

View File

@ -205,10 +205,15 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
if (!state->xmit.buf) { if (!state->xmit.buf) {
state->xmit.buf = (unsigned char *) page; state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit); uart_circ_clear(&state->xmit);
uart_port_unlock(uport, flags);
} else { } else {
uart_port_unlock(uport, flags);
/*
* Do not free() the page under the port lock, see
* uart_shutdown().
*/
free_page(page); free_page(page);
} }
uart_port_unlock(uport, flags);
retval = uport->ops->startup(uport); retval = uport->ops->startup(uport);
if (retval == 0) { if (retval == 0) {
@ -268,6 +273,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
struct uart_port *uport = uart_port_check(state); struct uart_port *uport = uart_port_check(state);
struct tty_port *port = &state->port; struct tty_port *port = &state->port;
unsigned long flags = 0; unsigned long flags = 0;
char *xmit_buf = NULL;
/* /*
* Set the TTY IO error marker * Set the TTY IO error marker
@ -298,14 +304,18 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
tty_port_set_suspended(port, 0); tty_port_set_suspended(port, 0);
/* /*
* Free the transmit buffer page. * Do not free() the transmit buffer page under the port lock since
* this can create various circular locking scenarios. For instance,
* console driver may need to allocate/free a debug object, which
* can endup in printk() recursion.
*/ */
uart_port_lock(state, flags); uart_port_lock(state, flags);
if (state->xmit.buf) { xmit_buf = state->xmit.buf;
free_page((unsigned long)state->xmit.buf); state->xmit.buf = NULL;
state->xmit.buf = NULL;
}
uart_port_unlock(uport, flags); uart_port_unlock(uport, flags);
if (xmit_buf)
free_page((unsigned long)xmit_buf);
} }
/** /**

View File

@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s)
dma_release_channel(chan); dma_release_channel(chan);
} }
static void sci_submit_rx(struct sci_port *s) static int sci_submit_rx(struct sci_port *s, bool port_lock_held)
{ {
struct dma_chan *chan = s->chan_rx; struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port; struct uart_port *port = &s->port;
@ -1359,19 +1359,22 @@ static void sci_submit_rx(struct sci_port *s)
s->active_rx = s->cookie_rx[0]; s->active_rx = s->cookie_rx[0];
dma_async_issue_pending(chan); dma_async_issue_pending(chan);
return; return 0;
fail: fail:
/* Switch to PIO */
if (!port_lock_held)
spin_lock_irqsave(&port->lock, flags);
if (i) if (i)
dmaengine_terminate_async(chan); dmaengine_terminate_async(chan);
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
s->cookie_rx[i] = -EINVAL; s->cookie_rx[i] = -EINVAL;
s->active_rx = -EINVAL; s->active_rx = 0;
/* Switch to PIO */
spin_lock_irqsave(&port->lock, flags);
s->chan_rx = NULL; s->chan_rx = NULL;
sci_start_rx(port); sci_start_rx(port);
spin_unlock_irqrestore(&port->lock, flags); if (!port_lock_held)
spin_unlock_irqrestore(&port->lock, flags);
return -EAGAIN;
} }
static void work_fn_tx(struct work_struct *work) static void work_fn_tx(struct work_struct *work)
@ -1491,7 +1494,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
} }
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s); sci_submit_rx(s, true);
/* Direct new serial port interrupts back to CPU */ /* Direct new serial port interrupts back to CPU */
scr = serial_port_in(port, SCSCR); scr = serial_port_in(port, SCSCR);
@ -1617,7 +1620,7 @@ static void sci_request_dma(struct uart_port *port)
s->chan_rx_saved = s->chan_rx = chan; s->chan_rx_saved = s->chan_rx = chan;
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s); sci_submit_rx(s, false);
} }
} }
@ -1666,8 +1669,10 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
disable_irq_nosync(irq); disable_irq_nosync(irq);
scr |= SCSCR_RDRQE; scr |= SCSCR_RDRQE;
} else { } else {
if (sci_submit_rx(s, false) < 0)
goto handle_pio;
scr &= ~SCSCR_RIE; scr &= ~SCSCR_RIE;
sci_submit_rx(s);
} }
serial_port_out(port, SCSCR, scr); serial_port_out(port, SCSCR, scr);
/* Clear current interrupt */ /* Clear current interrupt */
@ -1679,6 +1684,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
handle_pio:
#endif #endif
if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) { if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) {
@ -1693,7 +1700,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
* of whether the I_IXOFF is set, otherwise, how is the interrupt * of whether the I_IXOFF is set, otherwise, how is the interrupt
* to be disabled? * to be disabled?
*/ */
sci_receive_chars(ptr); sci_receive_chars(port);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -1749,7 +1756,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
} else { } else {
sci_handle_fifo_overrun(port); sci_handle_fifo_overrun(port);
if (!s->chan_rx) if (!s->chan_rx)
sci_receive_chars(ptr); sci_receive_chars(port);
} }
sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));

View File

@ -89,14 +89,14 @@ void sunserial_console_termios(struct console *con, struct device_node *uart_dp)
int baud, bits, stop, cflag; int baud, bits, stop, cflag;
char parity; char parity;
if (!strcmp(uart_dp->name, "rsc") || if (of_node_name_eq(uart_dp, "rsc") ||
!strcmp(uart_dp->name, "rsc-console") || of_node_name_eq(uart_dp, "rsc-console") ||
!strcmp(uart_dp->name, "rsc-control")) { of_node_name_eq(uart_dp, "rsc-control")) {
mode = of_get_property(uart_dp, mode = of_get_property(uart_dp,
"ssp-console-modes", NULL); "ssp-console-modes", NULL);
if (!mode) if (!mode)
mode = "115200,8,n,1,-"; mode = "115200,8,n,1,-";
} else if (!strcmp(uart_dp->name, "lom-console")) { } else if (of_node_name_eq(uart_dp, "lom-console")) {
mode = "9600,8,n,1,-"; mode = "9600,8,n,1,-";
} else { } else {
struct device_node *dp; struct device_node *dp;

View File

@ -1503,8 +1503,8 @@ static int su_probe(struct platform_device *op)
up->port.ops = &sunsu_pops; up->port.ops = &sunsu_pops;
ignore_line = false; ignore_line = false;
if (!strcmp(dp->name, "rsc-console") || if (of_node_name_eq(dp, "rsc-console") ||
!strcmp(dp->name, "lom-console")) of_node_name_eq(dp, "lom-console"))
ignore_line = true; ignore_line = true;
sunserial_console_match(SUNSU_CONSOLE(), dp, sunserial_console_match(SUNSU_CONSOLE(), dp,

View File

@ -22,6 +22,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/pm_runtime.h>
#define ULITE_NAME "ttyUL" #define ULITE_NAME "ttyUL"
#define ULITE_MAJOR 204 #define ULITE_MAJOR 204
@ -54,6 +55,7 @@
#define ULITE_CONTROL_RST_TX 0x01 #define ULITE_CONTROL_RST_TX 0x01
#define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_RST_RX 0x02
#define ULITE_CONTROL_IE 0x10 #define ULITE_CONTROL_IE 0x10
#define UART_AUTOSUSPEND_TIMEOUT 3000
/* Static pointer to console port */ /* Static pointer to console port */
#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
@ -63,6 +65,7 @@ static struct uart_port *console_port;
struct uartlite_data { struct uartlite_data {
const struct uartlite_reg_ops *reg_ops; const struct uartlite_reg_ops *reg_ops;
struct clk *clk; struct clk *clk;
struct uart_driver *ulite_uart_driver;
}; };
struct uartlite_reg_ops { struct uartlite_reg_ops {
@ -390,12 +393,12 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
static void ulite_pm(struct uart_port *port, unsigned int state, static void ulite_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate) unsigned int oldstate)
{ {
struct uartlite_data *pdata = port->private_data; if (!state) {
pm_runtime_get_sync(port->dev);
if (!state) } else {
clk_enable(pdata->clk); pm_runtime_mark_last_busy(port->dev);
else pm_runtime_put_autosuspend(port->dev);
clk_disable(pdata->clk); }
} }
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
@ -694,7 +697,9 @@ static int ulite_release(struct device *dev)
int rc = 0; int rc = 0;
if (port) { if (port) {
rc = uart_remove_one_port(&ulite_uart_driver, port); struct uartlite_data *pdata = port->private_data;
rc = uart_remove_one_port(pdata->ulite_uart_driver, port);
dev_set_drvdata(dev, NULL); dev_set_drvdata(dev, NULL);
port->mapbase = 0; port->mapbase = 0;
} }
@ -712,8 +717,11 @@ static int __maybe_unused ulite_suspend(struct device *dev)
{ {
struct uart_port *port = dev_get_drvdata(dev); struct uart_port *port = dev_get_drvdata(dev);
if (port) if (port) {
uart_suspend_port(&ulite_uart_driver, port); struct uartlite_data *pdata = port->private_data;
uart_suspend_port(pdata->ulite_uart_driver, port);
}
return 0; return 0;
} }
@ -728,17 +736,41 @@ static int __maybe_unused ulite_resume(struct device *dev)
{ {
struct uart_port *port = dev_get_drvdata(dev); struct uart_port *port = dev_get_drvdata(dev);
if (port) if (port) {
uart_resume_port(&ulite_uart_driver, port); struct uartlite_data *pdata = port->private_data;
uart_resume_port(pdata->ulite_uart_driver, port);
}
return 0; return 0;
} }
static int __maybe_unused ulite_runtime_suspend(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
struct uartlite_data *pdata = port->private_data;
clk_disable(pdata->clk);
return 0;
};
static int __maybe_unused ulite_runtime_resume(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
struct uartlite_data *pdata = port->private_data;
clk_enable(pdata->clk);
return 0;
}
/* --------------------------------------------------------------------- /* ---------------------------------------------------------------------
* Platform bus binding * Platform bus binding
*/ */
static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume); static const struct dev_pm_ops ulite_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ulite_suspend, ulite_resume)
SET_RUNTIME_PM_OPS(ulite_runtime_suspend,
ulite_runtime_resume, NULL)
};
#if defined(CONFIG_OF) #if defined(CONFIG_OF)
/* Match table for of_platform binding */ /* Match table for of_platform binding */
@ -763,6 +795,22 @@ static int ulite_probe(struct platform_device *pdev)
if (prop) if (prop)
id = be32_to_cpup(prop); id = be32_to_cpup(prop);
#endif #endif
if (id < 0) {
/* Look for a serialN alias */
id = of_alias_get_id(pdev->dev.of_node, "serial");
if (id < 0)
id = 0;
}
if (!ulite_uart_driver.state) {
dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n");
ret = uart_register_driver(&ulite_uart_driver);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register driver\n");
return ret;
}
}
pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data), pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data),
GFP_KERNEL); GFP_KERNEL);
if (!pdata) if (!pdata)
@ -788,24 +836,22 @@ static int ulite_probe(struct platform_device *pdev)
pdata->clk = NULL; pdata->clk = NULL;
} }
pdata->ulite_uart_driver = &ulite_uart_driver;
ret = clk_prepare_enable(pdata->clk); ret = clk_prepare_enable(pdata->clk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to prepare clock\n"); dev_err(&pdev->dev, "Failed to prepare clock\n");
return ret; return ret;
} }
if (!ulite_uart_driver.state) { pm_runtime_use_autosuspend(&pdev->dev);
dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n"); pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
ret = uart_register_driver(&ulite_uart_driver); pm_runtime_set_active(&pdev->dev);
if (ret < 0) { pm_runtime_enable(&pdev->dev);
dev_err(&pdev->dev, "Failed to register driver\n");
return ret;
}
}
ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata);
clk_disable(pdata->clk); pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return ret; return ret;
} }
@ -814,9 +860,14 @@ static int ulite_remove(struct platform_device *pdev)
{ {
struct uart_port *port = dev_get_drvdata(&pdev->dev); struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct uartlite_data *pdata = port->private_data; struct uartlite_data *pdata = port->private_data;
int rc;
clk_disable_unprepare(pdata->clk); clk_unprepare(pdata->clk);
return ulite_release(&pdev->dev); rc = ulite_release(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
return rc;
} }
/* work with hotplug and coldplug */ /* work with hotplug and coldplug */

View File

@ -123,7 +123,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ #define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */
#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ #define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */
#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ #define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */
#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ #define CDNS_UART_IXR_RXMASK 0x000021e7 /* Valid RX bit mask */
/* /*
* Do not enable parity error interrupt for the following * Do not enable parity error interrupt for the following
@ -364,7 +364,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id)
cdns_uart_handle_tx(dev_id); cdns_uart_handle_tx(dev_id);
isrstatus &= ~CDNS_UART_IXR_TXEMPTY; isrstatus &= ~CDNS_UART_IXR_TXEMPTY;
} }
if (isrstatus & CDNS_UART_IXR_MASK) if (isrstatus & CDNS_UART_IXR_RXMASK)
cdns_uart_handle_rx(dev_id, isrstatus); cdns_uart_handle_rx(dev_id, isrstatus);
spin_unlock(&port->lock); spin_unlock(&port->lock);
@ -1255,7 +1255,7 @@ static int cdns_uart_suspend(struct device *device)
may_wake = device_may_wakeup(device); may_wake = device_may_wakeup(device);
if (console_suspend_enabled && may_wake) { if (console_suspend_enabled && uart_console(port) && may_wake) {
unsigned long flags = 0; unsigned long flags = 0;
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
@ -1293,7 +1293,7 @@ static int cdns_uart_resume(struct device *device)
may_wake = device_may_wakeup(device); may_wake = device_may_wakeup(device);
if (console_suspend_enabled && !may_wake) { if (console_suspend_enabled && uart_console(port) && !may_wake) {
clk_enable(cdns_uart->pclk); clk_enable(cdns_uart->pclk);
clk_enable(cdns_uart->uartclk); clk_enable(cdns_uart->uartclk);
@ -1508,8 +1508,10 @@ static int cdns_uart_probe(struct platform_device *pdev)
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console), cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console),
GFP_KERNEL); GFP_KERNEL);
if (!cdns_uart_console) if (!cdns_uart_console) {
return -ENOMEM; rc = -ENOMEM;
goto err_out_id;
}
strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME, strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME,
sizeof(cdns_uart_console->name)); sizeof(cdns_uart_console->name));
@ -1624,6 +1626,7 @@ static int cdns_uart_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
pm_runtime_set_active(&pdev->dev); pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
device_init_wakeup(port->dev, true);
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/* /*
@ -1702,6 +1705,7 @@ static int cdns_uart_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev); pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
if (console_port == port) if (console_port == port)
@ -1719,6 +1723,7 @@ static struct platform_driver cdns_uart_platform_driver = {
.name = CDNS_UART_NAME, .name = CDNS_UART_NAME,
.of_match_table = cdns_uart_of_match, .of_match_table = cdns_uart_of_match,
.pm = &cdns_uart_dev_pm_ops, .pm = &cdns_uart_dev_pm_ops,
.suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_XILINX_PS_UART),
}, },
}; };

View File

@ -134,17 +134,10 @@ static struct sysrq_key_op sysrq_unraw_op = {
static void sysrq_handle_crash(int key) static void sysrq_handle_crash(int key)
{ {
char *killer = NULL; /* release the RCU read lock before crashing */
/* we need to release the RCU read lock here,
* otherwise we get an annoying
* 'BUG: sleeping function called from invalid context'
* complaint from the kernel before the panic.
*/
rcu_read_unlock(); rcu_read_unlock();
panic_on_oops = 1; /* force panic */
wmb(); panic("sysrq triggered crash\n");
*killer = 1;
} }
static struct sysrq_key_op sysrq_crash_op = { static struct sysrq_key_op sysrq_crash_op = {
.handler = sysrq_handle_crash, .handler = sysrq_handle_crash,
@ -660,8 +653,7 @@ static void sysrq_do_reset(struct timer_list *t)
state->reset_requested = true; state->reset_requested = true;
ksys_sync(); orderly_reboot();
kernel_restart(NULL);
} }
static void sysrq_handle_reset_request(struct sysrq_state *state) static void sysrq_handle_reset_request(struct sysrq_state *state)
@ -736,6 +728,8 @@ static void sysrq_of_get_keyreset_config(void)
/* Get reset timeout if any. */ /* Get reset timeout if any. */
of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms); of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms);
of_node_put(np);
} }
#else #else
static void sysrq_of_get_keyreset_config(void) static void sysrq_of_get_keyreset_config(void)

View File

@ -1268,14 +1268,16 @@ static int tty_reopen(struct tty_struct *tty)
if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
return -EBUSY; return -EBUSY;
tty->count++; retval = tty_ldisc_lock(tty, 5 * HZ);
if (tty->ldisc)
return 0;
retval = tty_ldisc_reinit(tty, tty->termios.c_line);
if (retval) if (retval)
tty->count--; return retval;
if (!tty->ldisc)
retval = tty_ldisc_reinit(tty, tty->termios.c_line);
tty_ldisc_unlock(tty);
if (retval == 0)
tty->count++;
return retval; return retval;
} }

View File

@ -327,6 +327,11 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
{ {
int ret; int ret;
/* Kindly asking blocked readers to release the read side */
set_bit(TTY_LDISC_CHANGING, &tty->flags);
wake_up_interruptible_all(&tty->read_wait);
wake_up_interruptible_all(&tty->write_wait);
ret = __tty_ldisc_lock(tty, timeout); ret = __tty_ldisc_lock(tty, timeout);
if (!ret) if (!ret)
return -EBUSY; return -EBUSY;
@ -337,6 +342,8 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
void tty_ldisc_unlock(struct tty_struct *tty) void tty_ldisc_unlock(struct tty_struct *tty)
{ {
clear_bit(TTY_LDISC_HALTED, &tty->flags); clear_bit(TTY_LDISC_HALTED, &tty->flags);
/* Can be cleared here - ldisc_unlock will wake up writers firstly */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
__tty_ldisc_unlock(tty); __tty_ldisc_unlock(tty);
} }
@ -471,6 +478,7 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
{ {
lockdep_assert_held_exclusive(&tty->ldisc_sem);
WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags)); WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
clear_bit(TTY_LDISC_OPEN, &tty->flags); clear_bit(TTY_LDISC_OPEN, &tty->flags);
if (ld->ops->close) if (ld->ops->close)
@ -492,6 +500,7 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld)
struct tty_ldisc *disc = tty_ldisc_get(tty, ld); struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
int r; int r;
lockdep_assert_held_exclusive(&tty->ldisc_sem);
if (IS_ERR(disc)) if (IS_ERR(disc))
return PTR_ERR(disc); return PTR_ERR(disc);
tty->ldisc = disc; tty->ldisc = disc;
@ -615,6 +624,7 @@ EXPORT_SYMBOL_GPL(tty_set_ldisc);
*/ */
static void tty_ldisc_kill(struct tty_struct *tty) static void tty_ldisc_kill(struct tty_struct *tty)
{ {
lockdep_assert_held_exclusive(&tty->ldisc_sem);
if (!tty->ldisc) if (!tty->ldisc)
return; return;
/* /*
@ -662,6 +672,7 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
struct tty_ldisc *ld; struct tty_ldisc *ld;
int retval; int retval;
lockdep_assert_held_exclusive(&tty->ldisc_sem);
ld = tty_ldisc_get(tty, disc); ld = tty_ldisc_get(tty, disc);
if (IS_ERR(ld)) { if (IS_ERR(ld)) {
BUG_ON(disc == N_TTY); BUG_ON(disc == N_TTY);
@ -760,6 +771,10 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
return retval; return retval;
if (o_tty) { if (o_tty) {
/*
* Called without o_tty->ldisc_sem held, as o_tty has been
* just allocated and no one has a reference to it.
*/
retval = tty_ldisc_open(o_tty, o_tty->ldisc); retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) { if (retval) {
tty_ldisc_close(tty, tty->ldisc); tty_ldisc_close(tty, tty->ldisc);
@ -825,6 +840,7 @@ int tty_ldisc_init(struct tty_struct *tty)
*/ */
void tty_ldisc_deinit(struct tty_struct *tty) void tty_ldisc_deinit(struct tty_struct *tty)
{ {
/* no ldisc_sem, tty is being destroyed */
if (tty->ldisc) if (tty->ldisc)
tty_ldisc_put(tty->ldisc); tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL; tty->ldisc = NULL;

View File

@ -34,29 +34,6 @@
#include <linux/sched/task.h> #include <linux/sched/task.h>
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define __acq(l, s, t, r, c, n, i) \
lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
# define __rel(l, n, i) \
lock_release(&(l)->dep_map, n, i)
#define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i)
#define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i)
#define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i)
#define lockdep_release(l, n, i) __rel(l, n, i)
#else
# define lockdep_acquire(l, s, t, i) do { } while (0)
# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0)
# define lockdep_acquire_read(l, s, t, i) do { } while (0)
# define lockdep_release(l, n, i) do { } while (0)
#endif
#ifdef CONFIG_LOCK_STAT
# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_)
#else
# define lock_stat(_lock, stat) do { } while (0)
#endif
#if BITS_PER_LONG == 64 #if BITS_PER_LONG == 64
# define LDSEM_ACTIVE_MASK 0xffffffffL # define LDSEM_ACTIVE_MASK 0xffffffffL
#else #else
@ -235,6 +212,7 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout)
raw_spin_lock_irq(&sem->wait_lock); raw_spin_lock_irq(&sem->wait_lock);
if (waiter.task) { if (waiter.task) {
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
sem->wait_readers--;
list_del(&waiter.list); list_del(&waiter.list);
raw_spin_unlock_irq(&sem->wait_lock); raw_spin_unlock_irq(&sem->wait_lock);
put_task_struct(waiter.task); put_task_struct(waiter.task);
@ -293,6 +271,16 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
if (!locked) if (!locked)
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
list_del(&waiter.list); list_del(&waiter.list);
/*
* In case of timeout, wake up every reader who gave the right of way
* to writer. Prevent separation readers into two groups:
* one that helds semaphore and another that sleeps.
* (in case of no contention with a writer)
*/
if (!locked && list_empty(&sem->write_wait))
__ldsem_wake_readers(sem);
raw_spin_unlock_irq(&sem->wait_lock); raw_spin_unlock_irq(&sem->wait_lock);
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
@ -310,17 +298,17 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem,
{ {
long count; long count;
lockdep_acquire_read(sem, subclass, 0, _RET_IP_); rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_);
count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count); count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count);
if (count <= 0) { if (count <= 0) {
lock_stat(sem, contended); lock_contended(&sem->dep_map, _RET_IP_);
if (!down_read_failed(sem, count, timeout)) { if (!down_read_failed(sem, count, timeout)) {
lockdep_release(sem, 1, _RET_IP_); rwsem_release(&sem->dep_map, 1, _RET_IP_);
return 0; return 0;
} }
} }
lock_stat(sem, acquired); lock_acquired(&sem->dep_map, _RET_IP_);
return 1; return 1;
} }
@ -329,17 +317,17 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem,
{ {
long count; long count;
lockdep_acquire(sem, subclass, 0, _RET_IP_); rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count); count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count);
if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
lock_stat(sem, contended); lock_contended(&sem->dep_map, _RET_IP_);
if (!down_write_failed(sem, count, timeout)) { if (!down_write_failed(sem, count, timeout)) {
lockdep_release(sem, 1, _RET_IP_); rwsem_release(&sem->dep_map, 1, _RET_IP_);
return 0; return 0;
} }
} }
lock_stat(sem, acquired); lock_acquired(&sem->dep_map, _RET_IP_);
return 1; return 1;
} }
@ -362,8 +350,8 @@ int ldsem_down_read_trylock(struct ld_semaphore *sem)
while (count >= 0) { while (count >= 0) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) { if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) {
lockdep_acquire_read(sem, 0, 1, _RET_IP_); rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_);
lock_stat(sem, acquired); lock_acquired(&sem->dep_map, _RET_IP_);
return 1; return 1;
} }
} }
@ -388,8 +376,8 @@ int ldsem_down_write_trylock(struct ld_semaphore *sem)
while ((count & LDSEM_ACTIVE_MASK) == 0) { while ((count & LDSEM_ACTIVE_MASK) == 0) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) { if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) {
lockdep_acquire(sem, 0, 1, _RET_IP_); rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
lock_stat(sem, acquired); lock_acquired(&sem->dep_map, _RET_IP_);
return 1; return 1;
} }
} }
@ -403,7 +391,7 @@ void ldsem_up_read(struct ld_semaphore *sem)
{ {
long count; long count;
lockdep_release(sem, 1, _RET_IP_); rwsem_release(&sem->dep_map, 1, _RET_IP_);
count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count); count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count);
if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
@ -417,7 +405,7 @@ void ldsem_up_write(struct ld_semaphore *sem)
{ {
long count; long count;
lockdep_release(sem, 1, _RET_IP_); rwsem_release(&sem->dep_map, 1, _RET_IP_);
count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count); count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count);
if (count < 0) if (count < 0)

View File

@ -32,6 +32,7 @@ struct font_desc {
#define ACORN8x8_IDX 8 #define ACORN8x8_IDX 8
#define MINI4x6_IDX 9 #define MINI4x6_IDX 9
#define FONT6x10_IDX 10 #define FONT6x10_IDX 10
#define TER16x32_IDX 11
extern const struct font_desc font_vga_8x8, extern const struct font_desc font_vga_8x8,
font_vga_8x16, font_vga_8x16,
@ -43,7 +44,8 @@ extern const struct font_desc font_vga_8x8,
font_sun_12x22, font_sun_12x22,
font_acorn_8x8, font_acorn_8x8,
font_mini_4x6, font_mini_4x6,
font_6x10; font_6x10,
font_ter_16x32;
/* Find a font with a specific name */ /* Find a font with a specific name */

23
include/linux/lantiq.h Normal file
View File

@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_LANTIQ_H
#define __LINUX_LANTIQ_H
#ifdef CONFIG_LANTIQ
#include <lantiq_soc.h>
#else
#ifndef LTQ_EARLY_ASC
#define LTQ_EARLY_ASC 0
#endif
#ifndef CPHYSADDR
#define CPHYSADDR(a) 0
#endif
static inline struct clk *clk_get_fpi(void)
{
return NULL;
}
#endif /* CONFIG_LANTIQ */
#endif /* __LINUX_LANTIQ_H */

View File

@ -210,7 +210,7 @@ void serdev_device_wait_until_sent(struct serdev_device *, long);
int serdev_device_get_tiocm(struct serdev_device *); int serdev_device_get_tiocm(struct serdev_device *);
int serdev_device_set_tiocm(struct serdev_device *, int, int); int serdev_device_set_tiocm(struct serdev_device *, int, int);
void serdev_device_write_wakeup(struct serdev_device *); void serdev_device_write_wakeup(struct serdev_device *);
int serdev_device_write(struct serdev_device *, const unsigned char *, size_t, unsigned long); int serdev_device_write(struct serdev_device *, const unsigned char *, size_t, long);
void serdev_device_write_flush(struct serdev_device *); void serdev_device_write_flush(struct serdev_device *);
int serdev_device_write_room(struct serdev_device *); int serdev_device_write_room(struct serdev_device *);

View File

@ -134,6 +134,10 @@ struct uart_8250_port {
void (*dl_write)(struct uart_8250_port *, int); void (*dl_write)(struct uart_8250_port *, int);
struct uart_8250_em485 *em485; struct uart_8250_em485 *em485;
/* Serial port overrun backoff */
struct delayed_work overrun_backoff;
u32 overrun_backoff_time_ms;
}; };
static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up) static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)

View File

@ -22,6 +22,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/console.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/circ_buf.h> #include <linux/circ_buf.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
@ -175,6 +176,7 @@ struct uart_port {
struct console *cons; /* struct console, if any */ struct console *cons; /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ) #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq; /* sysrq timeout */ unsigned long sysrq; /* sysrq timeout */
unsigned int sysrq_ch; /* char for sysrq */
#endif #endif
/* flags must be updated while holding port mutex */ /* flags must be updated while holding port mutex */
@ -485,8 +487,42 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch)
} }
return 0; return 0;
} }
static inline int
uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch)
{
if (port->sysrq) {
if (ch && time_before(jiffies, port->sysrq)) {
port->sysrq_ch = ch;
port->sysrq = 0;
return 1;
}
port->sysrq = 0;
}
return 0;
}
static inline void
uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags)
{
int sysrq_ch;
sysrq_ch = port->sysrq_ch;
port->sysrq_ch = 0;
spin_unlock_irqrestore(&port->lock, irqflags);
if (sysrq_ch)
handle_sysrq(sysrq_ch);
}
#else #else
#define uart_handle_sysrq_char(port,ch) ({ (void)port; 0; }) static inline int
uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { return 0; }
static inline int
uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch) { return 0; }
static inline void
uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags)
{
spin_unlock_irqrestore(&port->lock, irqflags);
}
#endif #endif
/* /*

View File

@ -366,6 +366,7 @@ struct tty_file_private {
#define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */
#define TTY_HUPPED 18 /* Post driver->hangup() */ #define TTY_HUPPED 18 /* Post driver->hangup() */
#define TTY_HUPPING 19 /* Hangup in progress */ #define TTY_HUPPING 19 /* Hangup in progress */
#define TTY_LDISC_CHANGING 20 /* Change pending - non-block IO */
#define TTY_LDISC_HALTED 22 /* Line discipline is halted */ #define TTY_LDISC_HALTED 22 /* Line discipline is halted */
/* Values for tty->flow_change */ /* Values for tty->flow_change */
@ -383,6 +384,12 @@ static inline void tty_set_flow_change(struct tty_struct *tty, int val)
smp_mb(); smp_mb();
} }
static inline bool tty_io_nonblock(struct tty_struct *tty, struct file *file)
{
return file->f_flags & O_NONBLOCK ||
test_bit(TTY_LDISC_CHANGING, &tty->flags);
}
static inline bool tty_io_error(struct tty_struct *tty) static inline bool tty_io_error(struct tty_struct *tty)
{ {
return test_bit(TTY_IO_ERROR, &tty->flags); return test_bit(TTY_IO_ERROR, &tty->flags);

View File

@ -109,6 +109,15 @@ config FONT_SUN12x22
big letters (like the letters used in the SPARC PROM). If the big letters (like the letters used in the SPARC PROM). If the
standard font is unreadable for you, say Y, otherwise say N. standard font is unreadable for you, say Y, otherwise say N.
config FONT_TER16x32
bool "Terminus 16x32 font (not supported by all drivers)"
depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC)
help
Terminus Font is a clean, fixed width bitmap font, designed
for long (8 and more hours per day) work with computers.
This is the high resolution, large version for use with HiDPI screens.
If the standard font is unreadable for you, say Y, otherwise say N.
config FONT_AUTOSELECT config FONT_AUTOSELECT
def_bool y def_bool y
depends on !FONT_8x8 depends on !FONT_8x8
@ -121,6 +130,7 @@ config FONT_AUTOSELECT
depends on !FONT_SUN8x16 depends on !FONT_SUN8x16
depends on !FONT_SUN12x22 depends on !FONT_SUN12x22
depends on !FONT_10x18 depends on !FONT_10x18
depends on !FONT_TER16x32
select FONT_8x16 select FONT_8x16
endif # FONT_SUPPORT endif # FONT_SUPPORT

View File

@ -14,6 +14,7 @@ font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o
font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o
font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o
font-objs-$(CONFIG_FONT_6x10) += font_6x10.o font-objs-$(CONFIG_FONT_6x10) += font_6x10.o
font-objs-$(CONFIG_FONT_TER16x32) += font_ter16x32.o
font-objs += $(font-objs-y) font-objs += $(font-objs-y)

2072
lib/fonts/font_ter16x32.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -67,6 +67,10 @@ static const struct font_desc *fonts[] = {
#undef NO_FONTS #undef NO_FONTS
&font_6x10, &font_6x10,
#endif #endif
#ifdef CONFIG_FONT_TER16x32
#undef NO_FONTS
&font_ter_16x32,
#endif
}; };
#define num_fonts ARRAY_SIZE(fonts) #define num_fonts ARRAY_SIZE(fonts)