summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tty/Kconfig19
-rw-r--r--drivers/tty/ehv_bytechan.c4
-rw-r--r--drivers/tty/hvc/hvc_rtas.c2
-rw-r--r--drivers/tty/n_gsm.c402
-rw-r--r--drivers/tty/n_tty.c6
-rw-r--r--drivers/tty/serial/21285.c32
-rw-r--r--drivers/tty/serial/8250/8250_bcm7271.c14
-rw-r--r--drivers/tty/serial/8250/8250_core.c5
-rw-r--r--drivers/tty/serial/8250/8250_dma.c19
-rw-r--r--drivers/tty/serial/8250/8250_ingenic.c32
-rw-r--r--drivers/tty/serial/8250/8250_omap.c85
-rw-r--r--drivers/tty/serial/8250/8250_port.c3
-rw-r--r--drivers/tty/serial/Kconfig2
-rw-r--r--drivers/tty/serial/altera_jtaguart.c112
-rw-r--r--drivers/tty/serial/altera_uart.c34
-rw-r--r--drivers/tty/serial/amba-pl010.c32
-rw-r--r--drivers/tty/serial/amba-pl011.c17
-rw-r--r--drivers/tty/serial/apbuart.c34
-rw-r--r--drivers/tty/serial/ar933x_uart.c3
-rw-r--r--drivers/tty/serial/arc_uart.c3
-rw-r--r--drivers/tty/serial/atmel_serial.c80
-rw-r--r--drivers/tty/serial/bcm63xx_uart.c47
-rw-r--r--drivers/tty/serial/clps711x.c3
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c3
-rw-r--r--drivers/tty/serial/digicolor-usart.c3
-rw-r--r--drivers/tty/serial/dz.c3
-rw-r--r--drivers/tty/serial/fsl_linflexuart.c3
-rw-r--r--drivers/tty/serial/fsl_lpuart.c386
-rw-r--r--drivers/tty/serial/imx.c71
-rw-r--r--drivers/tty/serial/ip22zilog.c6
-rw-r--r--drivers/tty/serial/lantiq.c36
-rw-r--r--drivers/tty/serial/liteuart.c3
-rw-r--r--drivers/tty/serial/lpc32xx_hs.c33
-rw-r--r--drivers/tty/serial/max3100.c4
-rw-r--r--drivers/tty/serial/max310x.c5
-rw-r--r--drivers/tty/serial/mcf.c34
-rw-r--r--drivers/tty/serial/men_z135_uart.c5
-rw-r--r--drivers/tty/serial/meson_uart.c3
-rw-r--r--drivers/tty/serial/milbeaut_usio.c3
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c39
-rw-r--r--drivers/tty/serial/mps2-uart.c26
-rw-r--r--drivers/tty/serial/msm_serial.c13
-rw-r--r--drivers/tty/serial/mux.c45
-rw-r--r--drivers/tty/serial/mvebu-uart.c41
-rw-r--r--drivers/tty/serial/mxs-auart.c32
-rw-r--r--drivers/tty/serial/omap-serial.c32
-rw-r--r--drivers/tty/serial/owl-uart.c32
-rw-r--r--drivers/tty/serial/pch_uart.c16
-rw-r--r--drivers/tty/serial/pic32_uart.c3
-rw-r--r--drivers/tty/serial/pmac_zilog.c6
-rw-r--r--drivers/tty/serial/pxa.c33
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c35
-rw-r--r--drivers/tty/serial/rda-uart.c3
-rw-r--r--drivers/tty/serial/rp2.c31
-rw-r--r--drivers/tty/serial/sa1100.c34
-rw-r--r--drivers/tty/serial/samsung_tty.c10
-rw-r--r--drivers/tty/serial/sb1250-duart.c3
-rw-r--r--drivers/tty/serial/sc16is7xx.c5
-rw-r--r--drivers/tty/serial/sccnxp.c3
-rw-r--r--drivers/tty/serial/serial-tegra.c9
-rw-r--r--drivers/tty/serial/serial_txx9.c32
-rw-r--r--drivers/tty/serial/sh-sci.c5
-rw-r--r--drivers/tty/serial/sifive.c31
-rw-r--r--drivers/tty/serial/sprd_serial.c40
-rw-r--r--drivers/tty/serial/st-asc.c48
-rw-r--r--drivers/tty/serial/stm32-usart.c54
-rw-r--r--drivers/tty/serial/sunhv.c6
-rw-r--r--drivers/tty/serial/sunplus-uart.c4
-rw-r--r--drivers/tty/serial/sunsab.c14
-rw-r--r--drivers/tty/serial/sunsu.c3
-rw-r--r--drivers/tty/serial/sunzilog.c6
-rw-r--r--drivers/tty/serial/timbuart.c3
-rw-r--r--drivers/tty/serial/uartlite.c3
-rw-r--r--drivers/tty/serial/ucc_uart.c3
-rw-r--r--drivers/tty/serial/vt8500_serial.c32
-rw-r--r--drivers/tty/serial/xilinx_uartps.c4
-rw-r--r--drivers/tty/serial/zs.c3
-rw-r--r--drivers/tty/synclink_gt.c19
-rw-r--r--drivers/tty/tty.h2
-rw-r--r--drivers/tty/tty_buffer.c32
-rw-r--r--drivers/tty/tty_io.c45
-rw-r--r--drivers/tty/tty_ldisc.c38
82 files changed, 1286 insertions, 1183 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index cc30ff93e2e4..d35fc068da74 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -149,6 +149,25 @@ config LEGACY_PTY_COUNT
When not in use, each legacy PTY occupies 12 bytes on 32-bit
architectures and 24 bytes on 64-bit architectures.
+config LEGACY_TIOCSTI
+ bool "Allow legacy TIOCSTI usage"
+ default y
+ help
+ Historically the kernel has allowed TIOCSTI, which will push
+ characters into a controlling TTY. This continues to be used
+ as a malicious privilege escalation mechanism, and provides no
+ meaningful real-world utility any more. Its use is considered
+ a dangerous legacy operation, and can be disabled on most
+ systems.
+
+ Say 'Y here only if you have confirmed that your system's
+ userspace depends on this functionality to continue operating
+ normally.
+
+ This functionality can be changed at runtime with the
+ dev.tty.legacy_tiocsti sysctl. This configuration option sets
+ the default value of the sysctl.
+
config LDISC_AUTOLOAD
bool "Automatically load TTY Line Disciplines"
default y
diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c
index 19d32cb6af84..8595483f4697 100644
--- a/drivers/tty/ehv_bytechan.c
+++ b/drivers/tty/ehv_bytechan.c
@@ -118,7 +118,7 @@ static int find_console_handle(void)
return 0;
stdout_irq = irq_of_parse_and_map(np, 0);
- if (stdout_irq == NO_IRQ) {
+ if (!stdout_irq) {
pr_err("ehv-bc: no 'interrupts' property in %pOF node\n", np);
return 0;
}
@@ -696,7 +696,7 @@ static int ehv_bc_tty_probe(struct platform_device *pdev)
bc->rx_irq = irq_of_parse_and_map(np, 0);
bc->tx_irq = irq_of_parse_and_map(np, 1);
- if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) {
+ if (!bc->rx_irq || !bc->tx_irq) {
dev_err(&pdev->dev, "no 'interrupts' property in %pOFn node\n",
np);
ret = -ENODEV;
diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c
index e8b8c645482b..184d325abeed 100644
--- a/drivers/tty/hvc/hvc_rtas.c
+++ b/drivers/tty/hvc/hvc_rtas.c
@@ -26,7 +26,7 @@
#include "hvc_console.h"
#define hvc_rtas_cookie 0x67781e15
-struct hvc_struct *hvc_rtas_dev;
+static struct hvc_struct *hvc_rtas_dev;
static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE;
static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE;
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index b6e0cc4571ea..daf12132deb1 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -38,8 +38,10 @@
#include <linux/sched/signal.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
+#include <linux/bitfield.h>
#include <linux/ctype.h>
#include <linux/mm.h>
+#include <linux/math.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/poll.h>
@@ -75,7 +77,12 @@ module_param(debug, int, 0600);
#define T1 10 /* 100mS */
#define T2 34 /* 333mS */
+#define T3 10 /* 10s */
#define N2 3 /* Retry 3 times */
+#define K 2 /* outstanding I frames */
+
+#define MAX_T3 255 /* In seconds. */
+#define MAX_WINDOW_SIZE 7 /* Limit of K in error recovery mode. */
/* Use long timers for testing at low speed with debug on */
#ifdef DEBUG_TIMING
@@ -89,6 +96,7 @@ module_param(debug, int, 0600);
*/
#define MAX_MRU 1500
#define MAX_MTU 1500
+#define MIN_MTU (PROT_OVERHEAD + 1)
/* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
#define PROT_OVERHEAD 7
#define GSM_NET_TX_TIMEOUT (HZ*10)
@@ -120,6 +128,7 @@ struct gsm_msg {
enum gsm_dlci_state {
DLCI_CLOSED,
+ DLCI_CONFIGURE, /* Sending PN (for adaption > 1) */
DLCI_OPENING, /* Sending SABM not seen UA */
DLCI_OPEN, /* SABM/UA complete */
DLCI_CLOSING, /* Sending DISC not seen UA/DM */
@@ -159,7 +168,12 @@ struct gsm_dlci {
int prev_adaption;
u32 modem_rx; /* Our incoming virtual modem lines */
u32 modem_tx; /* Our outgoing modem lines */
+ unsigned int mtu;
bool dead; /* Refuse re-open */
+ /* Configuration */
+ u8 prio; /* Priority */
+ u8 ftype; /* Frame type */
+ u8 k; /* Window size */
/* Flow control */
bool throttled; /* Private copy of throttle state */
bool constipated; /* Throttle status for outgoing */
@@ -172,6 +186,32 @@ struct gsm_dlci {
struct net_device *net; /* network interface, if created */
};
+/*
+ * Parameter bits used for parameter negotiation according to 3GPP 27.010
+ * chapter 5.4.6.3.1.
+ */
+
+struct gsm_dlci_param_bits {
+ u8 d_bits;
+ u8 i_cl_bits;
+ u8 p_bits;
+ u8 t_bits;
+ __le16 n_bits;
+ u8 na_bits;
+ u8 k_bits;
+};
+
+static_assert(sizeof(struct gsm_dlci_param_bits) == 8);
+
+#define PN_D_FIELD_DLCI GENMASK(5, 0)
+#define PN_I_CL_FIELD_FTYPE GENMASK(3, 0)
+#define PN_I_CL_FIELD_ADAPTION GENMASK(7, 4)
+#define PN_P_FIELD_PRIO GENMASK(5, 0)
+#define PN_T_FIELD_T1 GENMASK(7, 0)
+#define PN_N_FIELD_N1 GENMASK(15, 0)
+#define PN_NA_FIELD_N2 GENMASK(7, 0)
+#define PN_K_FIELD_K GENMASK(2, 0)
+
/* Total number of supported devices */
#define GSM_TTY_MINORS 256
@@ -282,7 +322,9 @@ struct gsm_mux {
int adaption; /* 1 or 2 supported */
u8 ftype; /* UI or UIH */
int t1, t2; /* Timers in 1/100th of a sec */
+ unsigned int t3; /* Power wake-up timer in seconds. */
int n2; /* Retry count */
+ u8 k; /* Window size */
/* Statistics (not currently exposed) */
unsigned long bad_fcs;
@@ -397,6 +439,7 @@ static const u8 gsm_fcs8[256] = {
#define INIT_FCS 0xFF
#define GOOD_FCS 0xCF
+static void gsm_dlci_close(struct gsm_dlci *dlci);
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
@@ -520,6 +563,57 @@ static void gsm_hex_dump_bytes(const char *fname, const u8 *data,
}
/**
+ * gsm_encode_params - encode DLCI parameters
+ * @dlci: DLCI to encode from
+ * @params: buffer to fill with the encoded parameters
+ *
+ * Encodes the parameters according to GSM 07.10 section 5.4.6.3.1
+ * table 3.
+ */
+static int gsm_encode_params(const struct gsm_dlci *dlci,
+ struct gsm_dlci_param_bits *params)
+{
+ const struct gsm_mux *gsm = dlci->gsm;
+ unsigned int i, cl;
+
+ switch (dlci->ftype) {
+ case UIH:
+ i = 0; /* UIH */
+ break;
+ case UI:
+ i = 1; /* UI */
+ break;
+ default:
+ pr_debug("unsupported frame type %d\n", dlci->ftype);
+ return -EINVAL;
+ }
+
+ switch (dlci->adaption) {
+ case 1: /* Unstructured */
+ cl = 0; /* convergence layer type 1 */
+ break;
+ case 2: /* Unstructured with modem bits. */
+ cl = 1; /* convergence layer type 2 */
+ break;
+ default:
+ pr_debug("unsupported adaption %d\n", dlci->adaption);
+ return -EINVAL;
+ }
+
+ params->d_bits = FIELD_PREP(PN_D_FIELD_DLCI, dlci->addr);
+ /* UIH, convergence layer type 1 */
+ params->i_cl_bits = FIELD_PREP(PN_I_CL_FIELD_FTYPE, i) |
+ FIELD_PREP(PN_I_CL_FIELD_ADAPTION, cl);
+ params->p_bits = FIELD_PREP(PN_P_FIELD_PRIO, dlci->prio);
+ params->t_bits = FIELD_PREP(PN_T_FIELD_T1, gsm->t1);
+ params->n_bits = cpu_to_le16(FIELD_PREP(PN_N_FIELD_N1, dlci->mtu));
+ params->na_bits = FIELD_PREP(PN_NA_FIELD_N2, gsm->n2);
+ params->k_bits = FIELD_PREP(PN_K_FIELD_K, dlci->k);
+
+ return 0;
+}
+
+/**
* gsm_register_devices - register all tty devices for a given mux index
*
* @driver: the tty driver that describes the tty devices
@@ -1076,12 +1170,12 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
return 0;
/* MTU/MRU count only the data bits but watch adaption mode */
- if ((len + h) > gsm->mtu)
- len = gsm->mtu - h;
+ if ((len + h) > dlci->mtu)
+ len = dlci->mtu - h;
size = len + h;
- msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+ msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
if (!msg)
return -ENOMEM;
dp = msg->data;
@@ -1145,19 +1239,19 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
len = dlci->skb->len + overhead;
/* MTU/MRU count only the data bits */
- if (len > gsm->mtu) {
+ if (len > dlci->mtu) {
if (dlci->adaption == 3) {
/* Over long frame, bin it */
dev_kfree_skb_any(dlci->skb);
dlci->skb = NULL;
return 0;
}
- len = gsm->mtu;
+ len = dlci->mtu;
} else
last = 1;
size = len + overhead;
- msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+ msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
if (msg == NULL) {
skb_queue_tail(&dlci->skb_list, dlci->skb);
dlci->skb = NULL;
@@ -1214,7 +1308,7 @@ static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
return -EINVAL;
}
- msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
+ msg = gsm_data_alloc(gsm, dlci->addr, size, dlci->ftype);
if (!msg) {
pr_err("%s: gsm_data_alloc error", __func__);
return -ENOMEM;
@@ -1287,7 +1381,7 @@ static int gsm_dlci_data_sweep(struct gsm_mux *gsm)
}
if (!sent)
break;
- };
+ }
return ret;
}
@@ -1340,8 +1434,9 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)
static int gsm_control_command(struct gsm_mux *gsm, int cmd, const u8 *data,
int dlen)
{
- struct gsm_msg *msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype);
+ struct gsm_msg *msg;
+ msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->dlci[0]->ftype);
if (msg == NULL)
return -ENOMEM;
@@ -1367,7 +1462,8 @@ static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
int dlen)
{
struct gsm_msg *msg;
- msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype);
+
+ msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->dlci[0]->ftype);
if (msg == NULL)
return;
msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */
@@ -1438,6 +1534,116 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
}
/**
+ * gsm_process_negotiation - process received parameters
+ * @gsm: GSM channel
+ * @addr: DLCI address
+ * @cr: command/response
+ * @params: encoded parameters from the parameter negotiation message
+ *
+ * Used when the response for our parameter negotiation command was
+ * received.
+ */
+static int gsm_process_negotiation(struct gsm_mux *gsm, unsigned int addr,
+ unsigned int cr,
+ const struct gsm_dlci_param_bits *params)
+{
+ struct gsm_dlci *dlci = gsm->dlci[addr];
+ unsigned int ftype, i, adaption, prio, n1, k;
+
+ i = FIELD_GET(PN_I_CL_FIELD_FTYPE, params->i_cl_bits);
+ adaption = FIELD_GET(PN_I_CL_FIELD_ADAPTION, params->i_cl_bits) + 1;
+ prio = FIELD_GET(PN_P_FIELD_PRIO, params->p_bits);
+ n1 = FIELD_GET(PN_N_FIELD_N1, get_unaligned_le16(&params->n_bits));
+ k = FIELD_GET(PN_K_FIELD_K, params->k_bits);
+
+ if (n1 < MIN_MTU) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s N1 out of range in PN\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (i) {
+ case 0x00:
+ ftype = UIH;
+ break;
+ case 0x01:
+ ftype = UI;
+ break;
+ case 0x02: /* I frames are not supported */
+ if (debug & DBG_ERRORS)
+ pr_info("%s unsupported I frame request in PN\n",
+ __func__);
+ return -EINVAL;
+ default:
+ if (debug & DBG_ERRORS)
+ pr_info("%s i out of range in PN\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!cr && gsm->initiator) {
+ if (adaption != dlci->adaption) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid adaption %d in PN\n",
+ __func__, adaption);
+ return -EINVAL;
+ }
+ if (prio != dlci->prio) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid priority %d in PN",
+ __func__, prio);
+ return -EINVAL;
+ }
+ if (n1 > gsm->mru || n1 > dlci->mtu) {
+ /* We requested a frame size but the other party wants
+ * to send larger frames. The standard allows only a
+ * smaller response value than requested (5.4.6.3.1).
+ */
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid N1 %d in PN\n", __func__,
+ n1);
+ return -EINVAL;
+ }
+ dlci->mtu = n1;
+ if (ftype != dlci->ftype) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid i %d in PN\n", __func__, i);
+ return -EINVAL;
+ }
+ if (ftype != UI && ftype != UIH && k > dlci->k) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid k %d in PN\n", __func__, k);
+ return -EINVAL;
+ }
+ dlci->k = k;
+ } else if (cr && !gsm->initiator) {
+ /* Only convergence layer type 1 and 2 are supported. */
+ if (adaption != 1 && adaption != 2) {
+ if (debug & DBG_ERRORS)
+ pr_info("%s invalid adaption %d in PN\n",
+ __func__, adaption);
+ return -EINVAL;
+ }
+ dlci->adaption = adaption;
+ if (n1 > gsm->mru) {
+ /* Propose a smaller value */
+ dlci->mtu = gsm->mru;
+ } else if (n1 > MAX_MTU) {
+ /* Propose a smaller value */
+ dlci->mtu = MAX_MTU;
+ } else {
+ dlci->mtu = n1;
+ }
+ dlci->prio = prio;
+ dlci->ftype = ftype;
+ dlci->k = k;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
* gsm_control_modem - modem status received
* @gsm: GSM channel
* @data: data following command
@@ -1491,6 +1697,65 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
}
/**
+ * gsm_control_negotiation - parameter negotiation received
+ * @gsm: GSM channel
+ * @cr: command/response flag
+ * @data: data following command
+ * @dlen: data length
+ *
+ * We have received a parameter negotiation message. This is used by
+ * the GSM mux protocol to configure protocol parameters for a new DLCI.
+ */
+static void gsm_control_negotiation(struct gsm_mux *gsm, unsigned int cr,
+ const u8 *data, unsigned int dlen)
+{
+ unsigned int addr;
+ struct gsm_dlci_param_bits pn_reply;
+ struct gsm_dlci *dlci;
+ struct gsm_dlci_param_bits *params;
+
+ if (dlen < sizeof(struct gsm_dlci_param_bits))
+ return;
+
+ /* Invalid DLCI? */
+ params = (struct gsm_dlci_param_bits *)data;
+ addr = FIELD_GET(PN_D_FIELD_DLCI, params->d_bits);
+ if (addr == 0 || addr >= NUM_DLCI || !gsm->dlci[addr])
+ return;
+ dlci = gsm->dlci[addr];
+
+ /* Too late for parameter negotiation? */
+ if ((!cr && dlci->state == DLCI_OPENING) || dlci->state == DLCI_OPEN)
+ return;
+
+ /* Process the received parameters */
+ if (gsm_process_negotiation(gsm, addr, cr, params) != 0) {
+ /* Negotiation failed. Close the link. */
+ if (debug & DBG_ERRORS)
+ pr_info("%s PN failed\n", __func__);
+ gsm_dlci_close(dlci);
+ return;
+ }
+
+ if (cr) {
+ /* Reply command with accepted parameters. */
+ if (gsm_encode_params(dlci, &pn_reply) == 0)
+ gsm_control_reply(gsm, CMD_PN, (const u8 *)&pn_reply,
+ sizeof(pn_reply));
+ else if (debug & DBG_ERRORS)
+ pr_info("%s PN invalid\n", __func__);
+ } else if (dlci->state == DLCI_CONFIGURE) {
+ /* Proceed with link setup by sending SABM before UA */
+ dlci->state = DLCI_OPENING;
+ gsm_command(gsm, dlci->addr, SABM|PF);
+ mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
+ } else {
+ if (debug & DBG_ERRORS)
+ pr_info("%s PN in invalid state\n", __func__);
+ }
+}
+
+/**
* gsm_control_rls - remote line status
* @gsm: GSM channel
* @data: data bytes
@@ -1599,8 +1864,12 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
/* Modem wishes to enter power saving state */
gsm_control_reply(gsm, CMD_PSC, NULL, 0);
break;
+ /* Optional commands */
+ case CMD_PN:
+ /* Modem sends a parameter negotiation command */
+ gsm_control_negotiation(gsm, 1, data, clen);
+ break;
/* Optional unsupported commands */
- case CMD_PN: /* Parameter negotiation */
case CMD_RPN: /* Remote port negotiation */
case CMD_SNC: /* Service negotiation command */
default:
@@ -1633,8 +1902,8 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
spin_lock_irqsave(&gsm->control_lock, flags);
ctrl = gsm->pending_cmd;
- /* Does the reply match our command */
command |= 1;
+ /* Does the reply match our command */
if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
/* Our command was replied to, kill the retry timer */
del_timer(&gsm->t2_timer);
@@ -1644,6 +1913,9 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
ctrl->error = -EOPNOTSUPP;
ctrl->done = 1;
wake_up(&gsm->event);
+ /* Or did we receive the PN response to our PN command */
+ } else if (command == CMD_PN) {
+ gsm_control_negotiation(gsm, 0, data, clen);
}
spin_unlock_irqrestore(&gsm->control_lock, flags);
}
@@ -1822,6 +2094,32 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
}
/**
+ * gsm_dlci_negotiate - start parameter negotiation
+ * @dlci: DLCI to open
+ *
+ * Starts the parameter negotiation for the new DLCI. This needs to be done
+ * before the DLCI initialized the channel via SABM.
+ */
+static int gsm_dlci_negotiate(struct gsm_dlci *dlci)
+{
+ struct gsm_mux *gsm = dlci->gsm;
+ struct gsm_dlci_param_bits params;
+ int ret;
+
+ ret = gsm_encode_params(dlci, &params);
+ if (ret != 0)
+ return ret;
+
+ /* We cannot asynchronous wait for the command response with
+ * gsm_command() and gsm_control_wait() at this point.
+ */
+ ret = gsm_control_command(gsm, CMD_PN, (const u8 *)&params,
+ sizeof(params));
+
+ return ret;
+}
+
+/**
* gsm_dlci_t1 - T1 timer expiry
* @t: timer contained in the DLCI that opened
*
@@ -1842,6 +2140,14 @@ static void gsm_dlci_t1(struct timer_list *t)
struct gsm_mux *gsm = dlci->gsm;
switch (dlci->state) {
+ case DLCI_CONFIGURE:
+ if (dlci->retries && gsm_dlci_negotiate(dlci) == 0) {
+ dlci->retries--;
+ mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
+ } else {
+ gsm_dlci_begin_close(dlci); /* prevent half open link */
+ }
+ break;
case DLCI_OPENING:
if (dlci->retries) {
dlci->retries--;
@@ -1880,17 +2186,46 @@ static void gsm_dlci_t1(struct timer_list *t)
* to the modem which should then reply with a UA or ADM, at which point
* we will move into open state. Opening is done asynchronously with retry
* running off timers and the responses.
+ * Parameter negotiation is performed before SABM if required.
*/
static void gsm_dlci_begin_open(struct gsm_dlci *dlci)
{
- struct gsm_mux *gsm = dlci->gsm;
- if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING)
+ struct gsm_mux *gsm = dlci ? dlci->gsm : NULL;
+ bool need_pn = false;
+
+ if (!gsm)
return;
- dlci->retries = gsm->n2;
- dlci->state = DLCI_OPENING;
- gsm_command(dlci->gsm, dlci->addr, SABM|PF);
- mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
+
+ if (dlci->addr != 0) {
+ if (gsm->adaption != 1 || gsm->adaption != dlci->adaption)
+ need_pn = true;
+ if (dlci->prio != (roundup(dlci->addr + 1, 8) - 1))
+ need_pn = true;
+ if (gsm->ftype != dlci->ftype)
+ need_pn = true;
+ }
+
+ switch (dlci->state) {
+ case DLCI_CLOSED:
+ case DLCI_CLOSING:
+ dlci->retries = gsm->n2;
+ if (!need_pn) {
+ dlci->state = DLCI_OPENING;
+ gsm_command(gsm, dlci->addr, SABM|PF);
+ } else {
+ /* Configure DLCI before setup */
+ dlci->state = DLCI_CONFIGURE;
+ if (gsm_dlci_negotiate(dlci) != 0) {
+ gsm_dlci_close(dlci);
+ return;
+ }
+ }
+ mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
+ break;
+ default:
+ break;
+ }
}
/**
@@ -2078,6 +2413,13 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
dlci->gsm = gsm;
dlci->addr = addr;
dlci->adaption = gsm->adaption;
+ dlci->mtu = gsm->mtu;
+ if (addr == 0)
+ dlci->prio = 0;
+ else
+ dlci->prio = roundup(addr + 1, 8) - 1;
+ dlci->ftype = gsm->ftype;
+ dlci->k = gsm->k;
dlci->state = DLCI_CLOSED;
if (addr) {
dlci->data = gsm_dlci_data;
@@ -2652,7 +2994,9 @@ static struct gsm_mux *gsm_alloc_mux(void)
gsm->t1 = T1;
gsm->t2 = T2;
+ gsm->t3 = T3;
gsm->n2 = N2;
+ gsm->k = K;
gsm->ftype = UIH;
gsm->adaption = 1;
gsm->encoding = GSM_ADV_OPT;
@@ -2692,7 +3036,7 @@ static void gsm_copy_config_values(struct gsm_mux *gsm,
c->initiator = gsm->initiator;
c->t1 = gsm->t1;
c->t2 = gsm->t2;
- c->t3 = 0; /* Not supported */
+ c->t3 = gsm->t3;
c->n2 = gsm->n2;
if (gsm->ftype == UIH)
c->i = 1;
@@ -2701,7 +3045,7 @@ static void gsm_copy_config_values(struct gsm_mux *gsm,
pr_debug("Ftype %d i %d\n", gsm->ftype, c->i);
c->mru = gsm->mru;
c->mtu = gsm->mtu;
- c->k = 0;
+ c->k = gsm->k;
}
static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
@@ -2714,7 +3058,11 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
if ((c->adaption != 1 && c->adaption != 2) || c->k)
return -EOPNOTSUPP;
/* Check the MRU/MTU range looks sane */
- if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
+ if (c->mru < MIN_MTU || c->mtu < MIN_MTU)
+ return -EINVAL;
+ if (c->mru > MAX_MRU || c->mtu > MAX_MTU)
+ return -EINVAL;
+ if (c->t3 > MAX_T3)
return -EINVAL;
if (c->n2 > 255)
return -EINVAL;
@@ -2722,6 +3070,8 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
return -EINVAL;
if (c->initiator > 1)
return -EINVAL;
+ if (c->k > MAX_WINDOW_SIZE)
+ return -EINVAL;
if (c->i == 0 || c->i > 2) /* UIH and UI only */
return -EINVAL;
/*
@@ -2769,6 +3119,10 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
gsm->t1 = c->t1;
if (c->t2)
gsm->t2 = c->t2;
+ if (c->t3)
+ gsm->t3 = c->t3;
+ if (c->k)
+ gsm->k = c->k;
/*
* FIXME: We need to separate activation/deactivation from adding
@@ -3299,9 +3653,9 @@ static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
pr_err("alloc_netdev failed\n");
return -ENOMEM;
}
- net->mtu = dlci->gsm->mtu;
- net->min_mtu = 8;
- net->max_mtu = dlci->gsm->mtu;
+ net->mtu = dlci->mtu;
+ net->min_mtu = MIN_MTU;
+ net->max_mtu = dlci->mtu;
mux_net = netdev_priv(net);
mux_net->dlci = dlci;
kref_init(&mux_net->ref);
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 597019690ae6..c8f56c9b1a1c 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2130,7 +2130,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
ssize_t retval = 0;
long timeout;
bool packet;
- size_t tail;
+ size_t old_tail;
/*
* Is this a continuation of a read started earler?
@@ -2193,7 +2193,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
}
packet = tty->ctrl.packet;
- tail = ldata->read_tail;
+ old_tail = ldata->read_tail;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
@@ -2282,7 +2282,7 @@ more_to_be_read:
if (time)
timeout = time;
}
- if (tail != ldata->read_tail)
+ if (old_tail != ldata->read_tail)
n_tty_kick_worker(tty);
up_read(&tty->termios_rwsem);
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index c7d34823f715..185462fd959c 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -154,35 +154,13 @@ static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
- struct circ_buf *xmit = &port->state->xmit;
- int count = 256;
-
- if (port->x_char) {
- *CSR_UARTDR = port->x_char;
- port->icount.tx++;
- port->x_char = 0;
- goto out;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- serial21285_stop_tx(port);
- goto out;
- }
-
- do {
- *CSR_UARTDR = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0 && !(*CSR_UARTFLG & 0x20));
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- serial21285_stop_tx(port);
+ uart_port_tx_limited(port, ch, 256,
+ !(*CSR_UARTFLG & 0x20),
+ *CSR_UARTDR = ch,
+ ({}));
- out:
return IRQ_HANDLED;
}
diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
index fa8ccf204d86..ed5a94747692 100644
--- a/drivers/tty/serial/8250/8250_bcm7271.c
+++ b/drivers/tty/serial/8250/8250_bcm7271.c
@@ -425,9 +425,7 @@ static int brcmuart_tx_dma(struct uart_8250_port *p)
priv->dma.tx_err = 0;
memcpy(priv->tx_buf, &xmit->buf[xmit->tail], tx_size);
- xmit->tail += tx_size;
- xmit->tail &= UART_XMIT_SIZE - 1;
- p->port.icount.tx += tx_size;
+ uart_xmit_advance(&p->port, tx_size);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&p->port);
@@ -1212,9 +1210,17 @@ static struct platform_driver brcmuart_platform_driver = {
static int __init brcmuart_init(void)
{
+ int ret;
+
brcmuart_debugfs_root = debugfs_create_dir(
brcmuart_platform_driver.driver.name, NULL);
- return platform_driver_register(&brcmuart_platform_driver);
+ ret = platform_driver_register(&brcmuart_platform_driver);
+ if (ret) {
+ debugfs_remove_recursive(brcmuart_debugfs_root);
+ return ret;
+ }
+
+ return 0;
}
module_init(brcmuart_init);
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 74568292186f..ab63c308be0a 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -32,6 +32,7 @@
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#ifdef CONFIG_SPARC
@@ -1175,8 +1176,8 @@ static int __init serial8250_init(void)
serial8250_isa_init_ports();
- pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %sabled\n",
- nr_uarts, share_irqs ? "en" : "dis");
+ pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %s\n",
+ nr_uarts, str_enabled_disabled(share_irqs));
#ifdef CONFIG_SPARC
ret = sunserial_register_minors(&serial8250_reg, UART_NR);
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index b85c82616e8c..37d6af2ec427 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -38,9 +38,8 @@ static void __dma_tx_complete(void *param)
spin_unlock_irqrestore(&p->port.lock, flags);
}
-static void __dma_rx_complete(void *param)
+static void __dma_rx_complete(struct uart_8250_port *p)
{
- struct uart_8250_port *p = param;
struct uart_8250_dma *dma = p->dma;
struct tty_port *tty_port = &p->port.state->port;
struct dma_tx_state state;
@@ -57,6 +56,20 @@ static void __dma_rx_complete(void *param)
tty_flip_buffer_push(tty_port);
}
+static void dma_rx_complete(void *param)
+{
+ struct uart_8250_port *p = param;
+ struct uart_8250_dma *dma = p->dma;
+ unsigned long flags;
+
+ __dma_rx_complete(p);
+
+ spin_lock_irqsave(&p->port.lock, flags);
+ if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR))
+ p->dma->rx_dma(p);
+ spin_unlock_irqrestore(&p->port.lock, flags);
+}
+
int serial8250_tx_dma(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
@@ -130,7 +143,7 @@ int serial8250_rx_dma(struct uart_8250_port *p)
return -EBUSY;
dma->rx_running = 1;
- desc->callback = __dma_rx_complete;
+ desc->callback = dma_rx_complete;
desc->callback_param = p;
dma->rx_cookie = dmaengine_submit(desc);
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 2b2f5d8d24b9..617b8ce60d6b 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -87,7 +87,7 @@ static void __init ingenic_early_console_setup_clock(struct earlycon_device *dev
dev->port.uartclk = be32_to_cpup(prop);
}
-static int __init ingenic_early_console_setup(struct earlycon_device *dev,
+static int __init ingenic_earlycon_setup_tail(struct earlycon_device *dev,
const char *opt)
{
struct uart_port *port = &dev->port;
@@ -103,8 +103,6 @@ static int __init ingenic_early_console_setup(struct earlycon_device *dev,
uart_parse_options(opt, &baud, &parity, &bits, &flow);
}
- ingenic_early_console_setup_clock(dev);
-
if (dev->baud)
baud = dev->baud;
divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
@@ -129,9 +127,36 @@ static int __init ingenic_early_console_setup(struct earlycon_device *dev,
return 0;
}
+static int __init ingenic_early_console_setup(struct earlycon_device *dev,
+ const char *opt)
+{
+ ingenic_early_console_setup_clock(dev);
+
+ return ingenic_earlycon_setup_tail(dev, opt);
+}
+
+static int __init jz4750_early_console_setup(struct earlycon_device *dev,
+ const char *opt)
+{
+ /*
+ * JZ4750/55/60 have an optional /2 divider between the EXT
+ * oscillator and some peripherals including UART, which will
+ * be enabled if using a 24 MHz oscillator, and disabled when
+ * using a 12 MHz oscillator.
+ */
+ ingenic_early_console_setup_clock(dev);
+ if (dev->port.uartclk >= 16000000)
+ dev->port.uartclk /= 2;
+
+ return ingenic_earlycon_setup_tail(dev, opt);
+}
+
OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart",
ingenic_early_console_setup);
+OF_EARLYCON_DECLARE(jz4750_uart, "ingenic,jz4750-uart",
+ jz4750_early_console_setup);
+
OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart",
ingenic_early_console_setup);
@@ -328,6 +353,7 @@ static const struct ingenic_uart_config x1000_uart_config = {
static const struct of_device_id of_match[] = {
{ .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
+ { .compatible = "ingenic,jz4750-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 3f33014022f0..734f092ef839 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -44,6 +44,7 @@
#define UART_HAS_EFR2 BIT(4)
#define UART_HAS_RHR_IT_DIS BIT(5)
#define UART_RX_TIMEOUT_QUIRK BIT(6)
+#define UART_HAS_NATIVE_RS485 BIT(7)
#define OMAP_UART_FCR_RX_TRIG 6
#define OMAP_UART_FCR_TX_TRIG 4
@@ -101,6 +102,11 @@
#define UART_OMAP_IER2 0x1B
#define UART_OMAP_IER2_RHR_IT_DIS BIT(2)
+/* Mode Definition Register 3 */
+#define UART_OMAP_MDR3 0x20
+#define UART_OMAP_MDR3_DIR_POL BIT(3)
+#define UART_OMAP_MDR3_DIR_EN BIT(4)
+
/* Enhanced features register 2 */
#define UART_OMAP_EFR2 0x23
#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6)
@@ -112,6 +118,7 @@ struct omap8250_priv {
int line;
u8 habit;
u8 mdr1;
+ u8 mdr3;
u8 efr;
u8 scr;
u8 wer;
@@ -346,7 +353,10 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
__omap8250_set_mctrl(&up->port, up->port.mctrl);
- if (up->port.rs485.flags & SER_RS485_ENABLED)
+ serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+ if (up->port.rs485.flags & SER_RS485_ENABLED &&
+ up->port.rs485_config == serial8250_em485_config)
serial8250_em485_stop_tx(up);
}
@@ -794,6 +804,74 @@ static void omap_8250_unthrottle(struct uart_port *port)
pm_runtime_put_autosuspend(port->dev);
}
+static int omap8250_rs485_config(struct uart_port *port,
+ struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ struct omap8250_priv *priv = port->private_data;
+ struct uart_8250_port *up = up_to_u8250p(port);
+ u32 fixed_delay_rts_before_send = 0;
+ u32 fixed_delay_rts_after_send = 0;
+ unsigned int baud;
+
+ /*
+ * There is a fixed delay of 3 bit clock cycles after the TX shift
+ * register is going empty to allow time for the stop bit to transition
+ * through the transceiver before direction is changed to receive.
+ *
+ * Additionally there appears to be a 1 bit clock delay between writing
+ * to the THR register and transmission of the start bit, per page 8783
+ * of the AM65 TRM: https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf
+ */
+ if (priv->quot) {
+ if (priv->mdr1 == UART_OMAP_MDR1_16X_MODE)
+ baud = port->uartclk / (16 * priv->quot);
+ else
+ baud = port->uartclk / (13 * priv->quot);
+
+ fixed_delay_rts_after_send = 3 * MSEC_PER_SEC / baud;
+ fixed_delay_rts_before_send = 1 * MSEC_PER_SEC / baud;
+ }
+
+ /*
+ * Fall back to RS485 software emulation if the UART is missing
+ * hardware support, if the device tree specifies an mctrl_gpio
+ * (indicates that RTS is unavailable due to a pinmux conflict)
+ * or if the requested delays exceed the fixed hardware delays.
+ */
+ if (!(priv->habit & UART_HAS_NATIVE_RS485) ||
+ mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) ||
+ rs485->delay_rts_after_send > fixed_delay_rts_after_send ||
+ rs485->delay_rts_before_send > fixed_delay_rts_before_send) {
+ priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
+ serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+ port->rs485_config = serial8250_em485_config;
+ return serial8250_em485_config(port, termios, rs485);
+ }
+
+ rs485->delay_rts_after_send = fixed_delay_rts_after_send;
+ rs485->delay_rts_before_send = fixed_delay_rts_before_send;
+
+ if (rs485->flags & SER_RS485_ENABLED)
+ priv->mdr3 |= UART_OMAP_MDR3_DIR_EN;
+ else
+ priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
+
+ /*
+ * Retain same polarity semantics as RS485 software emulation,
+ * i.e. SER_RS485_RTS_ON_SEND means driving RTS low on send.
+ */
+ if (rs485->flags & SER_RS485_RTS_ON_SEND)
+ priv->mdr3 &= ~UART_OMAP_MDR3_DIR_POL;
+ else
+ priv->mdr3 |= UART_OMAP_MDR3_DIR_POL;
+
+ serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+ return 0;
+}
+
#ifdef CONFIG_SERIAL_8250_DMA
static int omap_8250_rx_dma(struct uart_8250_port *p);
@@ -1243,7 +1321,7 @@ static struct omap8250_dma_params am33xx_dma = {
static struct omap8250_platdata am654_platdata = {
.dma_params = &am654_dma,
.habit = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS |
- UART_RX_TIMEOUT_QUIRK,
+ UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485,
};
static struct omap8250_platdata am33xx_platdata = {
@@ -1336,7 +1414,8 @@ static int omap8250_probe(struct platform_device *pdev)
up.port.shutdown = omap_8250_shutdown;
up.port.throttle = omap_8250_throttle;
up.port.unthrottle = omap_8250_unthrottle;
- up.port.rs485_config = serial8250_em485_config;
+ up.port.rs485_config = omap8250_rs485_config;
+ /* same rs485_supported for software emulation and native RS485 */
up.port.rs485_supported = serial8250_em485_supported;
up.rs485_start_tx = serial8250_em485_start_tx;
up.rs485_stop_tx = serial8250_em485_stop_tx;
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 388172289627..beba8f38b3dc 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1842,8 +1842,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
*/
serial_in(up, UART_SCR);
}
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_empty(xmit))
break;
if ((up->capabilities & UART_CAP_HFIFO) &&
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 434f83168546..c55b947f3cdb 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -958,6 +958,7 @@ config SERIAL_OMAP_CONSOLE
config SERIAL_SIFIVE
tristate "SiFive UART support"
depends on OF
+ default SOC_SIFIVE || SOC_CANAAN
select SERIAL_CORE
help
Select this option if you are building a kernel for a device that
@@ -967,6 +968,7 @@ config SERIAL_SIFIVE
config SERIAL_SIFIVE_CONSOLE
bool "Console on SiFive UART"
depends on SERIAL_SIFIVE=y
+ default SOC_SIFIVE || SOC_CANAAN
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c
index c2d154d78e54..9f843d1cee40 100644
--- a/drivers/tty/serial/altera_jtaguart.c
+++ b/drivers/tty/serial/altera_jtaguart.c
@@ -50,15 +50,6 @@
#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400
#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000
-/*
- * Local per-uart structure.
- */
-struct altera_jtaguart {
- struct uart_port port;
- unsigned int sigs; /* Local copy of line sigs */
- unsigned long imr; /* Local IMR mirror */
-};
-
static unsigned int altera_jtaguart_tx_space(struct uart_port *port, u32 *ctlp)
{
u32 ctl = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG);
@@ -85,29 +76,23 @@ static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs)
static void altera_jtaguart_start_tx(struct uart_port *port)
{
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
-
- pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK;
- writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+ port->read_status_mask |= ALTERA_JTAGUART_CONTROL_WE_MSK;
+ writel(port->read_status_mask,
+ port->membase + ALTERA_JTAGUART_CONTROL_REG);
}
static void altera_jtaguart_stop_tx(struct uart_port *port)
{
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
-
- pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
- writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+ port->read_status_mask &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
+ writel(port->read_status_mask,
+ port->membase + ALTERA_JTAGUART_CONTROL_REG);
}
static void altera_jtaguart_stop_rx(struct uart_port *port)
{
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
-
- pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK;
- writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+ port->read_status_mask &= ~ALTERA_JTAGUART_CONTROL_RE_MSK;
+ writel(port->read_status_mask,
+ port->membase + ALTERA_JTAGUART_CONTROL_REG);
}
static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state)
@@ -123,78 +108,51 @@ static void altera_jtaguart_set_termios(struct uart_port *port,
tty_termios_copy_hw(termios, old);
}
-static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp)
+static void altera_jtaguart_rx_chars(struct uart_port *port)
{
- struct uart_port *port = &pp->port;
- unsigned char ch, flag;
+ unsigned char ch;
unsigned long status;
while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) &
ALTERA_JTAGUART_DATA_RVALID_MSK) {
ch = status & ALTERA_JTAGUART_DATA_DATA_MSK;
- flag = TTY_NORMAL;
port->icount.rx++;
if (uart_handle_sysrq_char(port, ch))
continue;
- uart_insert_char(port, 0, 0, ch, flag);
+ uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
}
tty_flip_buffer_push(&port->state->port);
}
-static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
+static void altera_jtaguart_tx_chars(struct uart_port *port)
{
- struct uart_port *port = &pp->port;
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int pending, count;
-
- if (port->x_char) {
- /* Send special char - probably flow control */
- writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG);
- port->x_char = 0;
- port->icount.tx++;
- return;
- }
+ unsigned int count;
+ u8 ch;
- pending = uart_circ_chars_pending(xmit);
- if (pending > 0) {
- count = altera_jtaguart_tx_space(port, NULL);
- if (count > pending)
- count = pending;
- if (count > 0) {
- pending -= count;
- while (count--) {
- writel(xmit->buf[xmit->tail],
- port->membase + ALTERA_JTAGUART_DATA_REG);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
- if (pending < WAKEUP_CHARS)
- uart_write_wakeup(port);
- }
- }
+ count = altera_jtaguart_tx_space(port, NULL);
- if (pending == 0)
- altera_jtaguart_stop_tx(port);
+ uart_port_tx_limited(port, ch, count,
+ true,
+ writel(ch, port->membase + ALTERA_JTAGUART_DATA_REG),
+ ({}));
}
static irqreturn_t altera_jtaguart_interrupt(int irq, void *data)
{
struct uart_port *port = data;
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
unsigned int isr;
isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >>
- ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr;
+ ALTERA_JTAGUART_CONTROL_RI_OFF) & port->read_status_mask;
spin_lock(&port->lock);
if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK)
- altera_jtaguart_rx_chars(pp);
+ altera_jtaguart_rx_chars(port);
if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK)
- altera_jtaguart_tx_chars(pp);
+ altera_jtaguart_tx_chars(port);
spin_unlock(&port->lock);
@@ -211,8 +169,6 @@ static void altera_jtaguart_config_port(struct uart_port *port, int flags)
static int altera_jtaguart_startup(struct uart_port *port)
{
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
unsigned long flags;
int ret;
@@ -227,8 +183,9 @@ static int altera_jtaguart_startup(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
/* Enable RX interrupts now */
- pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK;
- writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+ port->read_status_mask = ALTERA_JTAGUART_CONTROL_RE_MSK;
+ writel(port->read_status_mask,
+ port->membase + ALTERA_JTAGUART_CONTROL_REG);
spin_unlock_irqrestore(&port->lock, flags);
@@ -237,15 +194,14 @@ static int altera_jtaguart_startup(struct uart_port *port)
static void altera_jtaguart_shutdown(struct uart_port *port)
{
- struct altera_jtaguart *pp =
- container_of(port, struct altera_jtaguart, port);
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
/* Disable all interrupts now */
- pp->imr = 0;
- writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+ port->read_status_mask = 0;
+ writel(port->read_status_mask,
+ port->membase + ALTERA_JTAGUART_CONTROL_REG);
spin_unlock_irqrestore(&port->lock, flags);
@@ -298,7 +254,7 @@ static const struct uart_ops altera_jtaguart_ops = {
};
#define ALTERA_JTAGUART_MAXPORTS 1
-static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS];
+static struct uart_port altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS];
#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE)
@@ -341,7 +297,7 @@ static void altera_jtaguart_console_putc(struct uart_port *port, unsigned char c
static void altera_jtaguart_console_write(struct console *co, const char *s,
unsigned int count)
{
- struct uart_port *port = &(altera_jtaguart_ports + co->index)->port;
+ struct uart_port *port = &altera_jtaguart_ports[co->index];
uart_console_write(port, s, count, altera_jtaguart_console_putc);
}
@@ -353,7 +309,7 @@ static int __init altera_jtaguart_console_setup(struct console *co,
if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS)
return -EINVAL;
- port = &altera_jtaguart_ports[co->index].port;
+ port = &altera_jtaguart_ports[co->index];
if (port->membase == NULL)
return -ENODEV;
return 0;
@@ -433,7 +389,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev)
if (i >= ALTERA_JTAGUART_MAXPORTS)
return -EINVAL;
- port = &altera_jtaguart_ports[i].port;
+ port = &altera_jtaguart_ports[i];
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_mem)
@@ -477,7 +433,7 @@ static int altera_jtaguart_remove(struct platform_device *pdev)
if (i == -1)
i = 0;
- port = &altera_jtaguart_ports[i].port;
+ port = &altera_jtaguart_ports[i];
uart_remove_one_port(&altera_jtaguart_driver, port);
iounmap(port->membase);
diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c
index 82f2790de28d..9ce3d24af536 100644
--- a/drivers/tty/serial/altera_uart.c
+++ b/drivers/tty/serial/altera_uart.c
@@ -247,47 +247,29 @@ static void altera_uart_rx_chars(struct uart_port *port)
static void altera_uart_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
-
- if (port->x_char) {
- /* Send special char - probably flow control */
- altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG);
- port->x_char = 0;
- port->icount.tx++;
- return;
- }
-
- while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
- ALTERA_UART_STATUS_TRDY_MSK) {
- if (xmit->head == xmit->tail)
- break;
- altera_uart_writel(port, xmit->buf[xmit->tail],
- ALTERA_UART_TXDATA_REG);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- altera_uart_stop_tx(port);
+ uart_port_tx(port, ch,
+ altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+ ALTERA_UART_STATUS_TRDY_MSK,
+ altera_uart_writel(port, ch, ALTERA_UART_TXDATA_REG));
}
static irqreturn_t altera_uart_interrupt(int irq, void *data)
{
struct uart_port *port = data;
struct altera_uart *pp = container_of(port, struct altera_uart, port);
+ unsigned long flags;
unsigned int isr;
isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr;
- spin_lock(&port->lock);
+ spin_lock_irqsave(&port->lock, flags);
if (isr & ALTERA_UART_STATUS_RRDY_MSK)
altera_uart_rx_chars(port);
if (isr & ALTERA_UART_STATUS_TRDY_MSK)
altera_uart_tx_chars(port);
- spin_unlock(&port->lock);
+ spin_unlock_irqrestore(&port->lock, flags);
return IRQ_RETVAL(isr);
}
diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c
index af27fb8ec145..a98fae2ca422 100644
--- a/drivers/tty/serial/amba-pl010.c
+++ b/drivers/tty/serial/amba-pl010.c
@@ -164,34 +164,12 @@ static void pl010_rx_chars(struct uart_port *port)
static void pl010_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- int count;
+ u8 ch;
- if (port->x_char) {
- writel(port->x_char, port->membase + UART01x_DR);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- pl010_stop_tx(port);
- return;
- }
-
- count = port->fifosize >> 1;
- do {
- writel(xmit->buf[xmit->tail], port->membase + UART01x_DR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (uart_circ_empty(xmit))
- pl010_stop_tx(port);
+ uart_port_tx_limited(port, ch, port->fifosize >> 1,
+ true,
+ writel(ch, port->membase + UART01x_DR),
+ ({}));
}
static void pl010_modem_status(struct uart_amba_port *uap)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 5cdced39eafd..d75c39f4622b 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -677,8 +677,7 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap)
* Now we know that DMA will fire, so advance the ring buffer
* with the stuff we just dispatched.
*/
- xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
- uap->port.icount.tx += count;
+ uart_xmit_advance(&uap->port, count);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
@@ -1045,6 +1044,9 @@ static void pl011_dma_rx_callback(void *data)
*/
static inline void pl011_dma_rx_stop(struct uart_amba_port *uap)
{
+ if (!uap->using_rx_dma)
+ return;
+
/* FIXME. Just disable the DMA enable */
uap->dmacr &= ~UART011_RXDMAE;
pl011_write(uap->dmacr, uap, REG_DMACR);
@@ -1828,8 +1830,17 @@ static void pl011_enable_interrupts(struct uart_amba_port *uap)
static void pl011_unthrottle_rx(struct uart_port *port)
{
struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port);
+ unsigned long flags;
- pl011_enable_interrupts(uap);
+ spin_lock_irqsave(&uap->port.lock, flags);
+
+ uap->im = UART011_RTIM;
+ if (!pl011_dma_rx_running(uap))
+ uap->im |= UART011_RXIM;
+
+ pl011_write(uap->im, uap, REG_IMSC);
+
+ spin_unlock_irqrestore(&uap->port.lock, flags);
}
static int pl011_startup(struct uart_port *port)
diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c
index 450f4edfda0f..915ee4b0d594 100644
--- a/drivers/tty/serial/apbuart.c
+++ b/drivers/tty/serial/apbuart.c
@@ -122,36 +122,12 @@ static void apbuart_rx_chars(struct uart_port *port)
static void apbuart_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- int count;
-
- if (port->x_char) {
- UART_PUT_CHAR(port, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- apbuart_stop_tx(port);
- return;
- }
-
- /* amba: fill FIFO */
- count = port->fifosize >> 1;
- do {
- UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- apbuart_stop_tx(port);
+ uart_port_tx_limited(port, ch, port->fifosize >> 1,
+ true,
+ UART_PUT_CHAR(port, ch),
+ ({}));
}
static irqreturn_t apbuart_int(int irq, void *dev_id)
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index 925484a42c82..4c3d04c6826a 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -425,8 +425,7 @@ static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
ar933x_uart_putc(up, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c
index 2a65ea2660e1..748e8b1cf4f7 100644
--- a/drivers/tty/serial/arc_uart.c
+++ b/drivers/tty/serial/arc_uart.c
@@ -166,8 +166,7 @@ static void arc_serial_tx_chars(struct uart_port *port)
sent = 1;
} else if (!uart_circ_empty(xmit)) {
ch = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
while (!(UART_GET_STATUS(port) & TXEMPTY))
cpu_relax();
UART_SET_DATA(port, ch);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index bd07f79a2df9..f1c06e12efa0 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -552,19 +552,23 @@ static u_int atmel_get_mctrl(struct uart_port *port)
static void atmel_stop_tx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ bool is_pdc = atmel_use_pdc_tx(port);
+ bool is_dma = is_pdc || atmel_use_dma_tx(port);
- if (atmel_use_pdc_tx(port)) {
+ if (is_pdc) {
/* disable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
}
- /*
- * Disable the transmitter.
- * This is mandatory when DMA is used, otherwise the DMA buffer
- * is fully transmitted.
- */
- atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
- atmel_port->tx_stopped = true;
+ if (is_dma) {
+ /*
+ * Disable the transmitter.
+ * This is mandatory when DMA is used, otherwise the DMA buffer
+ * is fully transmitted.
+ */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS);
+ atmel_port->tx_stopped = true;
+ }
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
@@ -572,7 +576,6 @@ static void atmel_stop_tx(struct uart_port *port)
if (atmel_uart_is_half_duplex(port))
if (!atomic_read(&atmel_port->tasklet_shutdown))
atmel_start_rx(port);
-
}
/*
@@ -581,27 +584,31 @@ static void atmel_stop_tx(struct uart_port *port)
static void atmel_start_tx(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ bool is_pdc = atmel_use_pdc_tx(port);
+ bool is_dma = is_pdc || atmel_use_dma_tx(port);
- if (atmel_use_pdc_tx(port) && (atmel_uart_readl(port, ATMEL_PDC_PTSR)
+ if (is_pdc && (atmel_uart_readl(port, ATMEL_PDC_PTSR)
& ATMEL_PDC_TXTEN))
/* The transmitter is already running. Yes, we
really need this.*/
return;
- if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port))
- if (atmel_uart_is_half_duplex(port))
- atmel_stop_rx(port);
+ if (is_dma && atmel_uart_is_half_duplex(port))
+ atmel_stop_rx(port);
- if (atmel_use_pdc_tx(port))
+ if (is_pdc) {
/* re-enable PDC transmit */
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+ }
/* Enable interrupts */
atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
- /* re-enable the transmitter */
- atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
- atmel_port->tx_stopped = false;
+ if (is_dma) {
+ /* re-enable the transmitter */
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN);
+ atmel_port->tx_stopped = false;
+ }
}
/*
@@ -824,30 +831,14 @@ static void atmel_rx_chars(struct uart_port *port)
*/
static void atmel_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ bool pending;
+ u8 ch;
- if (port->x_char &&
- (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY)) {
- atmel_uart_write_char(port, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
- return;
-
- while (atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY) {
- atmel_uart_write_char(port, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (!uart_circ_empty(xmit)) {
+ pending = uart_port_tx(port, ch,
+ atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY,
+ atmel_uart_write_char(port, ch));
+ if (pending) {
/* we still have characters to transmit, so we should continue
* transmitting them when TX is ready, regardless of
* mode or duplexity
@@ -875,10 +866,7 @@ static void atmel_complete_tx_dma(void *arg)
if (chan)
dmaengine_terminate_all(chan);
- xmit->tail += atmel_port->tx_len;
- xmit->tail &= UART_XMIT_SIZE - 1;
-
- port->icount.tx += atmel_port->tx_len;
+ uart_xmit_advance(port, atmel_port->tx_len);
spin_lock_irq(&atmel_port->lock_tx);
async_tx_ack(atmel_port->desc_tx);
@@ -1471,11 +1459,7 @@ static void atmel_tx_pdc(struct uart_port *port)
/* nothing left to transmit? */
if (atmel_uart_readl(port, ATMEL_PDC_TCR))
return;
-
- xmit->tail += pdc->ofs;
- xmit->tail &= UART_XMIT_SIZE - 1;
-
- port->icount.tx += pdc->ofs;
+ uart_xmit_advance(port, pdc->ofs);
pdc->ofs = 0;
/* more to transmit - setup next transfer */
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 5d9737c2d1f2..62bc7244dc67 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -303,53 +303,24 @@ static void bcm_uart_do_rx(struct uart_port *port)
*/
static void bcm_uart_do_tx(struct uart_port *port)
{
- struct circ_buf *xmit;
- unsigned int val, max_count;
-
- if (port->x_char) {
- bcm_uart_writel(port, port->x_char, UART_FIFO_REG);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_tx_stopped(port)) {
- bcm_uart_stop_tx(port);
- return;
- }
-
- xmit = &port->state->xmit;
- if (uart_circ_empty(xmit))
- goto txq_empty;
+ unsigned int val;
+ bool pending;
+ u8 ch;
val = bcm_uart_readl(port, UART_MCTL_REG);
val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT;
- max_count = port->fifosize - val;
-
- while (max_count--) {
- unsigned int c;
- c = xmit->buf[xmit->tail];
- bcm_uart_writel(port, c, UART_FIFO_REG);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (uart_circ_empty(xmit))
- goto txq_empty;
- return;
+ pending = uart_port_tx_limited(port, ch, port->fifosize - val,
+ true,
+ bcm_uart_writel(port, ch, UART_FIFO_REG),
+ ({}));
+ if (pending)
+ return;
-txq_empty:
/* nothing to send, disable transmit interrupt */
val = bcm_uart_readl(port, UART_IR_REG);
val &= ~UART_TX_INT_MASK;
bcm_uart_writel(port, val, UART_IR_REG);
- return;
}
/*
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index 404b43a5ae33..e190dce58f46 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -166,8 +166,7 @@ static irqreturn_t uart_clps711x_int_tx(int irq, void *dev_id)
u32 sysflg = 0;
writew(xmit->buf[xmit->tail], port->membase + UARTDR_OFFSET);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
regmap_read(s->syscon, SYSFLG_OFFSET, &sysflg);
if (sysflg & SYSFLG_UTXFF)
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index b4369ed45ae2..5565f302cb21 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -684,8 +684,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
while (count < pinfo->tx_fifosize) {
*p++ = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
count++;
if (xmit->head == xmit->tail)
break;
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
index 0c0a62346f23..ed197705f7ee 100644
--- a/drivers/tty/serial/digicolor-usart.c
+++ b/drivers/tty/serial/digicolor-usart.c
@@ -202,8 +202,7 @@ static void digicolor_uart_tx(struct uart_port *port)
while (!uart_circ_empty(xmit)) {
writeb(xmit->buf[xmit->tail], port->membase + UA_EMI_REC);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (digicolor_uart_tx_full(port))
break;
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c
index 829b452daee9..6b7ed7f2f3ca 100644
--- a/drivers/tty/serial/dz.c
+++ b/drivers/tty/serial/dz.c
@@ -279,9 +279,8 @@ static inline void dz_transmit_chars(struct dz_mux *mux)
* so we go one char at a time) :-<
*/
tmp = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1);
dz_out(dport, DZ_TDR, tmp);
- dport->port.icount.tx++;
+ uart_xmit_advance(&dport->port, 1);
if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)
uart_write_wakeup(&dport->port);
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c
index 84e8153e5420..6fc21b6684e6 100644
--- a/drivers/tty/serial/fsl_linflexuart.c
+++ b/drivers/tty/serial/fsl_linflexuart.c
@@ -178,8 +178,7 @@ static inline void linflex_transmit_buffer(struct uart_port *sport)
while (!uart_circ_empty(xmit)) {
linflex_put_char(sport, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->icount.tx++;
+ uart_xmit_advance(sport, 1);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 888e01fbd9c5..5e69fb73f570 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -18,6 +18,8 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty_flip.h>
@@ -232,6 +234,7 @@
/* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
#define DMA_RX_TIMEOUT (10)
+#define UART_AUTOSUSPEND_TIMEOUT 3000
#define DRIVER_NAME "fsl-lpuart"
#define DEV_NAME "ttyLP"
@@ -509,9 +512,7 @@ static void lpuart_dma_tx_complete(void *arg)
dma_unmap_sg(chan->device->dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
- xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
-
- sport->port.icount.tx += sport->dma_tx_bytes;
+ uart_xmit_advance(&sport->port, sport->dma_tx_bytes);
sport->dma_tx_in_progress = false;
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -582,7 +583,7 @@ static void lpuart_flush_buffer(struct uart_port *port)
sport->dma_tx_nents, DMA_TO_DEVICE);
sport->dma_tx_in_progress = false;
}
- dmaengine_terminate_all(chan);
+ dmaengine_terminate_async(chan);
}
if (lpuart_is_32(sport)) {
@@ -716,32 +717,12 @@ static int lpuart32_poll_get_char(struct uart_port *port)
static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
-
- if (sport->port.x_char) {
- writeb(sport->port.x_char, sport->port.membase + UARTDR);
- sport->port.icount.tx++;
- sport->port.x_char = 0;
- return;
- }
-
- if (lpuart_stopped_or_empty(&sport->port)) {
- lpuart_stop_tx(&sport->port);
- return;
- }
-
- while (!uart_circ_empty(xmit) &&
- (readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) {
- writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&sport->port);
+ struct uart_port *port = &sport->port;
+ u8 ch;
- if (uart_circ_empty(xmit))
- lpuart_stop_tx(&sport->port);
+ uart_port_tx(port, ch,
+ readb(port->membase + UARTTCFIFO) < sport->txfifo_size,
+ writeb(ch, port->membase + UARTDR));
}
static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
@@ -766,8 +747,7 @@ static inline void lpuart32_transmit_buffer(struct lpuart_port *sport)
txcnt &= UARTWATER_COUNT_MASK;
while (!uart_circ_empty(xmit) && (txcnt < sport->txfifo_size)) {
lpuart32_write(&sport->port, xmit->buf[xmit->tail], UARTDATA);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
+ uart_xmit_advance(&sport->port, 1);
txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
@@ -815,6 +795,20 @@ static void lpuart32_start_tx(struct uart_port *port)
}
}
+static void
+lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+ switch (state) {
+ case UART_PM_STATE_OFF:
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ break;
+ default:
+ pm_runtime_get_sync(port->dev);
+ break;
+ }
+}
+
/* return TIOCSER_TEMT when transmitter is not busy */
static unsigned int lpuart_tx_empty(struct uart_port *port)
{
@@ -1333,7 +1327,7 @@ static void lpuart_dma_rx_free(struct uart_port *port)
struct lpuart_port, port);
struct dma_chan *chan = sport->dma_rx_chan;
- dmaengine_terminate_all(chan);
+ dmaengine_terminate_sync(chan);
dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
kfree(sport->rx_ring.buf);
sport->rx_ring.tail = 0;
@@ -1650,10 +1644,23 @@ err:
sport->lpuart_dma_rx_use = false;
}
+static void lpuart_hw_setup(struct lpuart_port *sport)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ lpuart_setup_watermark_enable(sport);
+
+ lpuart_rx_dma_startup(sport);
+ lpuart_tx_dma_startup(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
static int lpuart_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- unsigned long flags;
unsigned char temp;
/* determine FIFO size and enable FIFO mode */
@@ -1667,15 +1674,7 @@ static int lpuart_startup(struct uart_port *port)
UARTPFIFO_FIFOSIZE_MASK);
lpuart_request_dma(sport);
-
- spin_lock_irqsave(&sport->port.lock, flags);
-
- lpuart_setup_watermark_enable(sport);
-
- lpuart_rx_dma_startup(sport);
- lpuart_tx_dma_startup(sport);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ lpuart_hw_setup(sport);
return 0;
}
@@ -1698,10 +1697,25 @@ static void lpuart32_configure(struct lpuart_port *sport)
lpuart32_write(&sport->port, temp, UARTCTRL);
}
+static void lpuart32_hw_setup(struct lpuart_port *sport)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+
+ lpuart32_setup_watermark_enable(sport);
+
+ lpuart_rx_dma_startup(sport);
+ lpuart_tx_dma_startup(sport);
+
+ lpuart32_configure(sport);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
static int lpuart32_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
- unsigned long flags;
unsigned long temp;
/* determine FIFO size */
@@ -1726,17 +1740,8 @@ static int lpuart32_startup(struct uart_port *port)
}
lpuart_request_dma(sport);
+ lpuart32_hw_setup(sport);
- spin_lock_irqsave(&sport->port.lock, flags);
-
- lpuart32_setup_watermark_enable(sport);
-
- lpuart_rx_dma_startup(sport);
- lpuart_tx_dma_startup(sport);
-
- lpuart32_configure(sport);
-
- spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
@@ -1752,7 +1757,7 @@ static void lpuart_dma_shutdown(struct lpuart_port *sport)
if (wait_event_interruptible_timeout(sport->dma_wait,
!sport->dma_tx_in_progress, msecs_to_jiffies(300)) <= 0) {
sport->dma_tx_in_progress = false;
- dmaengine_terminate_all(sport->dma_tx_chan);
+ dmaengine_terminate_sync(sport->dma_tx_chan);
}
sport->lpuart_dma_tx_use = false;
}
@@ -2240,6 +2245,7 @@ static const struct uart_ops lpuart_pops = {
.startup = lpuart_startup,
.shutdown = lpuart_shutdown,
.set_termios = lpuart_set_termios,
+ .pm = lpuart_uart_pm,
.type = lpuart_type,
.request_port = lpuart_request_port,
.release_port = lpuart_release_port,
@@ -2264,6 +2270,7 @@ static const struct uart_ops lpuart32_pops = {
.startup = lpuart32_startup,
.shutdown = lpuart32_shutdown,
.set_termios = lpuart32_set_termios,
+ .pm = lpuart_uart_pm,
.type = lpuart_type,
.request_port = lpuart_request_port,
.release_port = lpuart_release_port,
@@ -2744,6 +2751,11 @@ static int lpuart_probe(struct platform_device *pdev)
handler = lpuart_int;
}
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
ret = lpuart_global_reset(sport);
if (ret)
goto failed_reset;
@@ -2768,6 +2780,9 @@ failed_irq_request:
failed_attach_port:
failed_get_rs485:
failed_reset:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
lpuart_disable_clks(sport);
return ret;
}
@@ -2786,100 +2801,241 @@ static int lpuart_remove(struct platform_device *pdev)
if (sport->dma_rx_chan)
dma_release_channel(sport->dma_rx_chan);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
return 0;
}
-static int __maybe_unused lpuart_suspend(struct device *dev)
+static int lpuart_runtime_suspend(struct device *dev)
{
- struct lpuart_port *sport = dev_get_drvdata(dev);
- unsigned long temp;
- bool irq_wake;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
- if (lpuart_is_32(sport)) {
- /* disable Rx/Tx and interrupts */
- temp = lpuart32_read(&sport->port, UARTCTRL);
- temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
- lpuart32_write(&sport->port, temp, UARTCTRL);
- } else {
- /* disable Rx/Tx and interrupts */
- temp = readb(sport->port.membase + UARTCR2);
- temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
- writeb(temp, sport->port.membase + UARTCR2);
- }
+ lpuart_disable_clks(sport);
- uart_suspend_port(&lpuart_reg, &sport->port);
+ return 0;
+};
- /* uart_suspend_port() might set wakeup flag */
- irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
+static int lpuart_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpuart_port *sport = platform_get_drvdata(pdev);
- if (sport->lpuart_dma_rx_use) {
- /*
- * EDMA driver during suspend will forcefully release any
- * non-idle DMA channels. If port wakeup is enabled or if port
- * is console port or 'no_console_suspend' is set the Rx DMA
- * cannot resume as expected, hence gracefully release the
- * Rx DMA path before suspend and start Rx DMA path on resume.
- */
- if (irq_wake) {
- del_timer_sync(&sport->lpuart_timer);
- lpuart_dma_rx_free(&sport->port);
- }
+ return lpuart_enable_clks(sport);
+};
- /* Disable Rx DMA to use UART port as wakeup source */
- if (lpuart_is_32(sport)) {
- temp = lpuart32_read(&sport->port, UARTBAUD);
- lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
- UARTBAUD);
+static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on)
+{
+ unsigned int val, baud;
+
+ if (lpuart_is_32(sport)) {
+ val = lpuart32_read(&sport->port, UARTCTRL);
+ baud = lpuart32_read(&sport->port, UARTBAUD);
+ if (on) {
+ /* set rx_watermark to 0 in wakeup source mode */
+ lpuart32_write(&sport->port, 0, UARTWATER);
+ val |= UARTCTRL_RIE;
+ /* clear RXEDGIF flag before enable RXEDGIE interrupt */
+ lpuart32_write(&sport->port, UARTSTAT_RXEDGIF, UARTSTAT);
+ baud |= UARTBAUD_RXEDGIE;
} else {
- writeb(readb(sport->port.membase + UARTCR5) &
- ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+ val &= ~UARTCTRL_RIE;
+ baud &= ~UARTBAUD_RXEDGIE;
}
+ lpuart32_write(&sport->port, val, UARTCTRL);
+ lpuart32_write(&sport->port, baud, UARTBAUD);
+ } else {
+ val = readb(sport->port.membase + UARTCR2);
+ if (on)
+ val |= UARTCR2_RIE;
+ else
+ val &= ~UARTCR2_RIE;
+ writeb(val, sport->port.membase + UARTCR2);
}
+}
- if (sport->lpuart_dma_tx_use) {
- sport->dma_tx_in_progress = false;
- dmaengine_terminate_all(sport->dma_tx_chan);
+static bool lpuart_uport_is_active(struct lpuart_port *sport)
+{
+ struct tty_port *port = &sport->port.state->port;
+ struct tty_struct *tty;
+ struct device *tty_dev;
+ int may_wake = 0;
+
+ tty = tty_port_tty_get(port);
+ if (tty) {
+ tty_dev = tty->dev;
+ may_wake = device_may_wakeup(tty_dev);
+ tty_kref_put(tty);
}
- if (sport->port.suspended && !irq_wake)
- lpuart_disable_clks(sport);
+ if ((tty_port_initialized(port) && may_wake) ||
+ (!console_suspend_enabled && uart_console(&sport->port)))
+ return true;
- return 0;
+ return false;
}
-static int __maybe_unused lpuart_resume(struct device *dev)
+static int lpuart_suspend_noirq(struct device *dev)
{
struct lpuart_port *sport = dev_get_drvdata(dev);
bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
- if (sport->port.suspended && !irq_wake)
- lpuart_enable_clks(sport);
+ if (lpuart_uport_is_active(sport))
+ serial_lpuart_enable_wakeup(sport, !!irq_wake);
- if (lpuart_is_32(sport))
- lpuart32_setup_watermark_enable(sport);
- else
- lpuart_setup_watermark_enable(sport);
+ pinctrl_pm_select_sleep_state(dev);
- if (sport->lpuart_dma_rx_use) {
- if (irq_wake) {
- if (!lpuart_start_rx_dma(sport))
- rx_dma_timer_init(sport);
- else
- sport->lpuart_dma_rx_use = false;
+ return 0;
+}
+
+static int lpuart_resume_noirq(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned int val;
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (lpuart_uport_is_active(sport)) {
+ serial_lpuart_enable_wakeup(sport, false);
+
+ /* clear the wakeup flags */
+ if (lpuart_is_32(sport)) {
+ val = lpuart32_read(&sport->port, UARTSTAT);
+ lpuart32_write(&sport->port, val, UARTSTAT);
}
}
- lpuart_tx_dma_startup(sport);
+ return 0;
+}
- if (lpuart_is_32(sport))
- lpuart32_configure(sport);
+static int lpuart_suspend(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ unsigned long temp, flags;
+ uart_suspend_port(&lpuart_reg, &sport->port);
+
+ if (lpuart_uport_is_active(sport)) {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ /* disable Rx/Tx and interrupts */
+ temp = lpuart32_read(&sport->port, UARTCTRL);
+ temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+ } else {
+ /* disable Rx/Tx and interrupts */
+ temp = readb(sport->port.membase + UARTCR2);
+ temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
+ writeb(temp, sport->port.membase + UARTCR2);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ if (sport->lpuart_dma_rx_use) {
+ /*
+ * EDMA driver during suspend will forcefully release any
+ * non-idle DMA channels. If port wakeup is enabled or if port
+ * is console port or 'no_console_suspend' is set the Rx DMA
+ * cannot resume as expected, hence gracefully release the
+ * Rx DMA path before suspend and start Rx DMA path on resume.
+ */
+ del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
+
+ /* Disable Rx DMA to use UART port as wakeup source */
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTBAUD);
+ lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
+ UARTBAUD);
+ } else {
+ writeb(readb(sport->port.membase + UARTCR5) &
+ ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ }
+
+ if (sport->lpuart_dma_tx_use) {
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTBAUD);
+ temp &= ~UARTBAUD_TDMAE;
+ lpuart32_write(&sport->port, temp, UARTBAUD);
+ } else {
+ temp = readb(sport->port.membase + UARTCR5);
+ temp &= ~UARTCR5_TDMAS;
+ writeb(temp, sport->port.membase + UARTCR5);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ sport->dma_tx_in_progress = false;
+ dmaengine_terminate_sync(sport->dma_tx_chan);
+ }
+ } else if (pm_runtime_active(sport->port.dev)) {
+ lpuart_disable_clks(sport);
+ pm_runtime_disable(sport->port.dev);
+ pm_runtime_set_suspended(sport->port.dev);
+ }
+
+ return 0;
+}
+
+static void lpuart_console_fixup(struct lpuart_port *sport)
+{
+ struct tty_port *port = &sport->port.state->port;
+ struct uart_port *uport = &sport->port;
+ struct ktermios termios;
+
+ /* i.MX7ULP enter VLLS mode that lpuart module power off and registers
+ * all lost no matter the port is wakeup source.
+ * For console port, console baud rate setting lost and print messy
+ * log when enable the console port as wakeup source. To avoid the
+ * issue happen, user should not enable uart port as wakeup source
+ * in VLLS mode, or restore console setting here.
+ */
+ if (is_imx7ulp_lpuart(sport) && lpuart_uport_is_active(sport) &&
+ console_suspend_enabled && uart_console(&sport->port)) {
+
+ mutex_lock(&port->mutex);
+ memset(&termios, 0, sizeof(struct ktermios));
+ termios.c_cflag = uport->cons->cflag;
+ if (port->tty && termios.c_cflag == 0)
+ termios = port->tty->termios;
+ uport->ops->set_termios(uport, &termios, NULL);
+ mutex_unlock(&port->mutex);
+ }
+}
+
+static int lpuart_resume(struct device *dev)
+{
+ struct lpuart_port *sport = dev_get_drvdata(dev);
+ int ret;
+
+ if (lpuart_uport_is_active(sport)) {
+ if (lpuart_is_32(sport))
+ lpuart32_hw_setup(sport);
+ else
+ lpuart_hw_setup(sport);
+ } else if (pm_runtime_active(sport->port.dev)) {
+ ret = lpuart_enable_clks(sport);
+ if (ret)
+ return ret;
+ pm_runtime_set_active(sport->port.dev);
+ pm_runtime_enable(sport->port.dev);
+ }
+
+ lpuart_console_fixup(sport);
uart_resume_port(&lpuart_reg, &sport->port);
return 0;
}
-static SIMPLE_DEV_PM_OPS(lpuart_pm_ops, lpuart_suspend, lpuart_resume);
+static const struct dev_pm_ops lpuart_pm_ops = {
+ RUNTIME_PM_OPS(lpuart_runtime_suspend,
+ lpuart_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(lpuart_suspend_noirq,
+ lpuart_resume_noirq)
+ SYSTEM_SLEEP_PM_OPS(lpuart_suspend, lpuart_resume)
+};
static struct platform_driver lpuart_driver = {
.probe = lpuart_probe,
@@ -2887,7 +3043,7 @@ static struct platform_driver lpuart_driver = {
.driver = {
.name = "fsl-lpuart",
.of_match_table = lpuart_dt_ids,
- .pm = &lpuart_pm_ops,
+ .pm = pm_ptr(&lpuart_pm_ops),
},
};
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index aadda66405b4..757825edb0cd 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -489,7 +489,7 @@ static void imx_uart_stop_tx(struct uart_port *port)
static void imx_uart_stop_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- u32 ucr1, ucr2, ucr4;
+ u32 ucr1, ucr2, ucr4, uts;
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
@@ -505,7 +505,18 @@ static void imx_uart_stop_rx(struct uart_port *port)
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, ucr4, UCR4);
- ucr2 &= ~UCR2_RXEN;
+ /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
+ if (port->rs485.flags & SER_RS485_ENABLED &&
+ port->rs485.flags & SER_RS485_RTS_ON_SEND &&
+ sport->have_rtscts && !sport->have_rtsgpio) {
+ uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
+ uts |= UTS_LOOP;
+ imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
+ ucr2 |= UCR2_RXEN;
+ } else {
+ ucr2 &= ~UCR2_RXEN;
+ }
+
imx_uart_writel(sport, ucr2, UCR2);
}
@@ -563,8 +574,7 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
/* send xmit->buf[xmit->tail]
* out the port here */
imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
+ uart_xmit_advance(&sport->port, 1);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -590,9 +600,7 @@ static void imx_uart_dma_tx_callback(void *data)
ucr1 &= ~UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
- /* update the stat */
- xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx += sport->tx_bytes;
+ uart_xmit_advance(&sport->port, sport->tx_bytes);
dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
@@ -1393,7 +1401,7 @@ static int imx_uart_startup(struct uart_port *port)
int retval, i;
unsigned long flags;
int dma_is_inited = 0;
- u32 ucr1, ucr2, ucr3, ucr4;
+ u32 ucr1, ucr2, ucr3, ucr4, uts;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
@@ -1498,6 +1506,11 @@ static int imx_uart_startup(struct uart_port *port)
imx_uart_writel(sport, ucr2, UCR2);
}
+ /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
+ uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
+ uts &= ~UTS_LOOP;
+ imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
+
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
@@ -1507,7 +1520,7 @@ static void imx_uart_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- u32 ucr1, ucr2, ucr4;
+ u32 ucr1, ucr2, ucr4, uts;
if (sport->dma_is_enabled) {
dmaengine_terminate_sync(sport->dma_chan_tx);
@@ -1551,7 +1564,18 @@ static void imx_uart_shutdown(struct uart_port *port)
spin_lock_irqsave(&sport->port.lock, flags);
ucr1 = imx_uart_readl(sport, UCR1);
- ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
+ /* See SER_RS485_ENABLED/UTS_LOOP comment in imx_uart_probe() */
+ if (port->rs485.flags & SER_RS485_ENABLED &&
+ port->rs485.flags & SER_RS485_RTS_ON_SEND &&
+ sport->have_rtscts && !sport->have_rtsgpio) {
+ uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
+ uts |= UTS_LOOP;
+ imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
+ ucr1 |= UCR1_UARTEN;
+ } else {
+ ucr1 &= ~UCR1_UARTEN;
+ }
imx_uart_writel(sport, ucr1, UCR1);
ucr4 = imx_uart_readl(sport, UCR4);
@@ -2213,7 +2237,7 @@ static int imx_uart_probe(struct platform_device *pdev)
void __iomem *base;
u32 dma_buf_conf[2];
int ret = 0;
- u32 ucr1;
+ u32 ucr1, ucr2, uts;
struct resource *res;
int txirq, rxirq, rtsirq;
@@ -2350,6 +2374,31 @@ static int imx_uart_probe(struct platform_device *pdev)
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
+ /*
+ * In case RS485 is enabled without GPIO RTS control, the UART IP
+ * is used to control CTS signal. Keep both the UART and Receiver
+ * enabled, otherwise the UART IP pulls CTS signal always HIGH no
+ * matter how the UCR2 CTSC and CTS bits are set. To prevent any
+ * data from being fed into the RX FIFO, enable loopback mode in
+ * UTS register, which disconnects the RX path from external RXD
+ * pin and connects it to the Transceiver, which is disabled, so
+ * no data can be fed to the RX FIFO that way.
+ */
+ if (sport->port.rs485.flags & SER_RS485_ENABLED &&
+ sport->have_rtscts && !sport->have_rtsgpio) {
+ uts = imx_uart_readl(sport, imx_uart_uts_reg(sport));
+ uts |= UTS_LOOP;
+ imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
+
+ ucr1 = imx_uart_readl(sport, UCR1);
+ ucr1 |= UCR1_UARTEN;
+ imx_uart_writel(sport, ucr1, UCR1);
+
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 |= UCR2_RXEN;
+ imx_uart_writel(sport, ucr2, UCR2);
+ }
+
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
/*
* The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c
index dd0a8915ce4f..b1f27e168135 100644
--- a/drivers/tty/serial/ip22zilog.c
+++ b/drivers/tty/serial/ip22zilog.c
@@ -409,8 +409,7 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
ZSDELAY();
ZS_WSYNC(channel);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
@@ -609,8 +608,7 @@ static void ip22zilog_start_tx(struct uart_port *port)
ZSDELAY();
ZS_WSYNC(channel);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index c892f3c7d1ab..a58e9277dfad 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -95,7 +95,6 @@
#define ASCFSTAT_TXFFLMASK 0x3F00
#define ASCFSTAT_TXFREEMASK 0x3F000000
-static void lqasc_tx_chars(struct uart_port *port);
static struct ltq_uart_port *lqasc_port[MAXPORTS];
static struct uart_driver lqasc_reg;
@@ -151,9 +150,12 @@ lqasc_start_tx(struct uart_port *port)
{
unsigned long flags;
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+ u8 ch;
spin_lock_irqsave(&ltq_port->lock, flags);
- lqasc_tx_chars(port);
+ uart_port_tx(port, ch,
+ lqasc_tx_ready(port),
+ writeb(ch, port->membase + LTQ_ASC_TBUF));
spin_unlock_irqrestore(&ltq_port->lock, flags);
return;
}
@@ -226,36 +228,6 @@ lqasc_rx_chars(struct uart_port *port)
return 0;
}
-static void
-lqasc_tx_chars(struct uart_port *port)
-{
- struct circ_buf *xmit = &port->state->xmit;
- if (uart_tx_stopped(port)) {
- lqasc_stop_tx(port);
- return;
- }
-
- while (lqasc_tx_ready(port)) {
- if (port->x_char) {
- writeb(port->x_char, port->membase + LTQ_ASC_TBUF);
- port->icount.tx++;
- port->x_char = 0;
- continue;
- }
-
- if (uart_circ_empty(xmit))
- break;
-
- writeb(port->state->xmit.buf[port->state->xmit.tail],
- port->membase + LTQ_ASC_TBUF);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-}
-
static irqreturn_t
lqasc_tx_int(int irq, void *_port)
{
diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c
index 4c0604325ee9..062812fe1b09 100644
--- a/drivers/tty/serial/liteuart.c
+++ b/drivers/tty/serial/liteuart.c
@@ -136,8 +136,7 @@ static void liteuart_start_tx(struct uart_port *port)
} else if (!uart_circ_empty(xmit)) {
while (xmit->head != xmit->tail) {
ch = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
liteuart_putchar(port, ch);
}
}
diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c
index ed47f4768338..b38fe4728c26 100644
--- a/drivers/tty/serial/lpc32xx_hs.c
+++ b/drivers/tty/serial/lpc32xx_hs.c
@@ -276,8 +276,6 @@ static void __serial_lpc32xx_rx(struct uart_port *port)
tty_flip_buffer_push(tport);
}
-static void serial_lpc32xx_stop_tx(struct uart_port *port);
-
static bool serial_lpc32xx_tx_ready(struct uart_port *port)
{
u32 level = readl(LPC32XX_HSUART_LEVEL(port->membase));
@@ -287,34 +285,11 @@ static bool serial_lpc32xx_tx_ready(struct uart_port *port)
static void __serial_lpc32xx_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
-
- if (port->x_char) {
- writel((u32)port->x_char, LPC32XX_HSUART_FIFO(port->membase));
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
- goto exit_tx;
-
- /* Transfer data */
- while (serial_lpc32xx_tx_ready(port)) {
- writel((u32) xmit->buf[xmit->tail],
- LPC32XX_HSUART_FIFO(port->membase));
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
-exit_tx:
- if (uart_circ_empty(xmit))
- serial_lpc32xx_stop_tx(port);
+ uart_port_tx(port, ch,
+ serial_lpc32xx_tx_ready(port),
+ writel(ch, LPC32XX_HSUART_FIFO(port->membase)));
}
static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id)
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index c69602f356fd..bb74f23251fe 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -292,9 +292,7 @@ static void max3100_work(struct work_struct *w)
} else if (!uart_circ_empty(xmit) &&
!uart_tx_stopped(&s->port)) {
tx = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) &
- (UART_XMIT_SIZE - 1);
- s->port.icount.tx++;
+ uart_xmit_advance(&s->port, 1);
}
if (tx != 0xffff) {
max3100_calc_parity(s, &tx);
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index fbf6e2b3161c..4eb24e3407f8 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -787,10 +787,7 @@ static void max310x_handle_tx(struct uart_port *port)
} else {
max310x_batch_write(port, xmit->buf + xmit->tail, to_send);
}
-
- /* Add data to send */
- port->icount.tx += to_send;
- xmit->tail = (xmit->tail + to_send) & (UART_XMIT_SIZE - 1);
+ uart_xmit_advance(port, to_send);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c
index b1cd9a76dd93..3239babe12a4 100644
--- a/drivers/tty/serial/mcf.c
+++ b/drivers/tty/serial/mcf.c
@@ -327,34 +327,16 @@ static void mcf_rx_chars(struct mcf_uart *pp)
static void mcf_tx_chars(struct mcf_uart *pp)
{
struct uart_port *port = &pp->port;
- struct circ_buf *xmit = &port->state->xmit;
-
- if (port->x_char) {
- /* Send special char - probably flow control */
- writeb(port->x_char, port->membase + MCFUART_UTB);
- port->x_char = 0;
- port->icount.tx++;
- return;
- }
-
- while (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) {
- if (uart_circ_empty(xmit))
- break;
- writeb(xmit->buf[xmit->tail], port->membase + MCFUART_UTB);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1);
- port->icount.tx++;
- }
+ bool pending;
+ u8 ch;
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ pending = uart_port_tx(port, ch,
+ readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY,
+ writeb(ch, port->membase + MCFUART_UTB));
- if (uart_circ_empty(xmit)) {
- mcf_stop_tx(port);
- /* Disable TX to negate RTS automatically */
- if (port->rs485.flags & SER_RS485_ENABLED)
- writeb(MCFUART_UCR_TXDISABLE,
- port->membase + MCFUART_UCR);
- }
+ /* Disable TX to negate RTS automatically */
+ if (!pending && (port->rs485.flags & SER_RS485_ENABLED))
+ writeb(MCFUART_UCR_TXDISABLE, port->membase + MCFUART_UCR);
}
/****************************************************************************/
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
index 3690f5cf0f43..d2502aaa3e8c 100644
--- a/drivers/tty/serial/men_z135_uart.c
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -352,11 +352,8 @@ static void men_z135_handle_tx(struct men_z135_port *uart)
n = min(n, s);
memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n);
- xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1);
-
iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL);
-
- port->icount.tx += n;
+ uart_xmit_advance(port, n);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 056243c12836..74110017988a 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -162,8 +162,7 @@ static void meson_uart_start_tx(struct uart_port *port)
ch = xmit->buf[xmit->tail];
writel(ch, port->membase + AML_UART_WFIFO);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
if (!uart_circ_empty(xmit)) {
diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c
index c15e0d84dc7e..44988a2941b8 100644
--- a/drivers/tty/serial/milbeaut_usio.c
+++ b/drivers/tty/serial/milbeaut_usio.c
@@ -98,8 +98,7 @@ static void mlb_usio_tx_chars(struct uart_port *port)
do {
writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_empty(xmit))
break;
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index 73362d4bc45d..384ca195e3d5 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -1428,42 +1428,11 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port)
static inline bool
mpc52xx_uart_int_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
-
- /* Process out of band chars */
- if (port->x_char) {
- psc_ops->write_char(port, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- return true;
- }
-
- /* Nothing to do ? */
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- mpc52xx_uart_stop_tx(port);
- return false;
- }
-
- /* Send chars */
- while (psc_ops->raw_tx_rdy(port)) {
- psc_ops->write_char(port, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- }
-
- /* Wake up */
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- /* Maybe we're done after all */
- if (uart_circ_empty(xmit)) {
- mpc52xx_uart_stop_tx(port);
- return false;
- }
+ u8 ch;
- return true;
+ return uart_port_tx(port, ch,
+ psc_ops->raw_tx_rdy(port),
+ psc_ops->write_char(port, ch));
}
static irqreturn_t
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
index 2e3e6cf16817..860d161fa594 100644
--- a/drivers/tty/serial/mps2-uart.c
+++ b/drivers/tty/serial/mps2-uart.c
@@ -129,29 +129,11 @@ static void mps2_uart_stop_tx(struct uart_port *port)
static void mps2_uart_tx_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
-
- while (!(mps2_uart_read8(port, UARTn_STATE) & UARTn_STATE_TX_FULL)) {
- if (port->x_char) {
- mps2_uart_write8(port, port->x_char, UARTn_DATA);
- port->x_char = 0;
- port->icount.tx++;
- continue;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port))
- break;
-
- mps2_uart_write8(port, xmit->buf[xmit->tail], UARTn_DATA);
- xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE;
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- mps2_uart_stop_tx(port);
+ uart_port_tx(port, ch,
+ mps2_uart_tx_empty(port),
+ mps2_uart_write8(port, ch, UARTn_DATA));
}
static void mps2_uart_start_tx(struct uart_port *port)
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 7dd19a281579..843798e63084 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -464,12 +464,9 @@ static void msm_complete_tx_dma(void *args)
}
count = dma->count - state.residue;
- port->icount.tx += count;
+ uart_xmit_advance(port, count);
dma->count = 0;
- xmit->tail += count;
- xmit->tail &= UART_XMIT_SIZE - 1;
-
/* Restore "Tx FIFO below watermark" interrupt */
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
@@ -819,7 +816,7 @@ static void msm_handle_rx(struct uart_port *port)
port->icount.rx++;
}
- /* Mask conditions we're ignorning. */
+ /* Mask conditions we're ignoring. */
sr &= port->read_status_mask;
if (sr & MSM_UART_SR_RX_BREAK)
@@ -866,13 +863,11 @@ static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
else
num_chars = 1;
- for (i = 0; i < num_chars; i++) {
+ for (i = 0; i < num_chars; i++)
buf[i] = xmit->buf[xmit->tail + i];
- port->icount.tx++;
- }
iowrite32_rep(tf, buf, 1);
- xmit->tail = (xmit->tail + num_chars) & (UART_XMIT_SIZE - 1);
+ uart_xmit_advance(port, num_chars);
tf_pointer += num_chars;
}
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index ed0e763f622a..85ce1e9af44a 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -171,6 +171,13 @@ static void mux_break_ctl(struct uart_port *port, int break_state)
{
}
+static void mux_tx_done(struct uart_port *port)
+{
+ /* FIXME js: really needs to wait? */
+ while (UART_GET_FIFO_CNT(port))
+ udelay(1);
+}
+
/**
* mux_write - Write chars to the mux fifo.
* @port: Ptr to the uart_port.
@@ -180,39 +187,13 @@ static void mux_break_ctl(struct uart_port *port, int break_state)
*/
static void mux_write(struct uart_port *port)
{
- int count;
- struct circ_buf *xmit = &port->state->xmit;
-
- if(port->x_char) {
- UART_PUT_CHAR(port, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if(uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- mux_stop_tx(port);
- return;
- }
-
- count = (port->fifosize) - UART_GET_FIFO_CNT(port);
- do {
- UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if(uart_circ_empty(xmit))
- break;
-
- } while(--count > 0);
-
- while(UART_GET_FIFO_CNT(port))
- udelay(1);
-
- if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- mux_stop_tx(port);
+ uart_port_tx_limited(port, ch,
+ port->fifosize - UART_GET_FIFO_CNT(port),
+ true,
+ UART_PUT_CHAR(port, ch),
+ mux_tx_done(port));
}
/**
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index ba16e1da6bd3..31f739c7a08b 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -223,8 +223,7 @@ static void mvebu_uart_start_tx(struct uart_port *port)
if (IS_EXTENDED(port) && !uart_circ_empty(xmit)) {
writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
ctl = readl(port->membase + UART_INTR(port));
@@ -335,40 +334,12 @@ ignore_char:
static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int count;
- unsigned int st;
-
- if (port->x_char) {
- writel(port->x_char, port->membase + UART_TSH(port));
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- mvebu_uart_stop_tx(port);
- return;
- }
-
- for (count = 0; count < port->fifosize; count++) {
- writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
-
- if (uart_circ_empty(xmit))
- break;
-
- st = readl(port->membase + UART_STAT);
- if (st & STAT_TX_FIFO_FUL)
- break;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- mvebu_uart_stop_tx(port);
+ uart_port_tx_limited(port, ch, port->fifosize,
+ !(readl(port->membase + UART_STAT) & STAT_TX_FIFO_FUL),
+ writel(ch, port->membase + UART_TSH(port)),
+ ({}));
}
static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index d21a4f3ef2fe..ef6e7bb6105c 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -569,6 +569,8 @@ static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size)
static void mxs_auart_tx_chars(struct mxs_auart_port *s)
{
struct circ_buf *xmit = &s->port.state->xmit;
+ bool pending;
+ u8 ch;
if (auart_dma_enabled(s)) {
u32 i = 0;
@@ -603,31 +605,13 @@ static void mxs_auart_tx_chars(struct mxs_auart_port *s)
return;
}
-
- while (!(mxs_read(s, REG_STAT) & AUART_STAT_TXFF)) {
- if (s->port.x_char) {
- s->port.icount.tx++;
- mxs_write(s->port.x_char, s, REG_DATA);
- s->port.x_char = 0;
- continue;
- }
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
- s->port.icount.tx++;
- mxs_write(xmit->buf[xmit->tail], s, REG_DATA);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- } else
- break;
- }
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&s->port);
-
- if (uart_circ_empty(&(s->port.state->xmit)))
- mxs_clr(AUART_INTR_TXIEN, s, REG_INTR);
- else
+ pending = uart_port_tx(&s->port, ch,
+ !(mxs_read(s, REG_STAT) & AUART_STAT_TXFF),
+ mxs_write(ch, s, REG_DATA));
+ if (pending)
mxs_set(AUART_INTR_TXIEN, s, REG_INTR);
-
- if (uart_tx_stopped(&s->port))
- mxs_auart_stop_tx(&s->port);
+ else
+ mxs_clr(AUART_INTR_TXIEN, s, REG_INTR);
}
static void mxs_auart_rx_char(struct mxs_auart_port *s)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 7d0d2718ef59..82d35dbbfa6c 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -347,34 +347,12 @@ static void serial_omap_put_char(struct uart_omap_port *up, unsigned char ch)
static void transmit_chars(struct uart_omap_port *up, unsigned int lsr)
{
- struct circ_buf *xmit = &up->port.state->xmit;
- int count;
+ u8 ch;
- if (up->port.x_char) {
- serial_omap_put_char(up, up->port.x_char);
- up->port.icount.tx++;
- up->port.x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- serial_omap_stop_tx(&up->port);
- return;
- }
- count = up->port.fifosize / 4;
- do {
- serial_omap_put_char(up, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
-
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-
- if (uart_circ_empty(xmit))
- serial_omap_stop_tx(&up->port);
+ uart_port_tx_limited(&up->port, ch, up->port.fifosize / 4,
+ true,
+ serial_omap_put_char(up, ch),
+ ({}));
}
static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c
index fde39cc1145d..e99970a9437f 100644
--- a/drivers/tty/serial/owl-uart.c
+++ b/drivers/tty/serial/owl-uart.c
@@ -181,35 +181,11 @@ static void owl_uart_start_tx(struct uart_port *port)
static void owl_uart_send_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int ch;
-
- if (port->x_char) {
- while (!(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU))
- cpu_relax();
- owl_uart_write(port, port->x_char, OWL_UART_TXDAT);
- port->icount.tx++;
- port->x_char = 0;
- }
-
- if (uart_tx_stopped(port))
- return;
-
- while (!(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)) {
- if (uart_circ_empty(xmit))
- break;
+ u8 ch;
- ch = xmit->buf[xmit->tail];
- owl_uart_write(port, ch, OWL_UART_TXDAT);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (uart_circ_empty(xmit))
- owl_uart_stop_tx(port);
+ uart_port_tx(port, ch,
+ !(owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU),
+ owl_uart_write(port, ch, OWL_UART_TXDAT));
}
static void owl_uart_receive_chars(struct uart_port *port)
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index c59ce7886579..3d54a43768cd 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -694,6 +694,7 @@ static void pch_request_dma(struct uart_port *port)
if (!chan) {
dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n",
__func__);
+ pci_dev_put(dma_dev);
return;
}
priv->chan_tx = chan;
@@ -710,6 +711,7 @@ static void pch_request_dma(struct uart_port *port)
__func__);
dma_release_channel(priv->chan_tx);
priv->chan_tx = NULL;
+ pci_dev_put(dma_dev);
return;
}
@@ -717,6 +719,8 @@ static void pch_request_dma(struct uart_port *port)
priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize,
&priv->rx_buf_dma, GFP_KERNEL);
priv->chan_rx = chan;
+
+ pci_dev_put(dma_dev);
}
static void pch_dma_rx_complete(void *arg)
@@ -738,15 +742,12 @@ static void pch_dma_tx_complete(void *arg)
{
struct eg20t_port *priv = arg;
struct uart_port *port = &priv->port;
- struct circ_buf *xmit = &port->state->xmit;
struct scatterlist *sg = priv->sg_tx_p;
int i;
- for (i = 0; i < priv->nent; i++, sg++) {
- xmit->tail += sg_dma_len(sg);
- port->icount.tx += sg_dma_len(sg);
- }
- xmit->tail &= UART_XMIT_SIZE - 1;
+ for (i = 0; i < priv->nent; i++, sg++)
+ uart_xmit_advance(port, sg_dma_len(sg));
+
async_tx_ack(priv->desc_tx);
dma_unmap_sg(port->dev, sg, priv->orig_nent, DMA_TO_DEVICE);
priv->tx_dma_use = 0;
@@ -843,8 +844,7 @@ static unsigned int handle_tx(struct eg20t_port *priv)
while (!uart_tx_stopped(port) && !uart_circ_empty(xmit) && fifo_size) {
iowrite8(xmit->buf[xmit->tail], priv->membase + PCH_UART_THR);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
fifo_size--;
tx_empty = 0;
}
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
index c38754d593ca..ba3435263c1f 100644
--- a/drivers/tty/serial/pic32_uart.c
+++ b/drivers/tty/serial/pic32_uart.c
@@ -376,8 +376,7 @@ static void pic32_uart_do_tx(struct uart_port *port)
pic32_uart_writel(sport, PIC32_UART_TX, c);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_empty(xmit))
break;
if (--max_count == 0)
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index fe2e4ec423f7..13668ffdb1e7 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -410,8 +410,7 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap)
write_zsdata(uap, xmit->buf[xmit->tail]);
zssync(uap);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- uap->port.icount.tx++;
+ uart_xmit_advance(&uap->port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
@@ -627,8 +626,7 @@ static void pmz_start_tx(struct uart_port *port)
return;
write_zsdata(uap, xmit->buf[xmit->tail]);
zssync(uap);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 2d25231fad84..444fa4b654ac 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -174,35 +174,12 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status)
static void transmit_chars(struct uart_pxa_port *up)
{
- struct circ_buf *xmit = &up->port.state->xmit;
- int count;
+ u8 ch;
- if (up->port.x_char) {
- serial_out(up, UART_TX, up->port.x_char);
- up->port.icount.tx++;
- up->port.x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
- serial_pxa_stop_tx(&up->port);
- return;
- }
-
- count = up->port.fifosize / 2;
- do {
- serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
-
-
- if (uart_circ_empty(xmit))
- serial_pxa_stop_tx(&up->port);
+ uart_port_tx_limited(&up->port, ch, up->port.fifosize / 2,
+ true,
+ serial_out(up, UART_TX, ch),
+ ({}));
}
static void serial_pxa_start_tx(struct uart_port *port)
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 83b66b73303a..b487823f0e61 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -924,6 +924,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
false, true, true);
geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
geni_se_select_mode(&port->se, GENI_SE_FIFO);
+ qcom_geni_serial_start_rx(uport);
port->setup = true;
return 0;
@@ -1547,9 +1548,43 @@ static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev)
return ret;
}
+static int qcom_geni_serial_sys_hib_resume(struct device *dev)
+{
+ int ret = 0;
+ struct uart_port *uport;
+ struct qcom_geni_private_data *private_data;
+ struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
+
+ uport = &port->uport;
+ private_data = uport->private_data;
+
+ if (uart_console(uport)) {
+ geni_icc_set_tag(&port->se, 0x7);
+ geni_icc_set_bw(&port->se);
+ ret = uart_resume_port(private_data->drv, uport);
+ /*
+ * For hibernation usecase clients for
+ * console UART won't call port setup during restore,
+ * hence call port setup for console uart.
+ */
+ qcom_geni_serial_port_setup(uport);
+ } else {
+ /*
+ * Peripheral register settings are lost during hibernation.
+ * Update setup flag such that port setup happens again
+ * during next session. Clients of HS-UART will close and
+ * open the port during hibernation.
+ */
+ port->setup = false;
+ }
+ return ret;
+}
+
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend,
qcom_geni_serial_sys_resume)
+ .restore = qcom_geni_serial_sys_hib_resume,
+ .thaw = qcom_geni_serial_sys_hib_resume,
};
static const struct of_device_id qcom_geni_serial_match_table[] = {
diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c
index 0e387e2144fa..be5c842b5ba9 100644
--- a/drivers/tty/serial/rda-uart.c
+++ b/drivers/tty/serial/rda-uart.c
@@ -353,8 +353,7 @@ static void rda_uart_send_chars(struct uart_port *port)
ch = xmit->buf[xmit->tail];
rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/rp2.c b/drivers/tty/serial/rp2.c
index b81afb06f1f4..749b873a5d99 100644
--- a/drivers/tty/serial/rp2.c
+++ b/drivers/tty/serial/rp2.c
@@ -427,32 +427,13 @@ static void rp2_rx_chars(struct rp2_uart_port *up)
static void rp2_tx_chars(struct rp2_uart_port *up)
{
- u16 max_tx = FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT);
- struct circ_buf *xmit = &up->port.state->xmit;
+ u8 ch;
- if (uart_tx_stopped(&up->port)) {
- rp2_uart_stop_tx(&up->port);
- return;
- }
-
- for (; max_tx != 0; max_tx--) {
- if (up->port.x_char) {
- writeb(up->port.x_char, up->base + RP2_DATA_BYTE);
- up->port.x_char = 0;
- up->port.icount.tx++;
- continue;
- }
- if (uart_circ_empty(xmit)) {
- rp2_uart_stop_tx(&up->port);
- break;
- }
- writeb(xmit->buf[xmit->tail], up->base + RP2_DATA_BYTE);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&up->port);
+ uart_port_tx_limited(&up->port, ch,
+ FIFO_SIZE - readw(up->base + RP2_TX_FIFO_COUNT),
+ true,
+ writeb(ch, up->base + RP2_DATA_BYTE),
+ ({}));
}
static void rp2_ch_interrupt(struct rp2_uart_port *up)
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index dd9e3253cab4..55107bbc00ce 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -228,14 +228,7 @@ sa1100_rx_chars(struct sa1100_port *sport)
static void sa1100_tx_chars(struct sa1100_port *sport)
{
- struct circ_buf *xmit = &sport->port.state->xmit;
-
- if (sport->port.x_char) {
- UART_PUT_CHAR(sport, sport->port.x_char);
- sport->port.icount.tx++;
- sport->port.x_char = 0;
- return;
- }
+ u8 ch;
/*
* Check the modem control lines before
@@ -243,28 +236,9 @@ static void sa1100_tx_chars(struct sa1100_port *sport)
*/
sa1100_mctrl_check(sport);
- if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
- sa1100_stop_tx(&sport->port);
- return;
- }
-
- /*
- * Tried using FIFO (not checking TNF) for fifo fill:
- * still had the '4 bytes repeated' problem.
- */
- while (UART_GET_UTSR1(sport) & UTSR1_TNF) {
- UART_PUT_CHAR(sport, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&sport->port);
-
- if (uart_circ_empty(xmit))
- sa1100_stop_tx(&sport->port);
+ uart_port_tx(&sport->port, ch,
+ UART_GET_UTSR1(sport) & UTSR1_TNF,
+ UART_PUT_CHAR(sport, ch));
}
static irqreturn_t sa1100_int(int irq, void *dev_id)
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index 9c252c9ca95a..0fce856434da 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -288,7 +288,6 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
struct s3c24xx_uart_dma *dma = ourport->dma;
- struct circ_buf *xmit = &port->state->xmit;
struct dma_tx_state state;
int count;
@@ -316,8 +315,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
DMA_TO_DEVICE);
async_tx_ack(dma->tx_desc);
count = dma->tx_bytes_requested - state.residue;
- xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
- port->icount.tx += count;
+ uart_xmit_advance(port, count);
}
ourport->tx_enabled = 0;
@@ -351,8 +349,7 @@ static void s3c24xx_serial_tx_dma_complete(void *args)
spin_lock_irqsave(&port->lock, flags);
- xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
- port->icount.tx += count;
+ uart_xmit_advance(port, count);
ourport->tx_in_progress = 0;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
@@ -916,8 +913,7 @@ static void s3c24xx_serial_tx_chars(struct s3c24xx_uart_port *ourport)
break;
wr_reg(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
count--;
}
diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c
index c5d2b6cdcb4a..de56f383964e 100644
--- a/drivers/tty/serial/sb1250-duart.c
+++ b/drivers/tty/serial/sb1250-duart.c
@@ -399,8 +399,7 @@ static void sbd_transmit_chars(struct sbd_port *sport)
/* Send char. */
if (!stop_tx) {
write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- sport->port.icount.tx++;
+ uart_xmit_advance(&sport->port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&sport->port);
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 524921360ca7..39f92eb1e698 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -686,13 +686,10 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
}
to_send = (to_send > txlen) ? txlen : to_send;
- /* Add data to send */
- port->icount.tx += to_send;
-
/* Convert to linear buffer */
for (i = 0; i < to_send; ++i) {
s->buf[i] = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uart_xmit_advance(port, 1);
}
sc16is7xx_fifo_write(port, to_send);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index dd98509f52e5..7df687822634 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -468,8 +468,7 @@ static void sccnxp_handle_tx(struct uart_port *port)
break;
sccnxp_port_write(port, SCCNXP_THR_REG, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index b7170cb9a544..e5b9773db5e3 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -496,8 +496,7 @@ static void tegra_uart_fill_tx_fifo(struct tegra_uart_port *tup, int max_bytes)
break;
}
tegra_uart_write(tup, xmit->buf[xmit->tail], UART_TX);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- tup->uport.icount.tx++;
+ uart_xmit_advance(&tup->uport, 1);
}
}
@@ -619,8 +618,9 @@ static void tegra_uart_stop_tx(struct uart_port *u)
if (tup->tx_in_progress != TEGRA_UART_TX_DMA)
return;
- dmaengine_terminate_all(tup->tx_dma_chan);
+ dmaengine_pause(tup->tx_dma_chan);
dmaengine_tx_status(tup->tx_dma_chan, tup->tx_cookie, &state);
+ dmaengine_terminate_all(tup->tx_dma_chan);
count = tup->tx_bytes_requested - state.residue;
async_tx_ack(tup->tx_dma_desc);
uart_xmit_advance(&tup->uport, count);
@@ -763,8 +763,9 @@ static void tegra_uart_terminate_rx_dma(struct tegra_uart_port *tup)
return;
}
- dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_pause(tup->rx_dma_chan);
dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ dmaengine_terminate_all(tup->rx_dma_chan);
tegra_uart_rx_buffer_push(tup, state.residue);
tup->rx_dma_active = false;
diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c
index e12f1dc18c38..eab387b01e36 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -321,34 +321,12 @@ receive_chars(struct uart_port *up, unsigned int *status)
static inline void transmit_chars(struct uart_port *up)
{
- struct circ_buf *xmit = &up->state->xmit;
- int count;
+ u8 ch;
- if (up->x_char) {
- sio_out(up, TXX9_SITFIFO, up->x_char);
- up->icount.tx++;
- up->x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(up)) {
- serial_txx9_stop_tx(up);
- return;
- }
-
- count = TXX9_SIO_TX_FIFO;
- do {
- sio_out(up, TXX9_SITFIFO, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(up);
-
- if (uart_circ_empty(xmit))
- serial_txx9_stop_tx(up);
+ uart_port_tx_limited(up, ch, TXX9_SIO_TX_FIFO,
+ true,
+ sio_out(up, TXX9_SITFIFO, ch),
+ ({}));
}
static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 76452fe2af86..7bd080720929 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1181,10 +1181,7 @@ static void sci_dma_tx_complete(void *arg)
spin_lock_irqsave(&port->lock, flags);
- xmit->tail += s->tx_dma_len;
- xmit->tail &= UART_XMIT_SIZE - 1;
-
- port->icount.tx += s->tx_dma_len;
+ uart_xmit_advance(port, s->tx_dma_len);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c
index 7fb6760b5c37..1f565a216e74 100644
--- a/drivers/tty/serial/sifive.c
+++ b/drivers/tty/serial/sifive.c
@@ -288,33 +288,12 @@ static void __ssp_transmit_char(struct sifive_serial_port *ssp, int ch)
*/
static void __ssp_transmit_chars(struct sifive_serial_port *ssp)
{
- struct circ_buf *xmit = &ssp->port.state->xmit;
- int count;
-
- if (ssp->port.x_char) {
- __ssp_transmit_char(ssp, ssp->port.x_char);
- ssp->port.icount.tx++;
- ssp->port.x_char = 0;
- return;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) {
- sifive_serial_stop_tx(&ssp->port);
- return;
- }
- count = SIFIVE_TX_FIFO_DEPTH;
- do {
- __ssp_transmit_char(ssp, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- ssp->port.icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&ssp->port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- sifive_serial_stop_tx(&ssp->port);
+ uart_port_tx_limited(&ssp->port, ch, SIFIVE_TX_FIFO_DEPTH,
+ true,
+ __ssp_transmit_char(ssp, ch),
+ ({}));
}
/**
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 342a87967631..492a3bdab5ba 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -206,7 +206,6 @@ static void sprd_stop_tx_dma(struct uart_port *port)
{
struct sprd_uart_port *sp =
container_of(port, struct sprd_uart_port, port);
- struct circ_buf *xmit = &port->state->xmit;
struct dma_tx_state state;
u32 trans_len;
@@ -215,8 +214,7 @@ static void sprd_stop_tx_dma(struct uart_port *port)
dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
if (state.residue) {
trans_len = state.residue - sp->tx_dma.phys_addr;
- xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
- port->icount.tx += trans_len;
+ uart_xmit_advance(port, trans_len);
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
}
@@ -253,8 +251,7 @@ static void sprd_complete_tx_dma(void *data)
dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
sp->tx_dma.trans_len, DMA_TO_DEVICE);
- xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
- port->icount.tx += sp->tx_dma.trans_len;
+ uart_xmit_advance(port, sp->tx_dma.trans_len);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
@@ -626,35 +623,12 @@ static inline void sprd_rx(struct uart_port *port)
static inline void sprd_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- int count;
-
- if (port->x_char) {
- serial_out(port, SPRD_TXD, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- sprd_stop_tx(port);
- return;
- }
-
- count = THLD_TX_EMPTY;
- do {
- serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (--count > 0);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- sprd_stop_tx(port);
+ uart_port_tx_limited(port, ch, THLD_TX_EMPTY,
+ true,
+ serial_out(port, SPRD_TXD, ch),
+ ({}));
}
/* this handles the interrupt from one port */
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index fcecea689a0d..5215e6910f68 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -237,50 +237,12 @@ static inline unsigned asc_hw_txroom(struct uart_port *port)
*/
static void asc_transmit_chars(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- int txroom;
- unsigned char c;
-
- txroom = asc_hw_txroom(port);
-
- if ((txroom != 0) && port->x_char) {
- c = port->x_char;
- port->x_char = 0;
- asc_out(port, ASC_TXBUF, c);
- port->icount.tx++;
- txroom = asc_hw_txroom(port);
- }
-
- if (uart_tx_stopped(port)) {
- /*
- * We should try and stop the hardware here, but I
- * don't think the ASC has any way to do that.
- */
- asc_disable_tx_interrupts(port);
- return;
- }
-
- if (uart_circ_empty(xmit)) {
- asc_disable_tx_interrupts(port);
- return;
- }
-
- if (txroom == 0)
- return;
-
- do {
- c = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- asc_out(port, ASC_TXBUF, c);
- port->icount.tx++;
- txroom--;
- } while ((txroom > 0) && (!uart_circ_empty(xmit)));
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
+ u8 ch;
- if (uart_circ_empty(xmit))
- asc_disable_tx_interrupts(port);
+ uart_port_tx_limited(port, ch, asc_hw_txroom(port),
+ true,
+ asc_out(port, ASC_TXBUF, ch),
+ ({}));
}
static void asc_receive_chars(struct uart_port *port)
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index dfdbcf092fac..a1490033aa16 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -596,8 +596,7 @@ static void stm32_usart_transmit_chars_pio(struct uart_port *port)
if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
break;
writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
/* rely on TXE irq (mask or unmask) for sending remaining data */
@@ -673,8 +672,8 @@ static void stm32_usart_transmit_chars_dma(struct uart_port *port)
stm32_usart_set_bits(port, ofs->cr3, USART_CR3_DMAT);
- xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
- port->icount.tx += count;
+ uart_xmit_advance(port, count);
+
return;
fallback_err:
@@ -1681,22 +1680,10 @@ static int stm32_usart_serial_probe(struct platform_device *pdev)
if (!stm32port->info)
return -EINVAL;
- ret = stm32_usart_init_port(stm32port, pdev);
- if (ret)
- return ret;
-
- if (stm32port->wakeup_src) {
- device_set_wakeup_capable(&pdev->dev, true);
- ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq);
- if (ret)
- goto err_deinit_port;
- }
-
stm32port->rx_ch = dma_request_chan(&pdev->dev, "rx");
- if (PTR_ERR(stm32port->rx_ch) == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
- goto err_wakeirq;
- }
+ if (PTR_ERR(stm32port->rx_ch) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
/* Fall back in interrupt mode for any non-deferral error */
if (IS_ERR(stm32port->rx_ch))
stm32port->rx_ch = NULL;
@@ -1710,6 +1697,17 @@ static int stm32_usart_serial_probe(struct platform_device *pdev)
if (IS_ERR(stm32port->tx_ch))
stm32port->tx_ch = NULL;
+ ret = stm32_usart_init_port(stm32port, pdev);
+ if (ret)
+ goto err_dma_tx;
+
+ if (stm32port->wakeup_src) {
+ device_set_wakeup_capable(&pdev->dev, true);
+ ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq);
+ if (ret)
+ goto err_deinit_port;
+ }
+
if (stm32port->rx_ch && stm32_usart_of_dma_rx_probe(stm32port, pdev)) {
/* Fall back in interrupt mode */
dma_release_channel(stm32port->rx_ch);
@@ -1746,19 +1744,11 @@ err_port:
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
- if (stm32port->tx_ch) {
+ if (stm32port->tx_ch)
stm32_usart_of_dma_tx_remove(stm32port, pdev);
- dma_release_channel(stm32port->tx_ch);
- }
-
if (stm32port->rx_ch)
stm32_usart_of_dma_rx_remove(stm32port, pdev);
-err_dma_rx:
- if (stm32port->rx_ch)
- dma_release_channel(stm32port->rx_ch);
-
-err_wakeirq:
if (stm32port->wakeup_src)
dev_pm_clear_wake_irq(&pdev->dev);
@@ -1768,6 +1758,14 @@ err_deinit_port:
stm32_usart_deinit_port(stm32port);
+err_dma_tx:
+ if (stm32port->tx_ch)
+ dma_release_channel(stm32port->tx_ch);
+
+err_dma_rx:
+ if (stm32port->rx_ch)
+ dma_release_channel(stm32port->rx_ch);
+
return ret;
}
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 1938ba5e98c0..16c746a63258 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -47,8 +47,7 @@ static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit
if (status != HV_EOK)
break;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
}
@@ -63,8 +62,7 @@ static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
status = sun4v_con_write(ra, len, &sent);
if (status != HV_EOK)
break;
- xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1);
- port->icount.tx += sent;
+ uart_xmit_advance(port, sent);
}
}
diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c
index 7afe61a0e72e..727942c43c45 100644
--- a/drivers/tty/serial/sunplus-uart.c
+++ b/drivers/tty/serial/sunplus-uart.c
@@ -216,9 +216,7 @@ static void transmit_chars(struct uart_port *port)
do {
sp_uart_put_char(port, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) % UART_XMIT_SIZE;
- port->icount.tx++;
-
+ uart_xmit_advance(port, 1);
if (uart_circ_empty(xmit))
break;
} while (sunplus_tx_buf_not_full(port));
diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c
index 99608b2a2b74..48b39fdb0397 100644
--- a/drivers/tty/serial/sunsab.c
+++ b/drivers/tty/serial/sunsab.c
@@ -266,8 +266,7 @@ static void transmit_chars(struct uart_sunsab_port *up,
for (i = 0; i < up->port.fifosize; i++) {
writeb(xmit->buf[xmit->tail],
&up->regs->w.xfifo[i]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
if (uart_circ_empty(xmit))
break;
}
@@ -453,8 +452,7 @@ static void sunsab_start_tx(struct uart_port *port)
for (i = 0; i < up->port.fifosize; i++) {
writeb(xmit->buf[xmit->tail],
&up->regs->w.xfifo[i]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
if (uart_circ_empty(xmit))
break;
}
@@ -1133,7 +1131,13 @@ static int __init sunsab_init(void)
}
}
- return platform_driver_register(&sab_driver);
+ err = platform_driver_register(&sab_driver);
+ if (err) {
+ kfree(sunsab_ports);
+ sunsab_ports = NULL;
+ }
+
+ return err;
}
static void __exit sunsab_exit(void)
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 9ea7e567540d..fed052a0b931 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -417,8 +417,7 @@ static void transmit_chars(struct uart_sunsu_port *up)
count = up->port.fifosize;
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 87425290687d..ccb809216e94 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -508,8 +508,7 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
ZSDELAY();
ZS_WSYNC(channel);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- up->port.icount.tx++;
+ uart_xmit_advance(&up->port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
@@ -709,8 +708,7 @@ static void sunzilog_start_tx(struct uart_port *port)
ZSDELAY();
ZS_WSYNC(channel);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c
index bb19ed012def..0859394a78cd 100644
--- a/drivers/tty/serial/timbuart.c
+++ b/drivers/tty/serial/timbuart.c
@@ -101,8 +101,7 @@ static void timbuart_tx_chars(struct uart_port *port)
!uart_circ_empty(xmit)) {
iowrite8(xmit->buf[xmit->tail],
port->membase + TIMBUART_TXFIFO);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
}
dev_dbg(port->dev,
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index eca41ac5477c..94584e54ebbe 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -203,8 +203,7 @@ static int ulite_transmit(struct uart_port *port, int stat)
return 0;
uart_out32(xmit->buf[xmit->tail], ULITE_TX, port);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
/* wake up */
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 82cf14dd3d43..b09b6496ee3e 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -372,8 +372,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
p = qe2cpu_addr(be32_to_cpu(bdp->buf), qe_port);
while (count < qe_port->tx_fifosize) {
*p++ = xmit->buf[xmit->tail];
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ uart_xmit_advance(port, 1);
count++;
if (xmit->head == xmit->tail)
break;
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 10fbdb09965f..cc9157df732f 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -168,7 +168,7 @@ static void handle_rx(struct uart_port *port)
c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
- /* Mask conditions we're ignorning. */
+ /* Mask conditions we're ignoring. */
c &= ~port->read_status_mask;
if (c & FER) {
@@ -196,33 +196,11 @@ static unsigned int vt8500_tx_empty(struct uart_port *port)
static void handle_tx(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
+ u8 ch;
- if (port->x_char) {
- writeb(port->x_char, port->membase + VT8500_TXFIFO);
- port->icount.tx++;
- port->x_char = 0;
- }
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- vt8500_stop_tx(port);
- return;
- }
-
- while (vt8500_tx_empty(port)) {
- if (uart_circ_empty(xmit))
- break;
-
- writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
-
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (uart_circ_empty(xmit))
- vt8500_stop_tx(port);
+ uart_port_tx(port, ch,
+ vt8500_tx_empty(port),
+ writeb(ch, port->membase + VT8500_TXFIFO));
}
static void vt8500_start_tx(struct uart_port *port)
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 0cbd1892c53b..8e521c69a959 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -326,9 +326,7 @@ static void cdns_uart_handle_tx(void *dev_id)
!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
writel(xmit->buf[xmit->tail], port->membase + CDNS_UART_FIFO);
-
- port->icount.tx++;
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ uart_xmit_advance(port, 1);
numbytes--;
}
diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c
index 688db7d8b748..730c648e32ff 100644
--- a/drivers/tty/serial/zs.c
+++ b/drivers/tty/serial/zs.c
@@ -623,8 +623,7 @@ static void zs_raw_transmit_chars(struct zs_port *zport)
/* Send char. */
write_zsdata(zport, xmit->buf[xmit->tail]);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- zport->port.icount.tx++;
+ uart_xmit_advance(&zport->port, 1);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&zport->port);
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 25e9befdda3a..72b76cdde534 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -1433,16 +1433,8 @@ static int hdlcdev_open(struct net_device *dev)
int rc;
unsigned long flags;
- if (!try_module_get(THIS_MODULE))
- return -EBUSY;
-
DBGINFO(("%s hdlcdev_open\n", dev->name));
- /* generic HDLC layer open processing */
- rc = hdlc_open(dev);
- if (rc)
- return rc;
-
/* arbitrate between network and tty opens */
spin_lock_irqsave(&info->netlock, flags);
if (info->port.count != 0 || info->netcount != 0) {
@@ -1461,6 +1453,16 @@ static int hdlcdev_open(struct net_device *dev)
return rc;
}
+ /* generic HDLC layer open processing */
+ rc = hdlc_open(dev);
+ if (rc) {
+ shutdown(info);
+ spin_lock_irqsave(&info->netlock, flags);
+ info->netcount = 0;
+ spin_unlock_irqrestore(&info->netlock, flags);
+ return rc;
+ }
+
/* assert RTS and DTR, apply hardware settings */
info->signals |= SerialSignal_RTS | SerialSignal_DTR;
program_hw(info);
@@ -1506,7 +1508,6 @@ static int hdlcdev_close(struct net_device *dev)
info->netcount=0;
spin_unlock_irqrestore(&info->netlock, flags);
- module_put(THIS_MODULE);
return 0;
}
diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h
index 1c08c9b67b16..f45cd683c02e 100644
--- a/drivers/tty/tty.h
+++ b/drivers/tty/tty.h
@@ -93,7 +93,7 @@ void tty_ldisc_release(struct tty_struct *tty);
int __must_check tty_ldisc_init(struct tty_struct *tty);
void tty_ldisc_deinit(struct tty_struct *tty);
-void tty_sysctl_init(void);
+extern int tty_ldisc_autoload;
/* tty_audit.c */
#ifdef CONFIG_AUDIT
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 5e287dedce01..2df86ed90574 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -21,7 +21,7 @@
#include "tty.h"
#define MIN_TTYB_SIZE 256
-#define TTYB_ALIGN_MASK 255
+#define TTYB_ALIGN_MASK 0xff
/*
* Byte threshold to limit memory consumption for flip buffers.
@@ -37,7 +37,7 @@
* logic this must match.
*/
-#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
+#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~TTYB_ALIGN_MASK)
/**
* tty_buffer_lock_exclusive - gain exclusive access to buffer
@@ -107,7 +107,7 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size)
p->commit = 0;
p->lookahead = 0;
p->read = 0;
- p->flags = 0;
+ p->flags = true;
}
/**
@@ -249,7 +249,7 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
* __tty_buffer_request_room - grow tty buffer if needed
* @port: tty port
* @size: size desired
- * @flags: buffer flags if new buffer allocated (default = 0)
+ * @flags: buffer has to store flags along character data
*
* Make at least @size bytes of linear space available for the tty buffer.
*
@@ -260,19 +260,19 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
* Returns: the size we managed to find.
*/
static int __tty_buffer_request_room(struct tty_port *port, size_t size,
- int flags)
+ bool flags)
{
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *b, *n;
int left, change;
b = buf->tail;
- if (b->flags & TTYB_NORMAL)
+ if (!b->flags)
left = 2 * b->size - b->used;
else
left = b->size - b->used;
- change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL);
+ change = !b->flags && flags;
if (change || left < size) {
/* This is the slow path - looking for new buffers to use */
n = tty_buffer_alloc(port, size);
@@ -300,7 +300,7 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size,
int tty_buffer_request_room(struct tty_port *port, size_t size)
{
- return __tty_buffer_request_room(port, size, 0);
+ return __tty_buffer_request_room(port, size, true);
}
EXPORT_SYMBOL_GPL(tty_buffer_request_room);
@@ -320,17 +320,17 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port,
const unsigned char *chars, char flag, size_t size)
{
int copied = 0;
+ bool flags = flag != TTY_NORMAL;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
- int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
int space = __tty_buffer_request_room(port, goal, flags);
struct tty_buffer *tb = port->buf.tail;
if (unlikely(space == 0))
break;
memcpy(char_buf_ptr(tb, tb->used), chars, space);
- if (~tb->flags & TTYB_NORMAL)
+ if (tb->flags)
memset(flag_buf_ptr(tb, tb->used), flag, space);
tb->used += space;
copied += space;
@@ -393,13 +393,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags);
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
{
struct tty_buffer *tb;
- int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
+ bool flags = flag != TTY_NORMAL;
if (!__tty_buffer_request_room(port, 1, flags))
return 0;
tb = port->buf.tail;
- if (~tb->flags & TTYB_NORMAL)
+ if (tb->flags)
*flag_buf_ptr(tb, tb->used) = flag;
*char_buf_ptr(tb, tb->used++) = ch;
@@ -424,13 +424,13 @@ EXPORT_SYMBOL(__tty_insert_flip_char);
int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
size_t size)
{
- int space = __tty_buffer_request_room(port, size, TTYB_NORMAL);
+ int space = __tty_buffer_request_room(port, size, false);
if (likely(space)) {
struct tty_buffer *tb = port->buf.tail;
*chars = char_buf_ptr(tb, tb->used);
- if (~tb->flags & TTYB_NORMAL)
+ if (tb->flags)
memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space);
tb->used += space;
}
@@ -492,7 +492,7 @@ static void lookahead_bufs(struct tty_port *port, struct tty_buffer *head)
unsigned char *p, *f = NULL;
p = char_buf_ptr(head, head->lookahead);
- if (~head->flags & TTYB_NORMAL)
+ if (head->flags)
f = flag_buf_ptr(head, head->lookahead);
port->client_ops->lookahead_buf(port, p, f, count);
@@ -509,7 +509,7 @@ receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
const char *f = NULL;
int n;
- if (~head->flags & TTYB_NORMAL)
+ if (head->flags)
f = flag_buf_ptr(head, head->read);
n = port->client_ops->receive_buf(port, p, f, count);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index cafdff575716..034fa9865a0f 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -2255,6 +2255,7 @@ static int tty_fasync(int fd, struct file *filp, int on)
return retval;
}
+static bool tty_legacy_tiocsti __read_mostly = IS_ENABLED(CONFIG_LEGACY_TIOCSTI);
/**
* tiocsti - fake input character
* @tty: tty to fake input into
@@ -2273,6 +2274,9 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
char ch, mbz = 0;
struct tty_ldisc *ld;
+ if (!tty_legacy_tiocsti)
+ return -EIO;
+
if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(ch, p))
@@ -3588,13 +3592,51 @@ void console_sysfs_notify(void)
sysfs_notify(&consdev->kobj, NULL, "active");
}
+static struct ctl_table tty_table[] = {
+ {
+ .procname = "legacy_tiocsti",
+ .data = &tty_legacy_tiocsti,
+ .maxlen = sizeof(tty_legacy_tiocsti),
+ .mode = 0644,
+ .proc_handler = proc_dobool,
+ },
+ {
+ .procname = "ldisc_autoload",
+ .data = &tty_ldisc_autoload,
+ .maxlen = sizeof(tty_ldisc_autoload),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
+ { }
+};
+
+static struct ctl_table tty_dir_table[] = {
+ {
+ .procname = "tty",
+ .mode = 0555,
+ .child = tty_table,
+ },
+ { }
+};
+
+static struct ctl_table tty_root_table[] = {
+ {
+ .procname = "dev",
+ .mode = 0555,
+ .child = tty_dir_table,
+ },
+ { }
+};
+
/*
* Ok, now we can initialize the rest of the tty devices and can count
* on memory allocations, interrupts etc..
*/
int __init tty_init(void)
{
- tty_sysctl_init();
+ register_sysctl_table(tty_root_table);
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
@@ -3616,4 +3658,3 @@ int __init tty_init(void)
#endif
return 0;
}
-
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 776d8a62f77c..e758f44729e7 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -117,7 +117,7 @@ static void put_ldops(struct tty_ldisc_ops *ldops)
raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags);
}
-static int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD);
+int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD);
/**
* tty_ldisc_get - take a reference to an ldisc
@@ -817,39 +817,3 @@ void tty_ldisc_deinit(struct tty_struct *tty)
tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;
}
-
-static struct ctl_table tty_table[] = {
- {
- .procname = "ldisc_autoload",
- .data = &tty_ldisc_autoload,
- .maxlen = sizeof(tty_ldisc_autoload),
- .mode = 0644,
- .proc_handler = proc_dointvec,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
- },
- { }
-};
-
-static struct ctl_table tty_dir_table[] = {
- {
- .procname = "tty",
- .mode = 0555,
- .child = tty_table,
- },
- { }
-};
-
-static struct ctl_table tty_root_table[] = {
- {
- .procname = "dev",
- .mode = 0555,
- .child = tty_dir_table,
- },
- { }
-};
-
-void tty_sysctl_init(void)
-{
- register_sysctl_table(tty_root_table);
-}