summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-24 21:17:14 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-24 21:17:14 +0100
commit17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9 (patch)
treebf06872140bc74115625e34f590b9ccc6270cbd6 /drivers/tty
parentMerge tag 'usb-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/greg... (diff)
parenttty: n_gsm: add keep alive support (diff)
downloadlinux-17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9.tar.xz
linux-17cd4d6f05087ea1ae5c1416ef260e5b7fc5d5c9.zip
Merge tag 'tty-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty / serial driver updates from Greg KH: "Here is the big set of serial and tty driver updates for 6.3-rc1. Once again, Jiri and Ilpo have done a number of core vt and tty/serial layer cleanups that were much needed and appreciated. Other than that, it's just a bunch of little tty/serial driver updates: - qcom-geni-serial driver updates - liteuart driver updates - hvcs driver cleanups - n_gsm updates and additions for new features - more 8250 device support added - fpga/dfl update and additions - imx serial driver updates - fsl_lpuart updates - other tiny fixes and updates for serial drivers All of these have been in linux-next for a while with no reported problems" * tag 'tty-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (143 commits) tty: n_gsm: add keep alive support serial: imx: remove a redundant check dt-bindings: serial: snps-dw-apb-uart: add dma & dma-names properties soc: qcom: geni-se: Move qcom-geni-se.h to linux/soc/qcom/geni-se.h tty: n_gsm: add TIOCMIWAIT support tty: n_gsm: add RING/CD control support tty: n_gsm: mark unusable ioctl structure fields accordingly serial: imx: get rid of registers shadowing serial: imx: refine local variables in rxint() serial: imx: stop using USR2 in FIFO reading loop serial: imx: remove redundant USR2 read from FIFO reading loop serial: imx: do not break from FIFO reading loop prematurely serial: imx: do not sysrq broken chars serial: imx: work-around for hardware RX flood serial: imx: factor-out common code to imx_uart_soft_reset() serial: 8250_pci1xxxx: Add power management functions to quad-uart driver serial: 8250_pci1xxxx: Add RS485 support to quad-uart driver serial: 8250_pci1xxxx: Add driver for quad-uart support serial: 8250_pci: Add serial8250_pci_setup_port definition in 8250_pcilib.c tty: pcn_uart: fix memory leak with using debugfs_lookup() ...
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/amiserial.c12
-rw-r--r--drivers/tty/hvc/hvc_console.c4
-rw-r--r--drivers/tty/hvc/hvc_console.h2
-rw-r--r--drivers/tty/hvc/hvc_iucv.c6
-rw-r--r--drivers/tty/hvc/hvcs.c91
-rw-r--r--drivers/tty/moxa.c82
-rw-r--r--drivers/tty/mxser.c11
-rw-r--r--drivers/tty/n_gsm.c160
-rw-r--r--drivers/tty/serial/8250/8250_dfl.c167
-rw-r--r--drivers/tty/serial/8250/8250_early.c4
-rw-r--r--drivers/tty/serial/8250/8250_pci.c25
-rw-r--r--drivers/tty/serial/8250/8250_pci1xxxx.c494
-rw-r--r--drivers/tty/serial/8250/8250_pcilib.c40
-rw-r--r--drivers/tty/serial/8250/8250_pcilib.h15
-rw-r--r--drivers/tty/serial/8250/8250_port.c57
-rw-r--r--drivers/tty/serial/8250/Kconfig27
-rw-r--r--drivers/tty/serial/8250/Makefile3
-rw-r--r--drivers/tty/serial/Kconfig16
-rw-r--r--drivers/tty/serial/Makefile2
-rw-r--r--drivers/tty/serial/earlycon-semihost.c (renamed from drivers/tty/serial/earlycon-arm-semihost.c)25
-rw-r--r--drivers/tty/serial/earlycon.c9
-rw-r--r--drivers/tty/serial/fsl_lpuart.c119
-rw-r--r--drivers/tty/serial/imx.c308
-rw-r--r--drivers/tty/serial/liteuart.c241
-rw-r--r--drivers/tty/serial/max3100.c2
-rw-r--r--drivers/tty/serial/max310x.c3
-rw-r--r--drivers/tty/serial/msm_serial.c1
-rw-r--r--drivers/tty/serial/pch_uart.c2
-rw-r--r--drivers/tty/serial/pic32_uart.c2
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c638
-rw-r--r--drivers/tty/serial/sc16is7xx.c51
-rw-r--r--drivers/tty/serial/sccnxp.c12
-rw-r--r--drivers/tty/serial/serial-tegra.c7
-rw-r--r--drivers/tty/serial/serial_core.c77
-rw-r--r--drivers/tty/serial/stm32-usart.c6
-rw-r--r--drivers/tty/serial/sunhv.c8
-rw-r--r--drivers/tty/serial/ucc_uart.c2
-rw-r--r--drivers/tty/synclink_gt.c21
-rw-r--r--drivers/tty/tty_io.c8
-rw-r--r--drivers/tty/tty_ioctl.c8
-rw-r--r--drivers/tty/tty_port.c22
-rw-r--r--drivers/tty/vt/vt.c310
42 files changed, 2116 insertions, 984 deletions
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index f52266766df9..d7515d61659e 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -502,7 +502,7 @@ static int startup(struct tty_struct *tty, struct serial_state *info)
*/
change_speed(tty, info, NULL);
- tty_port_set_initialized(port, 1);
+ tty_port_set_initialized(port, true);
local_irq_restore(flags);
return 0;
@@ -556,7 +556,7 @@ static void shutdown(struct tty_struct *tty, struct serial_state *info)
set_bit(TTY_IO_ERROR, &tty->flags);
- tty_port_set_initialized(&info->tport, 0);
+ tty_port_set_initialized(&info->tport, false);
local_irq_restore(flags);
}
@@ -1329,7 +1329,7 @@ static void rs_hangup(struct tty_struct *tty)
rs_flush_buffer(tty);
shutdown(tty, info);
info->tport.count = 0;
- tty_port_set_active(&info->tport, 0);
+ tty_port_set_active(&info->tport, false);
info->tport.tty = NULL;
wake_up_interruptible(&info->tport.open_wait);
}
@@ -1454,18 +1454,18 @@ static const struct tty_operations serial_ops = {
.proc_show = rs_proc_show,
};
-static int amiga_carrier_raised(struct tty_port *port)
+static bool amiga_carrier_raised(struct tty_port *port)
{
return !(ciab.pra & SER_DCD);
}
-static void amiga_dtr_rts(struct tty_port *port, int raise)
+static void amiga_dtr_rts(struct tty_port *port, bool active)
{
struct serial_state *info = container_of(port, struct serial_state,
tport);
unsigned long flags;
- if (raise)
+ if (active)
info->MCR |= SER_DTR|SER_RTS;
else
info->MCR &= ~(SER_DTR|SER_RTS);
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index a683e21df19c..10c10cfdf92a 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -376,7 +376,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp)
/* We are ready... raise DTR/RTS */
if (C_BAUD(tty))
if (hp->ops->dtr_rts)
- hp->ops->dtr_rts(hp, 1);
+ hp->ops->dtr_rts(hp, true);
tty_port_set_initialized(&hp->port, true);
}
@@ -406,7 +406,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp)
if (C_HUPCL(tty))
if (hp->ops->dtr_rts)
- hp->ops->dtr_rts(hp, 0);
+ hp->ops->dtr_rts(hp, false);
if (hp->ops->notifier_del)
hp->ops->notifier_del(hp, hp->data);
diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h
index 18d005814e4b..9668f821db01 100644
--- a/drivers/tty/hvc/hvc_console.h
+++ b/drivers/tty/hvc/hvc_console.h
@@ -66,7 +66,7 @@ struct hv_ops {
int (*tiocmset)(struct hvc_struct *hp, unsigned int set, unsigned int clear);
/* Callbacks to handle tty ports */
- void (*dtr_rts)(struct hvc_struct *hp, int raise);
+ void (*dtr_rts)(struct hvc_struct *hp, bool active);
};
/* Register a vterm and a slot index for use as a console (console_init) */
diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c
index 7d49a872de48..543f35ddf523 100644
--- a/drivers/tty/hvc/hvc_iucv.c
+++ b/drivers/tty/hvc/hvc_iucv.c
@@ -658,13 +658,13 @@ static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id)
/**
* hvc_iucv_dtr_rts() - HVC notifier for handling DTR/RTS
* @hp: Pointer the HVC device (struct hvc_struct)
- * @raise: Non-zero to raise or zero to lower DTR/RTS lines
+ * @active: True to raise or false to lower DTR/RTS lines
*
* This routine notifies the HVC back-end to raise or lower DTR/RTS
* lines. Raising DTR/RTS is ignored. Lowering DTR/RTS indicates to
* drop the IUCV connection (similar to hang up the modem).
*/
-static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
+static void hvc_iucv_dtr_rts(struct hvc_struct *hp, bool active)
{
struct hvc_iucv_private *priv;
struct iucv_path *path;
@@ -672,7 +672,7 @@ static void hvc_iucv_dtr_rts(struct hvc_struct *hp, int raise)
/* Raising the DTR/RTS is ignored as IUCV connections can be
* established at any times.
*/
- if (raise)
+ if (active)
return;
priv = hvc_iucv_get_private(hp->vtermno);
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index 4ba24963685e..1de1a09bf82d 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -52,6 +52,7 @@
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kref.h>
@@ -285,6 +286,7 @@ struct hvcs_struct {
char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */
struct list_head next; /* list management */
struct vio_dev *vdev;
+ struct completion *destroyed;
};
static LIST_HEAD(hvcs_structs);
@@ -432,7 +434,7 @@ static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr
static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL);
-static struct attribute *hvcs_attrs[] = {
+static struct attribute *hvcs_dev_attrs[] = {
&dev_attr_partner_vtys.attr,
&dev_attr_partner_clcs.attr,
&dev_attr_current_vty.attr,
@@ -441,9 +443,7 @@ static struct attribute *hvcs_attrs[] = {
NULL,
};
-static struct attribute_group hvcs_attr_group = {
- .attrs = hvcs_attrs,
-};
+ATTRIBUTE_GROUPS(hvcs_dev);
static ssize_t rescan_show(struct device_driver *ddp, char *buf)
{
@@ -468,6 +468,13 @@ static ssize_t rescan_store(struct device_driver *ddp, const char * buf,
static DRIVER_ATTR_RW(rescan);
+static struct attribute *hvcs_attrs[] = {
+ &driver_attr_rescan.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(hvcs);
+
static void hvcs_kick(void)
{
hvcs_kicked = 1;
@@ -658,11 +665,13 @@ static void hvcs_destruct_port(struct tty_port *p)
{
struct hvcs_struct *hvcsd = container_of(p, struct hvcs_struct, port);
struct vio_dev *vdev;
+ struct completion *comp;
unsigned long flags;
spin_lock(&hvcs_structs_lock);
spin_lock_irqsave(&hvcsd->lock, flags);
+ comp = hvcsd->destroyed;
/* the list_del poisons the pointers */
list_del(&(hvcsd->next));
@@ -682,15 +691,16 @@ static void hvcs_destruct_port(struct tty_port *p)
hvcsd->p_unit_address = 0;
hvcsd->p_partition_ID = 0;
+ hvcsd->destroyed = NULL;
hvcs_return_index(hvcsd->index);
memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1);
spin_unlock_irqrestore(&hvcsd->lock, flags);
spin_unlock(&hvcs_structs_lock);
- sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group);
-
kfree(hvcsd);
+ if (comp)
+ complete(comp);
}
static const struct tty_port_operations hvcs_port_ops = {
@@ -721,7 +731,6 @@ static int hvcs_probe(
{
struct hvcs_struct *hvcsd;
int index, rc;
- int retval;
if (!dev || !id) {
printk(KERN_ERR "HVCS: probed with invalid parameter.\n");
@@ -778,13 +787,6 @@ static int hvcs_probe(
list_add_tail(&(hvcsd->next), &hvcs_structs);
spin_unlock(&hvcs_structs_lock);
- retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group);
- if (retval) {
- printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n",
- hvcsd->vdev->unit_address);
- return retval;
- }
-
printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address);
/*
@@ -797,6 +799,7 @@ static int hvcs_probe(
static void hvcs_remove(struct vio_dev *dev)
{
struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev);
+ DECLARE_COMPLETION_ONSTACK(comp);
unsigned long flags;
struct tty_struct *tty;
@@ -804,24 +807,22 @@ static void hvcs_remove(struct vio_dev *dev)
spin_lock_irqsave(&hvcsd->lock, flags);
- tty = hvcsd->port.tty;
+ hvcsd->destroyed = &comp;
+ tty = tty_port_tty_get(&hvcsd->port);
spin_unlock_irqrestore(&hvcsd->lock, flags);
/*
- * Let the last holder of this object cause it to be removed, which
- * would probably be tty_hangup below.
- */
- tty_port_put(&hvcsd->port);
-
- /*
- * The hangup is a scheduled function which will auto chain call
- * hvcs_hangup. The tty should always be valid at this time unless a
+ * The tty should always be valid at this time unless a
* simultaneous tty close already cleaned up the hvcs_struct.
*/
- if (tty)
- tty_hangup(tty);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+ tty_port_put(&hvcsd->port);
+ wait_for_completion(&comp);
printk(KERN_INFO "HVCS: vty-server@%X removed from the"
" vio bus.\n", dev->unit_address);
};
@@ -831,6 +832,10 @@ static struct vio_driver hvcs_vio_driver = {
.probe = hvcs_probe,
.remove = hvcs_remove,
.name = hvcs_driver_name,
+ .driver = {
+ .groups = hvcs_groups,
+ .dev_groups = hvcs_dev_groups,
+ },
};
/* Only called from hvcs_get_pi please */
@@ -1171,7 +1176,10 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
hvcsd = tty->driver_data;
spin_lock_irqsave(&hvcsd->lock, flags);
- if (--hvcsd->port.count == 0) {
+ if (hvcsd->port.count == 0) {
+ spin_unlock_irqrestore(&hvcsd->lock, flags);
+ return;
+ } else if (--hvcsd->port.count == 0) {
vio_disable_interrupts(hvcsd->vdev);
@@ -1215,12 +1223,9 @@ static void hvcs_hangup(struct tty_struct * tty)
{
struct hvcs_struct *hvcsd = tty->driver_data;
unsigned long flags;
- int temp_open_count;
int irq;
spin_lock_irqsave(&hvcsd->lock, flags);
- /* Preserve this so that we know how many kref refs to put */
- temp_open_count = hvcsd->port.count;
/*
* Don't kref put inside the spinlock because the destruction
@@ -1230,11 +1235,7 @@ static void hvcs_hangup(struct tty_struct * tty)
vio_disable_interrupts(hvcsd->vdev);
hvcsd->todo_mask = 0;
-
- /* I don't think the tty needs the hvcs_struct pointer after a hangup */
- tty->driver_data = NULL;
hvcsd->port.tty = NULL;
-
hvcsd->port.count = 0;
/* This will drop any buffered data on the floor which is OK in a hangup
@@ -1247,21 +1248,6 @@ static void hvcs_hangup(struct tty_struct * tty)
spin_unlock_irqrestore(&hvcsd->lock, flags);
free_irq(irq, hvcsd);
-
- /*
- * We need to kref_put() for every open_count we have since the
- * tty_hangup() function doesn't invoke a close per open connection on a
- * non-console device.
- */
- while(temp_open_count) {
- --temp_open_count;
- /*
- * The final put will trigger destruction of the hvcs_struct.
- * NOTE: If this hangup was signaled from user space then the
- * final put will never happen.
- */
- tty_port_put(&hvcsd->port);
- }
}
/*
@@ -1525,13 +1511,6 @@ static int __init hvcs_module_init(void)
pr_info("HVCS: Driver registered.\n");
- /* This needs to be done AFTER the vio_register_driver() call or else
- * the kobjects won't be initialized properly.
- */
- rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan);
- if (rc)
- pr_warn("HVCS: Failed to create rescan file (err %d)\n", rc);
-
return 0;
}
@@ -1556,8 +1535,6 @@ static void __exit hvcs_module_exit(void)
hvcs_pi_buff = NULL;
spin_unlock(&hvcs_pi_lock);
- driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan);
-
tty_unregister_driver(hvcs_tty_driver);
hvcs_free_index_list();
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 35b6fddf0341..42fa4c878b2e 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -501,16 +501,16 @@ static int moxa_tiocmset(struct tty_struct *tty,
static void moxa_poll(struct timer_list *);
static void moxa_set_tty_param(struct tty_struct *, const struct ktermios *);
static void moxa_shutdown(struct tty_port *);
-static int moxa_carrier_raised(struct tty_port *);
-static void moxa_dtr_rts(struct tty_port *, int);
+static bool moxa_carrier_raised(struct tty_port *);
+static void moxa_dtr_rts(struct tty_port *, bool);
/*
* moxa board interface functions:
*/
static void MoxaPortEnable(struct moxa_port *);
static void MoxaPortDisable(struct moxa_port *);
static int MoxaPortSetTermio(struct moxa_port *, struct ktermios *, speed_t);
-static int MoxaPortGetLineOut(struct moxa_port *, int *, int *);
-static void MoxaPortLineCtrl(struct moxa_port *, int, int);
+static int MoxaPortGetLineOut(struct moxa_port *, bool *, bool *);
+static void MoxaPortLineCtrl(struct moxa_port *, bool, bool);
static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int);
static int MoxaPortLineStatus(struct moxa_port *);
static void MoxaPortFlushData(struct moxa_port *, int);
@@ -1432,7 +1432,7 @@ static void moxa_shutdown(struct tty_port *port)
MoxaPortFlushData(ch, 2);
}
-static int moxa_carrier_raised(struct tty_port *port)
+static bool moxa_carrier_raised(struct tty_port *port)
{
struct moxa_port *ch = container_of(port, struct moxa_port, port);
int dcd;
@@ -1443,10 +1443,10 @@ static int moxa_carrier_raised(struct tty_port *port)
return dcd;
}
-static void moxa_dtr_rts(struct tty_port *port, int onoff)
+static void moxa_dtr_rts(struct tty_port *port, bool active)
{
struct moxa_port *ch = container_of(port, struct moxa_port, port);
- MoxaPortLineCtrl(ch, onoff, onoff);
+ MoxaPortLineCtrl(ch, active, active);
}
@@ -1481,10 +1481,10 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
if (!tty_port_initialized(&ch->port)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, &tty->termios);
- MoxaPortLineCtrl(ch, 1, 1);
+ MoxaPortLineCtrl(ch, true, true);
MoxaPortEnable(ch);
MoxaSetFifo(ch, ch->type == PORT_16550A);
- tty_port_set_initialized(&ch->port, 1);
+ tty_port_set_initialized(&ch->port, true);
}
mutex_unlock(&ch->port.mutex);
mutex_unlock(&moxa_openlock);
@@ -1557,19 +1557,21 @@ static unsigned int moxa_chars_in_buffer(struct tty_struct *tty)
static int moxa_tiocmget(struct tty_struct *tty)
{
struct moxa_port *ch = tty->driver_data;
- int flag = 0, dtr, rts;
+ bool dtr_active, rts_active;
+ int flag = 0;
+ int status;
- MoxaPortGetLineOut(ch, &dtr, &rts);
- if (dtr)
+ MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
+ if (dtr_active)
flag |= TIOCM_DTR;
- if (rts)
+ if (rts_active)
flag |= TIOCM_RTS;
- dtr = MoxaPortLineStatus(ch);
- if (dtr & 1)
+ status = MoxaPortLineStatus(ch);
+ if (status & 1)
flag |= TIOCM_CTS;
- if (dtr & 2)
+ if (status & 2)
flag |= TIOCM_DSR;
- if (dtr & 4)
+ if (status & 4)
flag |= TIOCM_CD;
return flag;
}
@@ -1577,8 +1579,8 @@ static int moxa_tiocmget(struct tty_struct *tty)
static int moxa_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
+ bool dtr_active, rts_active;
struct moxa_port *ch;
- int dtr, rts;
mutex_lock(&moxa_openlock);
ch = tty->driver_data;
@@ -1587,16 +1589,16 @@ static int moxa_tiocmset(struct tty_struct *tty,
return -EINVAL;
}
- MoxaPortGetLineOut(ch, &dtr, &rts);
+ MoxaPortGetLineOut(ch, &dtr_active, &rts_active);
if (set & TIOCM_RTS)
- rts = 1;
+ rts_active = true;
if (set & TIOCM_DTR)
- dtr = 1;
+ dtr_active = true;
if (clear & TIOCM_RTS)
- rts = 0;
+ rts_active = false;
if (clear & TIOCM_DTR)
- dtr = 0;
- MoxaPortLineCtrl(ch, dtr, rts);
+ dtr_active = false;
+ MoxaPortLineCtrl(ch, dtr_active, rts_active);
mutex_unlock(&moxa_openlock);
return 0;
}
@@ -1664,8 +1666,8 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
u16 __iomem *ip)
{
struct tty_struct *tty = tty_port_tty_get(&p->port);
+ bool inited = tty_port_initialized(&p->port);
void __iomem *ofsAddr;
- unsigned int inited = tty_port_initialized(&p->port);
u16 intr;
if (tty) {
@@ -1877,12 +1879,12 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode)
*
* Function 13: Get the DTR/RTS state of this port.
* Syntax:
- * int MoxaPortGetLineOut(int port, int *dtrState, int *rtsState);
+ * int MoxaPortGetLineOut(int port, bool *dtrState, bool *rtsState);
* int port : port number (0 - 127)
- * int * dtrState : pointer to INT to receive the current DTR
+ * bool * dtr_active : pointer to bool to receive the current DTR
* state. (if NULL, this function will not
* write to this address)
- * int * rtsState : pointer to INT to receive the current RTS
+ * bool * rts_active : pointer to bool to receive the current RTS
* state. (if NULL, this function will not
* write to this address)
*
@@ -1892,10 +1894,10 @@ static void MoxaPortFlushData(struct moxa_port *port, int mode)
*
* Function 14: Setting the DTR/RTS output state of this port.
* Syntax:
- * void MoxaPortLineCtrl(int port, int dtrState, int rtsState);
+ * void MoxaPortLineCtrl(int port, bool dtrState, bool rtsState);
* int port : port number (0 - 127)
- * int dtrState : DTR output state (0: off, 1: on)
- * int rtsState : RTS output state (0: off, 1: on)
+ * bool dtr_active : DTR output state
+ * bool rts_active : RTS output state
*
*
* Function 15: Setting the flow control of this port.
@@ -2103,24 +2105,24 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
return baud;
}
-static int MoxaPortGetLineOut(struct moxa_port *port, int *dtrState,
- int *rtsState)
+static int MoxaPortGetLineOut(struct moxa_port *port, bool *dtr_active,
+ bool *rts_active)
{
- if (dtrState)
- *dtrState = !!(port->lineCtrl & DTR_ON);
- if (rtsState)
- *rtsState = !!(port->lineCtrl & RTS_ON);
+ if (dtr_active)
+ *dtr_active = port->lineCtrl & DTR_ON;
+ if (rts_active)
+ *rts_active = port->lineCtrl & RTS_ON;
return 0;
}
-static void MoxaPortLineCtrl(struct moxa_port *port, int dtr, int rts)
+static void MoxaPortLineCtrl(struct moxa_port *port, bool dtr_active, bool rts_active)
{
u8 mode = 0;
- if (dtr)
+ if (dtr_active)
mode |= DTR_ON;
- if (rts)
+ if (rts_active)
mode |= RTS_ON;
port->lineCtrl = mode;
moxafunc(port->tableAddr, FC_LineControl, mode);
diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c
index 2436e0b10f9a..ef3116e87975 100644
--- a/drivers/tty/mxser.c
+++ b/drivers/tty/mxser.c
@@ -458,13 +458,14 @@ static void __mxser_stop_tx(struct mxser_port *info)
outb(info->IER, info->ioaddr + UART_IER);
}
-static int mxser_carrier_raised(struct tty_port *port)
+static bool mxser_carrier_raised(struct tty_port *port)
{
struct mxser_port *mp = container_of(port, struct mxser_port, port);
- return (inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD)?1:0;
+
+ return inb(mp->ioaddr + UART_MSR) & UART_MSR_DCD;
}
-static void mxser_dtr_rts(struct tty_port *port, int on)
+static void mxser_dtr_rts(struct tty_port *port, bool active)
{
struct mxser_port *mp = container_of(port, struct mxser_port, port);
unsigned long flags;
@@ -472,7 +473,7 @@ static void mxser_dtr_rts(struct tty_port *port, int on)
spin_lock_irqsave(&mp->slock, flags);
mcr = inb(mp->ioaddr + UART_MCR);
- if (on)
+ if (active)
mcr |= UART_MCR_DTR | UART_MCR_RTS;
else
mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
@@ -1063,7 +1064,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
} else {
retval = mxser_activate(port, tty);
if (retval == 0)
- tty_port_set_initialized(port, 1);
+ tty_port_set_initialized(port, true);
}
mutex_unlock(&port->mutex);
return retval;
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 348d85c13f91..7aa05ebed8e1 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -318,6 +318,11 @@ struct gsm_mux {
struct gsm_control *pending_cmd;/* Our current pending command */
spinlock_t control_lock; /* Protects the pending command */
+ /* Keep-alive */
+ struct timer_list ka_timer; /* Keep-alive response timer */
+ u8 ka_num; /* Keep-alive match pattern */
+ signed int ka_retries; /* Keep-alive retry counter, -1 if not yet initialized */
+
/* Configuration */
int adaption; /* 1 or 2 supported */
u8 ftype; /* UI or UIH */
@@ -325,6 +330,7 @@ struct gsm_mux {
unsigned int t3; /* Power wake-up timer in seconds. */
int n2; /* Retry count */
u8 k; /* Window size */
+ u32 keep_alive; /* Control channel keep-alive in 10ms */
/* Statistics (not currently exposed) */
unsigned long bad_fcs;
@@ -540,6 +546,11 @@ static u8 gsm_encode_modem(const struct gsm_dlci *dlci)
modembits |= MDM_IC;
if (dlci->modem_tx & TIOCM_CD || dlci->gsm->initiator)
modembits |= MDM_DV;
+ /* special mappings for passive side to operate as UE */
+ if (dlci->modem_tx & TIOCM_OUT1)
+ modembits |= MDM_IC;
+ if (dlci->modem_tx & TIOCM_OUT2)
+ modembits |= MDM_DV;
return modembits;
}
@@ -1531,6 +1542,7 @@ static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci,
if (brk & 0x01)
tty_insert_flip_char(&dlci->port, 0, TTY_BREAK);
dlci->modem_rx = mlines;
+ wake_up_interruptible(&dlci->gsm->event);
}
/**
@@ -1897,11 +1909,13 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
const u8 *data, int clen)
{
struct gsm_control *ctrl;
+ struct gsm_dlci *dlci;
unsigned long flags;
spin_lock_irqsave(&gsm->control_lock, flags);
ctrl = gsm->pending_cmd;
+ dlci = gsm->dlci[0];
command |= 1;
/* Does the reply match our command */
if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) {
@@ -1916,6 +1930,53 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
/* Or did we receive the PN response to our PN command */
} else if (command == CMD_PN) {
gsm_control_negotiation(gsm, 0, data, clen);
+ /* Or did we receive the TEST response to our TEST command */
+ } else if (command == CMD_TEST && clen == 1 && *data == gsm->ka_num) {
+ gsm->ka_retries = -1; /* trigger new keep-alive message */
+ if (dlci && !dlci->dead)
+ mod_timer(&gsm->ka_timer, jiffies + gsm->keep_alive * HZ / 100);
+ }
+ spin_unlock_irqrestore(&gsm->control_lock, flags);
+}
+
+/**
+ * gsm_control_keep_alive - check timeout or start keep-alive
+ * @t: timer contained in our gsm object
+ *
+ * Called off the keep-alive timer expiry signaling that our link
+ * partner is not responding anymore. Link will be closed.
+ * This is also called to startup our timer.
+ */
+
+static void gsm_control_keep_alive(struct timer_list *t)
+{
+ struct gsm_mux *gsm = from_timer(gsm, t, ka_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gsm->control_lock, flags);
+ if (gsm->ka_num && gsm->ka_retries == 0) {
+ /* Keep-alive expired -> close the link */
+ if (debug & DBG_ERRORS)
+ pr_debug("%s keep-alive timed out\n", __func__);
+ spin_unlock_irqrestore(&gsm->control_lock, flags);
+ if (gsm->dlci[0])
+ gsm_dlci_begin_close(gsm->dlci[0]);
+ return;
+ } else if (gsm->keep_alive && gsm->dlci[0] && !gsm->dlci[0]->dead) {
+ if (gsm->ka_retries > 0) {
+ /* T2 expired for keep-alive -> resend */
+ gsm->ka_retries--;
+ } else {
+ /* Start keep-alive timer */
+ gsm->ka_num++;
+ if (!gsm->ka_num)
+ gsm->ka_num++;
+ gsm->ka_retries = (signed int)gsm->n2;
+ }
+ gsm_control_command(gsm, CMD_TEST, &gsm->ka_num,
+ sizeof(gsm->ka_num));
+ mod_timer(&gsm->ka_timer,
+ jiffies + gsm->t2 * HZ / 100);
}
spin_unlock_irqrestore(&gsm->control_lock, flags);
}
@@ -2059,14 +2120,16 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
tty_port_tty_hangup(&dlci->port, false);
gsm_dlci_clear_queues(dlci->gsm, dlci);
/* Ensure that gsmtty_open() can return. */
- tty_port_set_initialized(&dlci->port, 0);
+ tty_port_set_initialized(&dlci->port, false);
wake_up_interruptible(&dlci->port.open_wait);
- } else
+ } else {
+ del_timer(&dlci->gsm->ka_timer);
dlci->gsm->dead = true;
+ }
/* A DLCI 0 close is a MUX termination so we need to kick that
back to userspace somehow */
gsm_dlci_data_kick(dlci);
- wake_up(&dlci->gsm->event);
+ wake_up_all(&dlci->gsm->event);
}
/**
@@ -2078,6 +2141,8 @@ static void gsm_dlci_close(struct gsm_dlci *dlci)
static void gsm_dlci_open(struct gsm_dlci *dlci)
{
+ struct gsm_mux *gsm = dlci->gsm;
+
/* Note that SABM UA .. SABM UA first UA lost can mean that we go
open -> open */
del_timer(&dlci->t1);
@@ -2087,8 +2152,15 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
if (debug & DBG_ERRORS)
pr_debug("DLCI %d goes open.\n", dlci->addr);
/* Send current modem state */
- if (dlci->addr)
+ if (dlci->addr) {
gsm_modem_update(dlci, 0);
+ } else {
+ /* Start keep-alive control */
+ gsm->ka_num = 0;
+ gsm->ka_retries = -1;
+ mod_timer(&gsm->ka_timer,
+ jiffies + gsm->keep_alive * HZ / 100);
+ }
gsm_dlci_data_kick(dlci);
wake_up(&dlci->gsm->event);
}
@@ -2267,6 +2339,7 @@ static void gsm_dlci_begin_close(struct gsm_dlci *dlci)
dlci->state = DLCI_CLOSING;
gsm_command(dlci->gsm, dlci->addr, DISC|PF);
mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100);
+ wake_up_interruptible(&gsm->event);
}
/**
@@ -2840,6 +2913,7 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
/* Finish outstanding timers, making sure they are done */
del_timer_sync(&gsm->kick_timer);
del_timer_sync(&gsm->t2_timer);
+ del_timer_sync(&gsm->ka_timer);
/* Finish writing to ldisc */
flush_work(&gsm->tx_work);
@@ -2987,6 +3061,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
INIT_LIST_HEAD(&gsm->tx_data_list);
timer_setup(&gsm->kick_timer, gsm_kick_timer, 0);
timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
+ timer_setup(&gsm->ka_timer, gsm_control_keep_alive, 0);
INIT_WORK(&gsm->tx_work, gsmld_write_task);
init_waitqueue_head(&gsm->event);
spin_lock_init(&gsm->control_lock);
@@ -3003,6 +3078,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
gsm->mru = 64; /* Default to encoding 1 so these should be 64 */
gsm->mtu = 64;
gsm->dead = true; /* Avoid early tty opens */
+ gsm->keep_alive = 0; /* Disabled */
/* Store the instance to the mux array or abort if no space is
* available.
@@ -3138,6 +3214,29 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
return 0;
}
+static void gsm_copy_config_ext_values(struct gsm_mux *gsm,
+ struct gsm_config_ext *ce)
+{
+ memset(ce, 0, sizeof(*ce));
+ ce->keep_alive = gsm->keep_alive;
+}
+
+static int gsm_config_ext(struct gsm_mux *gsm, struct gsm_config_ext *ce)
+{
+ unsigned int i;
+
+ /*
+ * Check that userspace doesn't put stuff in here to prevent breakages
+ * in the future.
+ */
+ for (i = 0; i < ARRAY_SIZE(ce->reserved); i++)
+ if (ce->reserved[i])
+ return -EINVAL;
+
+ gsm->keep_alive = ce->keep_alive;
+ return 0;
+}
+
/**
* gsmld_output - write to link
* @gsm: our mux
@@ -3456,6 +3555,7 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
unsigned long arg)
{
struct gsm_config c;
+ struct gsm_config_ext ce;
struct gsm_mux *gsm = tty->disc_data;
unsigned int base;
@@ -3472,6 +3572,15 @@ static int gsmld_ioctl(struct tty_struct *tty, unsigned int cmd,
case GSMIOC_GETFIRST:
base = mux_num_to_base(gsm);
return put_user(base + 1, (__u32 __user *)arg);
+ case GSMIOC_GETCONF_EXT:
+ gsm_copy_config_ext_values(gsm, &ce);
+ if (copy_to_user((void __user *)arg, &ce, sizeof(ce)))
+ return -EFAULT;
+ return 0;
+ case GSMIOC_SETCONF_EXT:
+ if (copy_from_user(&ce, (void __user *)arg, sizeof(ce)))
+ return -EFAULT;
+ return gsm_config_ext(gsm, &ce);
default:
return n_tty_ioctl_helper(tty, cmd, arg);
}
@@ -3770,16 +3879,43 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
return -EPROTONOSUPPORT;
}
-static int gsm_carrier_raised(struct tty_port *port)
+/**
+ * gsm_wait_modem_change - wait for modem status line change
+ * @dlci: channel
+ * @mask: modem status line bits
+ *
+ * The function returns if:
+ * - any given modem status line bit changed
+ * - the wait event function got interrupted (e.g. by a signal)
+ * - the underlying DLCI was closed
+ * - the underlying ldisc device was removed
+ */
+static int gsm_wait_modem_change(struct gsm_dlci *dlci, u32 mask)
+{
+ struct gsm_mux *gsm = dlci->gsm;
+ u32 old = dlci->modem_rx;
+ int ret;
+
+ ret = wait_event_interruptible(gsm->event, gsm->dead ||
+ dlci->state != DLCI_OPEN ||
+ (old ^ dlci->modem_rx) & mask);
+ if (gsm->dead)
+ return -ENODEV;
+ if (dlci->state != DLCI_OPEN)
+ return -EL2NSYNC;
+ return ret;
+}
+
+static bool gsm_carrier_raised(struct tty_port *port)
{
struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
struct gsm_mux *gsm = dlci->gsm;
/* Not yet open so no carrier info */
if (dlci->state != DLCI_OPEN)
- return 0;
+ return false;
if (debug & DBG_CD_ON)
- return 1;
+ return true;
/*
* Basic mode with control channel in ADM mode may not respond
@@ -3787,16 +3923,16 @@ static int gsm_carrier_raised(struct tty_port *port)
*/
if (gsm->encoding == GSM_BASIC_OPT &&
gsm->dlci[0]->mode == DLCI_MODE_ADM && !dlci->modem_rx)
- return 1;
+ return true;
return dlci->modem_rx & TIOCM_CD;
}
-static void gsm_dtr_rts(struct tty_port *port, int onoff)
+static void gsm_dtr_rts(struct tty_port *port, bool active)
{
struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
unsigned int modem_tx = dlci->modem_tx;
- if (onoff)
+ if (active)
modem_tx |= TIOCM_DTR | TIOCM_RTS;
else
modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
@@ -3880,7 +4016,7 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
dlci->modem_rx = 0;
/* We could in theory open and close before we wait - eg if we get
a DM straight back. This is ok as that will have caused a hangup */
- tty_port_set_initialized(port, 1);
+ tty_port_set_initialized(port, true);
/* Start sending off SABM messages */
if (gsm->initiator)
gsm_dlci_begin_open(dlci);
@@ -4029,6 +4165,8 @@ static int gsmtty_ioctl(struct tty_struct *tty,
gsm_destroy_network(dlci);
mutex_unlock(&dlci->mutex);
return 0;
+ case TIOCMIWAIT:
+ return gsm_wait_modem_change(dlci, (u32)arg);
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/tty/serial/8250/8250_dfl.c b/drivers/tty/serial/8250/8250_dfl.c
new file mode 100644
index 000000000000..6c5ff019df4b
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dfl.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA UART
+ *
+ * Copyright (C) 2022 Intel Corporation.
+ *
+ * Authors:
+ * Ananda Ravuri <ananda.ravuri@intel.com>
+ * Matthew Gerlach <matthew.gerlach@linux.intel.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/dfl.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/types.h>
+
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+
+#define DFHv1_PARAM_ID_CLK_FRQ 0x2
+#define DFHv1_PARAM_ID_FIFO_LEN 0x3
+
+#define DFHv1_PARAM_ID_REG_LAYOUT 0x4
+#define DFHv1_PARAM_REG_LAYOUT_WIDTH GENMASK_ULL(63, 32)
+#define DFHv1_PARAM_REG_LAYOUT_SHIFT GENMASK_ULL(31, 0)
+
+struct dfl_uart {
+ int line;
+};
+
+static int dfh_get_u64_param_val(struct dfl_device *dfl_dev, int param_id, u64 *pval)
+{
+ size_t psize;
+ u64 *p;
+
+ p = dfh_find_param(dfl_dev, param_id, &psize);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ if (psize != sizeof(*pval))
+ return -EINVAL;
+
+ *pval = *p;
+
+ return 0;
+}
+
+static int dfl_uart_get_params(struct dfl_device *dfl_dev, struct uart_8250_port *uart)
+{
+ struct device *dev = &dfl_dev->dev;
+ u64 fifo_len, clk_freq, reg_layout;
+ u32 reg_width;
+ int ret;
+
+ ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_CLK_FRQ, &clk_freq);
+ if (ret)
+ return dev_err_probe(dev, ret, "missing CLK_FRQ param\n");
+
+ uart->port.uartclk = clk_freq;
+
+ ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_FIFO_LEN, &fifo_len);
+ if (ret)
+ return dev_err_probe(dev, ret, "missing FIFO_LEN param\n");
+
+ switch (fifo_len) {
+ case 32:
+ uart->port.type = PORT_ALTR_16550_F32;
+ break;
+
+ case 64:
+ uart->port.type = PORT_ALTR_16550_F64;
+ break;
+
+ case 128:
+ uart->port.type = PORT_ALTR_16550_F128;
+ break;
+
+ default:
+ return dev_err_probe(dev, -EINVAL, "unsupported FIFO_LEN %llu\n", fifo_len);
+ }
+
+ ret = dfh_get_u64_param_val(dfl_dev, DFHv1_PARAM_ID_REG_LAYOUT, &reg_layout);
+ if (ret)
+ return dev_err_probe(dev, ret, "missing REG_LAYOUT param\n");
+
+ uart->port.regshift = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_SHIFT, reg_layout);
+ reg_width = FIELD_GET(DFHv1_PARAM_REG_LAYOUT_WIDTH, reg_layout);
+ switch (reg_width) {
+ case 4:
+ uart->port.iotype = UPIO_MEM32;
+ break;
+
+ case 2:
+ uart->port.iotype = UPIO_MEM16;
+ break;
+
+ default:
+ return dev_err_probe(dev, -EINVAL, "unsupported reg-width %u\n", reg_width);
+
+ }
+
+ return 0;
+}
+
+static int dfl_uart_probe(struct dfl_device *dfl_dev)
+{
+ struct device *dev = &dfl_dev->dev;
+ struct uart_8250_port uart = { };
+ struct dfl_uart *dfluart;
+ int ret;
+
+ uart.port.flags = UPF_IOREMAP;
+ uart.port.mapbase = dfl_dev->mmio_res.start;
+ uart.port.mapsize = resource_size(&dfl_dev->mmio_res);
+
+ ret = dfl_uart_get_params(dfl_dev, &uart);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed uart feature walk\n");
+
+ if (dfl_dev->num_irqs == 1)
+ uart.port.irq = dfl_dev->irqs[0];
+
+ dfluart = devm_kzalloc(dev, sizeof(*dfluart), GFP_KERNEL);
+ if (!dfluart)
+ return -ENOMEM;
+
+ dfluart->line = serial8250_register_8250_port(&uart);
+ if (dfluart->line < 0)
+ return dev_err_probe(dev, dfluart->line, "unable to register 8250 port.\n");
+
+ dev_set_drvdata(dev, dfluart);
+
+ return 0;
+}
+
+static void dfl_uart_remove(struct dfl_device *dfl_dev)
+{
+ struct dfl_uart *dfluart = dev_get_drvdata(&dfl_dev->dev);
+
+ serial8250_unregister_port(dfluart->line);
+}
+
+#define FME_FEATURE_ID_UART 0x24
+
+static const struct dfl_device_id dfl_uart_ids[] = {
+ { FME_ID, FME_FEATURE_ID_UART },
+ { }
+};
+MODULE_DEVICE_TABLE(dfl, dfl_uart_ids);
+
+static struct dfl_driver dfl_uart_driver = {
+ .drv = {
+ .name = "dfl-uart",
+ },
+ .id_table = dfl_uart_ids,
+ .probe = dfl_uart_probe,
+ .remove = dfl_uart_remove,
+};
+module_dfl_driver(dfl_uart_driver);
+
+MODULE_DESCRIPTION("DFL Intel UART driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
index f271becfc46c..0ebde0ab8167 100644
--- a/drivers/tty/serial/8250/8250_early.c
+++ b/drivers/tty/serial/8250/8250_early.c
@@ -136,11 +136,11 @@ static void __init init_port(struct earlycon_device *device)
unsigned char c;
unsigned int ier;
- serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */
+ serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */
ier = serial8250_early_in(port, UART_IER);
serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
serial8250_early_out(port, UART_FCR, 0); /* no fifo */
- serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */
+ serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
if (port->uartclk) {
divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 8e9f247590bd..c55be6fda0ca 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include "8250.h"
+#include "8250_pcilib.h"
/*
* init function returns:
@@ -89,28 +90,7 @@ static int
setup_port(struct serial_private *priv, struct uart_8250_port *port,
u8 bar, unsigned int offset, int regshift)
{
- struct pci_dev *dev = priv->dev;
-
- if (bar >= PCI_STD_NUM_BARS)
- return -EINVAL;
-
- if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
- if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev))
- return -ENOMEM;
-
- port->port.iotype = UPIO_MEM;
- port->port.iobase = 0;
- port->port.mapbase = pci_resource_start(dev, bar) + offset;
- port->port.membase = pcim_iomap_table(dev)[bar] + offset;
- port->port.regshift = regshift;
- } else {
- port->port.iotype = UPIO_PORT;
- port->port.iobase = pci_resource_start(dev, bar) + offset;
- port->port.mapbase = 0;
- port->port.membase = NULL;
- port->port.regshift = 0;
- }
- return 0;
+ return serial8250_pci_setup_port(priv->dev, port, bar, offset, regshift);
}
/*
@@ -5757,3 +5737,4 @@ module_pci_driver(serial_pci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
+MODULE_IMPORT_NS(SERIAL_8250_PCI);
diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c
new file mode 100644
index 000000000000..a3b25779d921
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_pci1xxxx.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Probe module for 8250/16550-type MCHP PCI serial ports.
+ *
+ * Based on drivers/tty/serial/8250/8250_pci.c,
+ *
+ * Copyright (C) 2022 Microchip Technology Inc., All Rights Reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/units.h>
+#include <linux/tty.h>
+
+#include <asm/byteorder.h>
+
+#include "8250.h"
+#include "8250_pcilib.h"
+
+#define PCI_DEVICE_ID_EFAR_PCI12000 0xa002
+#define PCI_DEVICE_ID_EFAR_PCI11010 0xa012
+#define PCI_DEVICE_ID_EFAR_PCI11101 0xa022
+#define PCI_DEVICE_ID_EFAR_PCI11400 0xa032
+#define PCI_DEVICE_ID_EFAR_PCI11414 0xa042
+
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p 0x0001
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012 0x0002
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013 0x0003
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023 0x0004
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123 0x0005
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01 0x0006
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02 0x0007
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03 0x0008
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12 0x0009
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13 0x000a
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23 0x000b
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0 0x000c
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1 0x000d
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2 0x000e
+#define PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 0x000f
+
+#define PCI_SUBDEVICE_ID_EFAR_PCI12000 PCI_DEVICE_ID_EFAR_PCI12000
+#define PCI_SUBDEVICE_ID_EFAR_PCI11010 PCI_DEVICE_ID_EFAR_PCI11010
+#define PCI_SUBDEVICE_ID_EFAR_PCI11101 PCI_DEVICE_ID_EFAR_PCI11101
+#define PCI_SUBDEVICE_ID_EFAR_PCI11400 PCI_DEVICE_ID_EFAR_PCI11400
+#define PCI_SUBDEVICE_ID_EFAR_PCI11414 PCI_DEVICE_ID_EFAR_PCI11414
+
+#define UART_ACTV_REG 0x11
+#define UART_BLOCK_SET_ACTIVE BIT(0)
+
+#define UART_PCI_CTRL_REG 0x80
+#define UART_PCI_CTRL_SET_MULTIPLE_MSI BIT(4)
+#define UART_PCI_CTRL_D3_CLK_ENABLE BIT(0)
+
+#define ADCL_CFG_REG 0x40
+#define ADCL_CFG_POL_SEL BIT(2)
+#define ADCL_CFG_PIN_SEL BIT(1)
+#define ADCL_CFG_EN BIT(0)
+
+#define UART_BIT_SAMPLE_CNT 16
+#define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8)
+#define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8)
+#define UART_CLOCK_DEFAULT (62500 * HZ_PER_KHZ)
+
+#define UART_WAKE_REG 0x8C
+#define UART_WAKE_MASK_REG 0x90
+#define UART_WAKE_N_PIN BIT(2)
+#define UART_WAKE_NCTS BIT(1)
+#define UART_WAKE_INT BIT(0)
+#define UART_WAKE_SRCS \
+ (UART_WAKE_N_PIN | UART_WAKE_NCTS | UART_WAKE_INT)
+
+#define UART_BAUD_CLK_DIVISOR_REG 0x54
+
+#define UART_RESET_REG 0x94
+#define UART_RESET_D3_RESET_DISABLE BIT(16)
+
+#define MAX_PORTS 4
+#define PORT_OFFSET 0x100
+
+static const int logical_to_physical_port_idx[][MAX_PORTS] = {
+ {0, 1, 2, 3}, /* PCI12000, PCI11010, PCI11101, PCI11400, PCI11414 */
+ {0, 1, 2, 3}, /* PCI4p */
+ {0, 1, 2, -1}, /* PCI3p012 */
+ {0, 1, 3, -1}, /* PCI3p013 */
+ {0, 2, 3, -1}, /* PCI3p023 */
+ {1, 2, 3, -1}, /* PCI3p123 */
+ {0, 1, -1, -1}, /* PCI2p01 */
+ {0, 2, -1, -1}, /* PCI2p02 */
+ {0, 3, -1, -1}, /* PCI2p03 */
+ {1, 2, -1, -1}, /* PCI2p12 */
+ {1, 3, -1, -1}, /* PCI2p13 */
+ {2, 3, -1, -1}, /* PCI2p23 */
+ {0, -1, -1, -1}, /* PCI1p0 */
+ {1, -1, -1, -1}, /* PCI1p1 */
+ {2, -1, -1, -1}, /* PCI1p2 */
+ {3, -1, -1, -1}, /* PCI1p3 */
+};
+
+struct pci1xxxx_8250 {
+ unsigned int nr;
+ void __iomem *membase;
+ int line[];
+};
+
+static int pci1xxxx_get_num_ports(struct pci_dev *dev)
+{
+ switch (dev->subsystem_device) {
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p0:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p1:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p2:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3:
+ case PCI_SUBDEVICE_ID_EFAR_PCI12000:
+ case PCI_SUBDEVICE_ID_EFAR_PCI11010:
+ case PCI_SUBDEVICE_ID_EFAR_PCI11101:
+ case PCI_SUBDEVICE_ID_EFAR_PCI11400:
+ default:
+ return 1;
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p01:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p02:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p03:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p12:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p13:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_2p23:
+ return 2;
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p012:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p123:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p013:
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_3p023:
+ return 3;
+ case PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_4p:
+ case PCI_SUBDEVICE_ID_EFAR_PCI11414:
+ return 4;
+ }
+}
+
+static unsigned int pci1xxxx_get_divisor(struct uart_port *port,
+ unsigned int baud, unsigned int *frac)
+{
+ unsigned int quot;
+
+ /*
+ * Calculate baud rate sampling period in nanoseconds.
+ * Fractional part x denotes x/255 parts of a nanosecond.
+ */
+ quot = NSEC_PER_SEC / (baud * UART_BIT_SAMPLE_CNT);
+ *frac = (NSEC_PER_SEC - quot * baud * UART_BIT_SAMPLE_CNT) *
+ 255 / UART_BIT_SAMPLE_CNT / baud;
+
+ return quot;
+}
+
+static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud,
+ unsigned int quot, unsigned int frac)
+{
+ writel(FIELD_PREP(BAUD_CLOCK_DIV_INT_MSK, quot) | frac,
+ port->membase + UART_BAUD_CLK_DIVISOR_REG);
+}
+
+static int pci1xxxx_rs485_config(struct uart_port *port,
+ struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ u32 delay_in_baud_periods;
+ u32 baud_period_in_ns;
+ u32 mode_cfg = 0;
+ u32 clock_div;
+
+ /*
+ * pci1xxxx's uart hardware supports only RTS delay after
+ * Tx and in units of bit times to a maximum of 15
+ */
+ if (rs485->flags & SER_RS485_ENABLED) {
+ mode_cfg = ADCL_CFG_EN | ADCL_CFG_PIN_SEL;
+
+ if (!(rs485->flags & SER_RS485_RTS_ON_SEND))
+ mode_cfg |= ADCL_CFG_POL_SEL;
+
+ if (rs485->delay_rts_after_send) {
+ clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG);
+ baud_period_in_ns =
+ FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) *
+ UART_BIT_SAMPLE_CNT;
+ delay_in_baud_periods =
+ rs485->delay_rts_after_send * NSEC_PER_MSEC /
+ baud_period_in_ns;
+ delay_in_baud_periods =
+ min_t(u32, delay_in_baud_periods,
+ FIELD_MAX(ADCL_CFG_RTS_DELAY_MASK));
+ mode_cfg |= FIELD_PREP(ADCL_CFG_RTS_DELAY_MASK,
+ delay_in_baud_periods);
+ rs485->delay_rts_after_send =
+ baud_period_in_ns * delay_in_baud_periods /
+ NSEC_PER_MSEC;
+ }
+ }
+ writel(mode_cfg, port->membase + ADCL_CFG_REG);
+ return 0;
+}
+
+static const struct serial_rs485 pci1xxxx_rs485_supported = {
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+ SER_RS485_RTS_AFTER_SEND,
+ .delay_rts_after_send = 1,
+ /* Delay RTS before send is not supported */
+};
+
+static bool pci1xxxx_port_suspend(int line)
+{
+ struct uart_8250_port *up = serial8250_get_port(line);
+ struct uart_port *port = &up->port;
+ struct tty_port *tport = &port->state->port;
+ unsigned long flags;
+ bool ret = false;
+ u8 wakeup_mask;
+
+ mutex_lock(&tport->mutex);
+ if (port->suspended == 0 && port->dev) {
+ wakeup_mask = readb(up->port.membase + UART_WAKE_MASK_REG);
+
+ spin_lock_irqsave(&port->lock, flags);
+ port->mctrl &= ~TIOCM_OUT2;
+ port->ops->set_mctrl(port, port->mctrl);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ ret = (wakeup_mask & UART_WAKE_SRCS) != UART_WAKE_SRCS;
+ }
+
+ writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
+}
+
+static void pci1xxxx_port_resume(int line)
+{
+ struct uart_8250_port *up = serial8250_get_port(line);
+ struct uart_port *port = &up->port;
+ struct tty_port *tport = &port->state->port;
+ unsigned long flags;
+
+ mutex_lock(&tport->mutex);
+ writeb(UART_BLOCK_SET_ACTIVE, port->membase + UART_ACTV_REG);
+ writeb(UART_WAKE_SRCS, port->membase + UART_WAKE_REG);
+
+ if (port->suspended == 0) {
+ spin_lock_irqsave(&port->lock, flags);
+ port->mctrl |= TIOCM_OUT2;
+ port->ops->set_mctrl(port, port->mctrl);
+ spin_unlock_irqrestore(&port->lock, flags);
+ }
+ mutex_unlock(&tport->mutex);
+}
+
+static int pci1xxxx_suspend(struct device *dev)
+{
+ struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
+ struct pci_dev *pcidev = to_pci_dev(dev);
+ bool wakeup = false;
+ unsigned int data;
+ void __iomem *p;
+ int i;
+
+ for (i = 0; i < priv->nr; i++) {
+ if (priv->line[i] >= 0) {
+ serial8250_suspend_port(priv->line[i]);
+ wakeup |= pci1xxxx_port_suspend(priv->line[i]);
+ }
+ }
+
+ p = pci_ioremap_bar(pcidev, 0);
+ if (!p) {
+ dev_err(dev, "remapping of bar 0 memory failed");
+ return -ENOMEM;
+ }
+
+ data = readl(p + UART_RESET_REG);
+ writel(data | UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG);
+
+ if (wakeup)
+ writeb(UART_PCI_CTRL_D3_CLK_ENABLE, p + UART_PCI_CTRL_REG);
+
+ iounmap(p);
+ device_set_wakeup_enable(dev, true);
+ pci_wake_from_d3(pcidev, true);
+
+ return 0;
+}
+
+static int pci1xxxx_resume(struct device *dev)
+{
+ struct pci1xxxx_8250 *priv = dev_get_drvdata(dev);
+ struct pci_dev *pcidev = to_pci_dev(dev);
+ unsigned int data;
+ void __iomem *p;
+ int i;
+
+ p = pci_ioremap_bar(pcidev, 0);
+ if (!p) {
+ dev_err(dev, "remapping of bar 0 memory failed");
+ return -ENOMEM;
+ }
+
+ data = readl(p + UART_RESET_REG);
+ writel(data & ~UART_RESET_D3_RESET_DISABLE, p + UART_RESET_REG);
+ iounmap(p);
+
+ for (i = 0; i < priv->nr; i++) {
+ if (priv->line[i] >= 0) {
+ pci1xxxx_port_resume(priv->line[i]);
+ serial8250_resume_port(priv->line[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int pci1xxxx_setup(struct pci_dev *pdev,
+ struct uart_8250_port *port, int port_idx)
+{
+ int ret;
+
+ port->port.flags |= UPF_FIXED_TYPE | UPF_SKIP_TEST;
+ port->port.type = PORT_MCHP16550A;
+ port->port.set_termios = serial8250_do_set_termios;
+ port->port.get_divisor = pci1xxxx_get_divisor;
+ port->port.set_divisor = pci1xxxx_set_divisor;
+ port->port.rs485_config = pci1xxxx_rs485_config;
+ port->port.rs485_supported = pci1xxxx_rs485_supported;
+
+ ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0);
+ if (ret < 0)
+ return ret;
+
+ writeb(UART_BLOCK_SET_ACTIVE, port->port.membase + UART_ACTV_REG);
+ writeb(UART_WAKE_SRCS, port->port.membase + UART_WAKE_REG);
+ writeb(UART_WAKE_N_PIN, port->port.membase + UART_WAKE_MASK_REG);
+
+ return 0;
+}
+
+static unsigned int pci1xxxx_get_max_port(int subsys_dev)
+{
+ unsigned int i = MAX_PORTS;
+
+ if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx))
+ while (i--) {
+ if (logical_to_physical_port_idx[subsys_dev][i] != -1)
+ return logical_to_physical_port_idx[subsys_dev][i] + 1;
+ }
+
+ if (subsys_dev == PCI_SUBDEVICE_ID_EFAR_PCI11414)
+ return 4;
+
+ return 1;
+}
+
+static int pci1xxxx_logical_to_physical_port_translate(int subsys_dev, int port)
+{
+ if (subsys_dev < ARRAY_SIZE(logical_to_physical_port_idx))
+ return logical_to_physical_port_idx[subsys_dev][port];
+
+ return logical_to_physical_port_idx[0][port];
+}
+
+static int pci1xxxx_serial_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct pci1xxxx_8250 *priv;
+ struct uart_8250_port uart;
+ unsigned int max_vec_reqd;
+ unsigned int nr_ports, i;
+ int num_vectors;
+ int subsys_dev;
+ int port_idx;
+ int rc;
+
+ rc = pcim_enable_device(pdev);
+ if (rc)
+ return rc;
+
+ nr_ports = pci1xxxx_get_num_ports(pdev);
+
+ priv = devm_kzalloc(dev, struct_size(priv, line, nr_ports), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->membase = pci_ioremap_bar(pdev, 0);
+ if (!priv->membase)
+ return -ENOMEM;
+
+ pci_set_master(pdev);
+
+ priv->nr = nr_ports;
+
+ subsys_dev = pdev->subsystem_device;
+ max_vec_reqd = pci1xxxx_get_max_port(subsys_dev);
+
+ num_vectors = pci_alloc_irq_vectors(pdev, 1, max_vec_reqd, PCI_IRQ_ALL_TYPES);
+ if (num_vectors < 0) {
+ pci_iounmap(pdev, priv->membase);
+ return num_vectors;
+ }
+
+ memset(&uart, 0, sizeof(uart));
+ uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT;
+ uart.port.uartclk = UART_CLOCK_DEFAULT;
+ uart.port.dev = dev;
+
+ if (num_vectors == max_vec_reqd)
+ writeb(UART_PCI_CTRL_SET_MULTIPLE_MSI, priv->membase + UART_PCI_CTRL_REG);
+
+ for (i = 0; i < nr_ports; i++) {
+ priv->line[i] = -ENODEV;
+
+ port_idx = pci1xxxx_logical_to_physical_port_translate(subsys_dev, i);
+
+ if (num_vectors == max_vec_reqd)
+ uart.port.irq = pci_irq_vector(pdev, port_idx);
+ else
+ uart.port.irq = pci_irq_vector(pdev, 0);
+
+ rc = pci1xxxx_setup(pdev, &uart, port_idx);
+ if (rc) {
+ dev_warn(dev, "Failed to setup port %u\n", i);
+ continue;
+ }
+
+ priv->line[i] = serial8250_register_8250_port(&uart);
+ if (priv->line[i] < 0) {
+ dev_warn(dev,
+ "Couldn't register serial port %lx, irq %d, type %d, error %d\n",
+ uart.port.iobase, uart.port.irq, uart.port.iotype,
+ priv->line[i]);
+ }
+ }
+
+ pci_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static void pci1xxxx_serial_remove(struct pci_dev *dev)
+{
+ struct pci1xxxx_8250 *priv = pci_get_drvdata(dev);
+ unsigned int i;
+
+ for (i = 0; i < priv->nr; i++) {
+ if (priv->line[i] >= 0)
+ serial8250_unregister_port(priv->line[i]);
+ }
+
+ pci_free_irq_vectors(dev);
+ pci_iounmap(dev, priv->membase);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pci1xxxx_pm_ops, pci1xxxx_suspend, pci1xxxx_resume);
+
+static const struct pci_device_id pci1xxxx_pci_tbl[] = {
+ { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11010) },
+ { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11101) },
+ { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11400) },
+ { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI11414) },
+ { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_PCI12000) },
+ {}
+};
+MODULE_DEVICE_TABLE(pci, pci1xxxx_pci_tbl);
+
+static struct pci_driver pci1xxxx_pci_driver = {
+ .name = "pci1xxxx serial",
+ .probe = pci1xxxx_serial_probe,
+ .remove = pci1xxxx_serial_remove,
+ .driver = {
+ .pm = pm_sleep_ptr(&pci1xxxx_pm_ops),
+ },
+ .id_table = pci1xxxx_pci_tbl,
+};
+module_pci_driver(pci1xxxx_pci_driver);
+
+static_assert((ARRAY_SIZE(logical_to_physical_port_idx) == PCI_SUBDEVICE_ID_EFAR_PCI1XXXX_1p3 + 1));
+
+MODULE_IMPORT_NS(SERIAL_8250_PCI);
+MODULE_DESCRIPTION("Microchip Technology Inc. PCIe to UART module");
+MODULE_AUTHOR("Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>");
+MODULE_AUTHOR("Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c
new file mode 100644
index 000000000000..d234e9194feb
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_pcilib.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 8250 PCI library.
+ *
+ * Copyright (C) 2001 Russell King, All Rights Reserved.
+ */
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "8250.h"
+#include "8250_pcilib.h"
+
+int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port,
+ u8 bar, unsigned int offset, int regshift)
+{
+ if (bar >= PCI_STD_NUM_BARS)
+ return -EINVAL;
+
+ if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
+ if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev))
+ return -ENOMEM;
+
+ port->port.iotype = UPIO_MEM;
+ port->port.iobase = 0;
+ port->port.mapbase = pci_resource_start(dev, bar) + offset;
+ port->port.membase = pcim_iomap_table(dev)[bar] + offset;
+ port->port.regshift = regshift;
+ } else {
+ port->port.iotype = UPIO_PORT;
+ port->port.iobase = pci_resource_start(dev, bar) + offset;
+ port->port.mapbase = 0;
+ port->port.membase = NULL;
+ port->port.regshift = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(serial8250_pci_setup_port, SERIAL_8250_PCI);
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_pcilib.h b/drivers/tty/serial/8250/8250_pcilib.h
new file mode 100644
index 000000000000..1aaf1b50ce9c
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_pcilib.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * 8250 PCI library header file.
+ *
+ * Copyright (C) 2001 Russell King, All Rights Reserved.
+ */
+
+#include <linux/types.h>
+
+struct pci_dev;
+
+struct uart_8250_port;
+
+int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, u8 bar,
+ unsigned int offset, int regshift);
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index beba8f38b3dc..fa43df05342b 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -313,6 +313,14 @@ static const struct serial8250_config uart_config[] = {
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
+ [PORT_MCHP16550A] = {
+ .name = "MCHP16550A",
+ .fifo_size = 256,
+ .tx_loadsz = 256,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01,
+ .rxtrig_bytes = {2, 66, 130, 194},
+ .flags = UART_CAP_FIFO,
+ },
};
/* Uart divisor latch read */
@@ -1050,11 +1058,12 @@ static void autoconfig_16550a(struct uart_8250_port *up)
serial_out(up, UART_LCR, 0);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR7_64BYTE);
- status1 = serial_in(up, UART_IIR) >> 5;
+ status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO |
+ UART_IIR_FIFO_ENABLED);
serial_out(up, UART_FCR, 0);
serial_out(up, UART_LCR, 0);
- if (status1 == 7)
+ if (status1 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED))
up->port.type = PORT_16550A_FSL64;
else
DEBUG_AUTOCONF("Motorola 8xxx DUART ");
@@ -1122,17 +1131,20 @@ static void autoconfig_16550a(struct uart_8250_port *up)
*/
serial_out(up, UART_LCR, 0);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
- status1 = serial_in(up, UART_IIR) >> 5;
+ status1 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
- status2 = serial_in(up, UART_IIR) >> 5;
+ status2 = serial_in(up, UART_IIR) & (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED);
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+
serial_out(up, UART_LCR, 0);
DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
- if (status1 == 6 && status2 == 7) {
+ if (status1 == UART_IIR_FIFO_ENABLED_16550A &&
+ status2 == (UART_IIR_64BYTE_FIFO | UART_IIR_FIFO_ENABLED_16550A)) {
up->port.type = PORT_16750;
up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP;
return;
@@ -1236,14 +1248,14 @@ static void autoconfig(struct uart_8250_port *up)
* Mask out IER[7:4] bits for test as some UARTs (e.g. TL
* 16C754B) allow only to modify them if an EFR bit is set.
*/
- scratch2 = serial_in(up, UART_IER) & 0x0f;
- serial_out(up, UART_IER, 0x0F);
+ scratch2 = serial_in(up, UART_IER) & UART_IER_ALL_INTR;
+ serial_out(up, UART_IER, UART_IER_ALL_INTR);
#ifdef __i386__
outb(0, 0x080);
#endif
- scratch3 = serial_in(up, UART_IER) & 0x0f;
+ scratch3 = serial_in(up, UART_IER) & UART_IER_ALL_INTR;
serial_out(up, UART_IER, scratch);
- if (scratch2 != 0 || scratch3 != 0x0F) {
+ if (scratch2 != 0 || scratch3 != UART_IER_ALL_INTR) {
/*
* We failed; there's nothing here
*/
@@ -1267,10 +1279,10 @@ static void autoconfig(struct uart_8250_port *up)
* that conflicts with COM 1-4 --- we hope!
*/
if (!(port->flags & UPF_SKIP_TEST)) {
- serial8250_out_MCR(up, UART_MCR_LOOP | 0x0A);
- status1 = serial_in(up, UART_MSR) & 0xF0;
+ serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS);
+ status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS;
serial8250_out_MCR(up, save_mcr);
- if (status1 != 0x90) {
+ if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) {
spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
status1);
@@ -1293,22 +1305,19 @@ static void autoconfig(struct uart_8250_port *up)
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
- /* Assign this as it is to truncate any bits above 7. */
- scratch = serial_in(up, UART_IIR);
-
- switch (scratch >> 6) {
- case 0:
+ switch (serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED) {
+ case UART_IIR_FIFO_ENABLED_8250:
autoconfig_8250(up);
break;
- case 1:
- port->type = PORT_UNKNOWN;
- break;
- case 2:
+ case UART_IIR_FIFO_ENABLED_16550:
port->type = PORT_16550;
break;
- case 3:
+ case UART_IIR_FIFO_ENABLED_16550A:
autoconfig_16550a(up);
break;
+ default:
+ port->type = PORT_UNKNOWN;
+ break;
}
#ifdef CONFIG_SERIAL_8250_RSA
@@ -1394,7 +1403,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
serial8250_out_MCR(up,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
- serial_out(up, UART_IER, 0x0f); /* enable all intrs */
+ serial_out(up, UART_IER, UART_IER_ALL_INTR);
serial_in(up, UART_LSR);
serial_in(up, UART_RX);
serial_in(up, UART_IIR);
@@ -1511,8 +1520,6 @@ static inline void __stop_tx(struct uart_8250_port *p)
u16 lsr = serial_lsr_in(p);
u64 stop_delay = 0;
- p->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
-
if (!(lsr & UART_LSR_THRE))
return;
/*
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index b0f62345bc84..978dc196c29b 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -129,9 +129,13 @@ config SERIAL_8250_DMA
This builds DMA support that can be used with 8250/16650
compatible UART controllers that support DMA signaling.
+config SERIAL_8250_PCILIB
+ bool
+
config SERIAL_8250_PCI
tristate "8250/16550 PCI device support"
depends on SERIAL_8250 && PCI
+ select SERIAL_8250_PCILIB
default SERIAL_8250
help
This builds standard PCI serial support. You may be able to
@@ -291,6 +295,17 @@ config SERIAL_8250_HUB6
To compile this driver as a module, choose M here: the module
will be called 8250_hub6.
+config SERIAL_8250_PCI1XXXX
+ tristate "Microchip 8250 based serial port"
+ depends on SERIAL_8250 && PCI
+ select SERIAL_8250_PCILIB
+ default SERIAL_8250
+ help
+ Select this option if you have a setup with Microchip PCIe
+ Switch with serial port enabled and wish to enable 8250
+ serial driver for the serial interface. This driver support
+ will ensure to support baud rates upto 1.5Mpbs.
+
#
# Misc. options/drivers.
#
@@ -370,6 +385,18 @@ config SERIAL_8250_FSL
erratum for Freescale 16550 UARTs in the 8250 driver. It also
enables support for ACPI enumeration.
+config SERIAL_8250_DFL
+ tristate "DFL bus driver for Altera 16550 UART"
+ depends on SERIAL_8250 && FPGA_DFL
+ help
+ This option enables support for a Device Feature List (DFL) bus
+ driver for the Altera 16550 UART. One or more Altera 16550 UARTs
+ can be instantiated in a FPGA and then be discovered during
+ enumeration of the DFL bus.
+
+ To compile this driver as a module, chose M here: the
+ module will be called 8250_dfl.
+
config SERIAL_8250_DW
tristate "Support for Synopsys DesignWare 8250 quirks"
depends on SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 1615bfdde2a0..4fc2fc1f41b6 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250.o 8250_base.o
8250_base-$(CONFIG_SERIAL_8250_DMA) += 8250_dma.o
8250_base-$(CONFIG_SERIAL_8250_DWLIB) += 8250_dwlib.o
8250_base-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o
+8250_base-$(CONFIG_SERIAL_8250_PCILIB) += 8250_pcilib.o
obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
obj-$(CONFIG_SERIAL_8250_EXAR) += 8250_exar.o
@@ -26,8 +27,10 @@ obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o
obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o
obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o
obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o
+obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o
obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o
obj-$(CONFIG_SERIAL_8250_MEN_MCB) += 8250_men_mcb.o
+obj-$(CONFIG_SERIAL_8250_DFL) += 8250_dfl.o
obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o
obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o
obj-$(CONFIG_SERIAL_8250_IOC3) += 8250_ioc3.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index ed0672d2d0ef..625358f44419 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -73,17 +73,17 @@ config SERIAL_AMBA_PL011_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
-config SERIAL_EARLYCON_ARM_SEMIHOST
- bool "Early console using ARM semihosting"
- depends on ARM64 || ARM
+config SERIAL_EARLYCON_SEMIHOST
+ bool "Early console using Arm compatible semihosting"
+ depends on ARM64 || ARM || RISCV
select SERIAL_CORE
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
- Support for early debug console using ARM semihosting. This enables
- the console before standard serial driver is probed. This is enabled
- with "earlycon=smh" on the kernel command line. The console is
- enabled when early_param is processed.
+ Support for early debug console using Arm compatible semihosting.
+ This enables the console before standard serial driver is probed.
+ This is enabled with "earlycon=smh" on the kernel command line.
+ The console is enabled when early_param is processed.
config SERIAL_EARLYCON_RISCV_SBI
bool "Early console using RISC-V SBI"
@@ -1507,7 +1507,7 @@ config SERIAL_MILBEAUT_USIO_CONSOLE
config SERIAL_LITEUART
tristate "LiteUART serial port support"
depends on HAS_IOMEM
- depends on OF || COMPILE_TEST
+ depends on OF
depends on LITEX || COMPILE_TEST
select SERIAL_CORE
help
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 238a9557b487..cd9afd9e3018 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
-obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
+obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
# These Sparc drivers have to appear before others such as 8250
diff --git a/drivers/tty/serial/earlycon-arm-semihost.c b/drivers/tty/serial/earlycon-semihost.c
index fcdec5f42376..e4692a8433f9 100644
--- a/drivers/tty/serial/earlycon-arm-semihost.c
+++ b/drivers/tty/serial/earlycon-semihost.c
@@ -11,30 +11,7 @@
#include <linux/console.h>
#include <linux/init.h>
#include <linux/serial_core.h>
-
-#ifdef CONFIG_THUMB2_KERNEL
-#define SEMIHOST_SWI "0xab"
-#else
-#define SEMIHOST_SWI "0x123456"
-#endif
-
-/*
- * Semihosting-based debug console
- */
-static void smh_putc(struct uart_port *port, unsigned char c)
-{
-#ifdef CONFIG_ARM64
- asm volatile("mov x1, %0\n"
- "mov x0, #3\n"
- "hlt 0xf000\n"
- : : "r" (&c) : "x0", "x1", "memory");
-#else
- asm volatile("mov r1, %0\n"
- "mov r0, #3\n"
- "svc " SEMIHOST_SWI "\n"
- : : "r" (&c) : "r0", "r1", "memory");
-#endif
-}
+#include <asm/semihost.h>
static void smh_write(struct console *con, const char *s, unsigned n)
{
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index 4f6e9bf57169..a5fbb6ed38ae 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -120,7 +120,13 @@ static int __init parse_options(struct earlycon_device *device, char *options)
}
if (options) {
+ char *uartclk;
+
device->baud = simple_strtoul(options, NULL, 0);
+ uartclk = strchr(options, ',');
+ if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0)
+ pr_warn("[%s] unsupported earlycon uart clkrate option\n",
+ options);
length = min(strcspn(options, " ") + 1,
(size_t)(sizeof(device->options)));
strscpy(device->options, options, length);
@@ -139,7 +145,8 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
buf = NULL;
spin_lock_init(&port->lock);
- port->uartclk = BASE_BAUD * 16;
+ if (!port->uartclk)
+ port->uartclk = BASE_BAUD * 16;
if (port->mapbase)
port->membase = earlycon_map(port->mapbase, 64);
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 5e69fb73f570..e945f41b93d4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -5,6 +5,8 @@
* Copyright 2012-2014 Freescale Semiconductor, Inc.
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
@@ -181,7 +183,7 @@
#define UARTCTRL_SBK 0x00010000
#define UARTCTRL_MA1IE 0x00008000
#define UARTCTRL_MA2IE 0x00004000
-#define UARTCTRL_IDLECFG 0x00000100
+#define UARTCTRL_IDLECFG GENMASK(10, 8)
#define UARTCTRL_LOOPS 0x00000080
#define UARTCTRL_DOZEEN 0x00000040
#define UARTCTRL_RSRC 0x00000020
@@ -199,6 +201,7 @@
#define UARTDATA_MASK 0x3ff
#define UARTMODIR_IREN 0x00020000
+#define UARTMODIR_RTSWATER GENMASK(10, 8)
#define UARTMODIR_TXCTSSRC 0x00000020
#define UARTMODIR_TXCTSC 0x00000010
#define UARTMODIR_RXRTSE 0x00000008
@@ -212,6 +215,7 @@
#define UARTFIFO_RXUF 0x00010000
#define UARTFIFO_TXFLUSH 0x00008000
#define UARTFIFO_RXFLUSH 0x00004000
+#define UARTFIFO_RXIDEN GENMASK(12, 10)
#define UARTFIFO_TXOFE 0x00000200
#define UARTFIFO_RXUFE 0x00000100
#define UARTFIFO_TXFE 0x00000080
@@ -238,7 +242,7 @@
#define DRIVER_NAME "fsl-lpuart"
#define DEV_NAME "ttyLP"
-#define UART_NR 6
+#define UART_NR 8
/* IMX lpuart has four extra unused regs located at the beginning */
#define IMX_REG_OFF 0x10
@@ -248,6 +252,7 @@ enum lpuart_type {
LS1021A_LPUART,
LS1028A_LPUART,
IMX7ULP_LPUART,
+ IMX8ULP_LPUART,
IMX8QXP_LPUART,
IMXRT1050_LPUART,
};
@@ -260,6 +265,7 @@ struct lpuart_port {
unsigned int txfifo_size;
unsigned int rxfifo_size;
+ u8 rx_watermark;
bool lpuart_dma_tx_use;
bool lpuart_dma_rx_use;
struct dma_chan *dma_tx_chan;
@@ -286,38 +292,52 @@ struct lpuart_soc_data {
enum lpuart_type devtype;
char iotype;
u8 reg_off;
+ u8 rx_watermark;
};
static const struct lpuart_soc_data vf_data = {
.devtype = VF610_LPUART,
.iotype = UPIO_MEM,
+ .rx_watermark = 1,
};
static const struct lpuart_soc_data ls1021a_data = {
.devtype = LS1021A_LPUART,
.iotype = UPIO_MEM32BE,
+ .rx_watermark = 1,
};
static const struct lpuart_soc_data ls1028a_data = {
.devtype = LS1028A_LPUART,
.iotype = UPIO_MEM32,
+ .rx_watermark = 1,
};
static struct lpuart_soc_data imx7ulp_data = {
.devtype = IMX7ULP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 1,
+};
+
+static struct lpuart_soc_data imx8ulp_data = {
+ .devtype = IMX8ULP_LPUART,
+ .iotype = UPIO_MEM32,
+ .reg_off = IMX_REG_OFF,
+ .rx_watermark = 3,
};
static struct lpuart_soc_data imx8qxp_data = {
.devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 31,
};
static struct lpuart_soc_data imxrt1050_data = {
.devtype = IMXRT1050_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
+ .rx_watermark = 1,
};
static const struct of_device_id lpuart_dt_ids[] = {
@@ -325,6 +345,7 @@ static const struct of_device_id lpuart_dt_ids[] = {
{ .compatible = "fsl,ls1021a-lpuart", .data = &ls1021a_data, },
{ .compatible = "fsl,ls1028a-lpuart", .data = &ls1028a_data, },
{ .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
+ { .compatible = "fsl,imx8ulp-lpuart", .data = &imx8ulp_data, },
{ .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
{ .compatible = "fsl,imxrt1050-lpuart", .data = &imxrt1050_data},
{ /* sentinel */ }
@@ -345,6 +366,11 @@ static inline bool is_imx7ulp_lpuart(struct lpuart_port *sport)
return sport->devtype == IMX7ULP_LPUART;
}
+static inline bool is_imx8ulp_lpuart(struct lpuart_port *sport)
+{
+ return sport->devtype == IMX8ULP_LPUART;
+}
+
static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
{
return sport->devtype == IMX8QXP_LPUART;
@@ -1387,9 +1413,9 @@ static int lpuart32_config_rs485(struct uart_port *port, struct ktermios *termio
* Note: UART is assumed to be active high.
*/
if (rs485->flags & SER_RS485_RTS_ON_SEND)
- modem &= ~UARTMODEM_TXRTSPOL;
- else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
modem |= UARTMODEM_TXRTSPOL;
+ else if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
+ modem &= ~UARTMODEM_TXRTSPOL;
}
lpuart32_write(&sport->port, modem, UARTMODIR);
@@ -1462,12 +1488,32 @@ static void lpuart_break_ctl(struct uart_port *port, int break_state)
static void lpuart32_break_ctl(struct uart_port *port, int break_state)
{
- unsigned long temp;
+ unsigned long temp, modem;
+ struct tty_struct *tty;
+ unsigned int cflag = 0;
+
+ tty = tty_port_tty_get(&port->state->port);
+ if (tty) {
+ cflag = tty->termios.c_cflag;
+ tty_kref_put(tty);
+ }
temp = lpuart32_read(port, UARTCTRL) & ~UARTCTRL_SBK;
+ modem = lpuart32_read(port, UARTMODIR);
- if (break_state != 0)
+ if (break_state != 0) {
temp |= UARTCTRL_SBK;
+ /*
+ * LPUART CTS has higher priority than SBK, need to disable CTS before
+ * asserting SBK to avoid any interference if flow control is enabled.
+ */
+ if (cflag & CRTSCTS && modem & UARTMODIR_TXCTSE)
+ lpuart32_write(port, modem & ~UARTMODIR_TXCTSE, UARTMODIR);
+ } else {
+ /* Re-enable the CTS when break off. */
+ if (cflag & CRTSCTS && !(modem & UARTMODIR_TXCTSE))
+ lpuart32_write(port, modem | UARTMODIR_TXCTSE, UARTMODIR);
+ }
lpuart32_write(port, temp, UARTCTRL);
}
@@ -1497,8 +1543,10 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
}
+ if (uart_console(&sport->port))
+ sport->rx_watermark = 1;
writeb(0, sport->port.membase + UARTTWFIFO);
- writeb(1, sport->port.membase + UARTRWFIFO);
+ writeb(sport->rx_watermark, sport->port.membase + UARTRWFIFO);
/* Restore cr2 */
writeb(cr2_saved, sport->port.membase + UARTCR2);
@@ -1523,19 +1571,30 @@ static void lpuart32_setup_watermark(struct lpuart_port *sport)
ctrl = lpuart32_read(&sport->port, UARTCTRL);
ctrl_saved = ctrl;
ctrl &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_TE |
- UARTCTRL_RIE | UARTCTRL_RE);
+ UARTCTRL_RIE | UARTCTRL_RE | UARTCTRL_ILIE);
lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* enable FIFO mode */
val = lpuart32_read(&sport->port, UARTFIFO);
val |= UARTFIFO_TXFE | UARTFIFO_RXFE;
val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
+ val |= FIELD_PREP(UARTFIFO_RXIDEN, 0x3);
lpuart32_write(&sport->port, val, UARTFIFO);
/* set the watermark */
- val = (0x1 << UARTWATER_RXWATER_OFF) | (0x0 << UARTWATER_TXWATER_OFF);
+ if (uart_console(&sport->port))
+ sport->rx_watermark = 1;
+ val = (sport->rx_watermark << UARTWATER_RXWATER_OFF) |
+ (0x0 << UARTWATER_TXWATER_OFF);
lpuart32_write(&sport->port, val, UARTWATER);
+ /* set RTS watermark */
+ if (!uart_console(&sport->port)) {
+ val = lpuart32_read(&sport->port, UARTMODIR);
+ val |= FIELD_PREP(UARTMODIR_RTSWATER, sport->rxfifo_size >> 1);
+ lpuart32_write(&sport->port, val, UARTMODIR);
+ }
+
/* Restore cr2 */
lpuart32_write(&sport->port, ctrl_saved, UARTCTRL);
}
@@ -1547,7 +1606,8 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport)
lpuart32_setup_watermark(sport);
temp = lpuart32_read(&sport->port, UARTCTRL);
- temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
+ temp |= UARTCTRL_RE | UARTCTRL_TE;
+ temp |= FIELD_PREP(UARTCTRL_IDLECFG, 0x7);
lpuart32_write(&sport->port, temp, UARTCTRL);
}
@@ -1679,19 +1739,23 @@ static int lpuart_startup(struct uart_port *port)
return 0;
}
+static void lpuart32_hw_disable(struct lpuart_port *sport)
+{
+ unsigned long temp;
+
+ temp = lpuart32_read(&sport->port, UARTCTRL);
+ temp &= ~(UARTCTRL_RIE | UARTCTRL_ILIE | UARTCTRL_RE |
+ UARTCTRL_TIE | UARTCTRL_TE);
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+}
+
static void lpuart32_configure(struct lpuart_port *sport)
{
unsigned long temp;
- if (sport->lpuart_dma_rx_use) {
- /* RXWATER must be 0 */
- temp = lpuart32_read(&sport->port, UARTWATER);
- temp &= ~(UARTWATER_WATER_MASK << UARTWATER_RXWATER_OFF);
- lpuart32_write(&sport->port, temp, UARTWATER);
- }
temp = lpuart32_read(&sport->port, UARTCTRL);
if (!sport->lpuart_dma_rx_use)
- temp |= UARTCTRL_RIE;
+ temp |= UARTCTRL_RIE | UARTCTRL_ILIE;
if (!sport->lpuart_dma_tx_use)
temp |= UARTCTRL_TIE;
lpuart32_write(&sport->port, temp, UARTCTRL);
@@ -1703,11 +1767,12 @@ static void lpuart32_hw_setup(struct lpuart_port *sport)
spin_lock_irqsave(&sport->port.lock, flags);
- lpuart32_setup_watermark_enable(sport);
+ lpuart32_hw_disable(sport);
lpuart_rx_dma_startup(sport);
lpuart_tx_dma_startup(sport);
+ lpuart32_setup_watermark_enable(sport);
lpuart32_configure(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1796,10 +1861,19 @@ static void lpuart32_shutdown(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
- /* disable Rx/Tx and interrupts */
+ /* clear status */
+ temp = lpuart32_read(&sport->port, UARTSTAT);
+ lpuart32_write(&sport->port, temp, UARTSTAT);
+
+ /* disable Rx/Tx DMA */
+ temp = lpuart32_read(port, UARTBAUD);
+ temp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
+ lpuart32_write(port, temp, UARTBAUD);
+
+ /* disable Rx/Tx and interrupts and break condition */
temp = lpuart32_read(port, UARTCTRL);
- temp &= ~(UARTCTRL_TE | UARTCTRL_RE |
- UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
+ temp &= ~(UARTCTRL_TE | UARTCTRL_RE | UARTCTRL_ILIE |
+ UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE | UARTCTRL_SBK);
lpuart32_write(port, temp, UARTCTRL);
spin_unlock_irqrestore(&port->lock, flags);
@@ -2631,7 +2705,7 @@ static int lpuart_global_reset(struct lpuart_port *sport)
return ret;
}
- if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
+ if (is_imx7ulp_lpuart(sport) || is_imx8ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
/*
* If the transmitter is used by earlycon, wait for transmit engine to
* complete and then reset.
@@ -2688,6 +2762,7 @@ static int lpuart_probe(struct platform_device *pdev)
sport->port.dev = &pdev->dev;
sport->port.type = PORT_LPUART;
sport->devtype = sdata->devtype;
+ sport->rx_watermark = sdata->rx_watermark;
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 757825edb0cd..523f296d5747 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -210,12 +210,8 @@ struct imx_port {
struct mctrl_gpios *gpios;
- /* shadow registers */
- unsigned int ucr1;
- unsigned int ucr2;
- unsigned int ucr3;
- unsigned int ucr4;
- unsigned int ufcr;
+ /* counter to stop 0xff flood */
+ int idle_counter;
/* DMA fields */
unsigned int dma_is_enabled:1;
@@ -273,59 +269,14 @@ static const struct of_device_id imx_uart_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
-static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
-{
- switch (offset) {
- case UCR1:
- sport->ucr1 = val;
- break;
- case UCR2:
- sport->ucr2 = val;
- break;
- case UCR3:
- sport->ucr3 = val;
- break;
- case UCR4:
- sport->ucr4 = val;
- break;
- case UFCR:
- sport->ufcr = val;
- break;
- default:
- break;
- }
+static inline void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
+{
writel(val, sport->port.membase + offset);
}
-static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
+static inline u32 imx_uart_readl(struct imx_port *sport, u32 offset)
{
- switch (offset) {
- case UCR1:
- return sport->ucr1;
- break;
- case UCR2:
- /*
- * UCR2_SRST is the only bit in the cached registers that might
- * differ from the value that was last written. As it only
- * automatically becomes one after being cleared, reread
- * conditionally.
- */
- if (!(sport->ucr2 & UCR2_SRST))
- sport->ucr2 = readl(sport->port.membase + offset);
- return sport->ucr2;
- break;
- case UCR3:
- return sport->ucr3;
- break;
- case UCR4:
- return sport->ucr4;
- break;
- case UFCR:
- return sport->ufcr;
- break;
- default:
- return readl(sport->port.membase + offset);
- }
+ return readl(sport->port.membase + offset);
}
static inline unsigned imx_uart_uts_reg(struct imx_port *sport)
@@ -398,6 +349,41 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
}
/* called with port.lock taken and irqs off */
+static void imx_uart_soft_reset(struct imx_port *sport)
+{
+ int i = 10;
+ u32 ucr2, ubir, ubmr, uts;
+
+ /*
+ * According to the Reference Manual description of the UART SRST bit:
+ *
+ * "Reset the transmit and receive state machines,
+ * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
+ * and UTS[6-3]".
+ *
+ * We don't need to restore the old values from USR1, USR2, URXD and
+ * UTXD. UBRC is read only, so only save/restore the other three
+ * registers.
+ */
+ ubir = imx_uart_readl(sport, UBIR);
+ ubmr = imx_uart_readl(sport, UBMR);
+ uts = imx_uart_readl(sport, IMX21_UTS);
+
+ ucr2 = imx_uart_readl(sport, UCR2);
+ imx_uart_writel(sport, ucr2 & ~UCR2_SRST, UCR2);
+
+ while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
+ udelay(1);
+
+ /* Restore the registers */
+ imx_uart_writel(sport, ubir, UBIR);
+ imx_uart_writel(sport, ubmr, UBMR);
+ imx_uart_writel(sport, uts, IMX21_UTS);
+
+ sport->idle_counter = 0;
+}
+
+/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -476,7 +462,8 @@ static void imx_uart_stop_tx(struct uart_port *port)
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
- imx_uart_start_rx(port);
+ if (!port->rs485_rx_during_tx_gpio)
+ imx_uart_start_rx(port);
sport->tx_state = OFF;
}
@@ -705,7 +692,8 @@ static void imx_uart_start_tx(struct uart_port *port)
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
- if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
+ if (!(port->rs485.flags & SER_RS485_RX_DURING_TX) &&
+ !port->rs485_rx_during_tx_gpio)
imx_uart_stop_rx(port);
sport->tx_state = WAIT_AFTER_RTS;
@@ -771,7 +759,7 @@ static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)
imx_uart_writel(sport, USR1_RTSD, USR1);
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
- uart_handle_cts_change(&sport->port, !!usr1);
+ uart_handle_cts_change(&sport->port, usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
return IRQ_HANDLED;
@@ -801,33 +789,73 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/* Check if hardware Rx flood is in progress, and issue soft reset to stop it.
+ * This is to be called from Rx ISRs only when some bytes were actually
+ * received.
+ *
+ * A way to reproduce the flood (checked on iMX6SX) is: open iMX UART at 9600
+ * 8N1, and from external source send 0xf0 char at 115200 8N1. In about 90% of
+ * cases this starts a flood of "receiving" of 0xff characters by the iMX6 UART
+ * that is terminated by any activity on RxD line, or could be stopped by
+ * issuing soft reset to the UART (just stop/start of RX does not help). Note
+ * that what we do here is sending isolated start bit about 2.4 times shorter
+ * than it is to be on UART configured baud rate.
+ */
+static void imx_uart_check_flood(struct imx_port *sport, u32 usr2)
+{
+ /* To detect hardware 0xff flood we monitor RxD line between RX
+ * interrupts to isolate "receiving" of char(s) with no activity
+ * on RxD line, that'd never happen on actual data transfers.
+ *
+ * We use USR2_WAKE bit to check for activity on RxD line, but we have a
+ * race here if we clear USR2_WAKE when receiving of a char is in
+ * progress, so we might get RX interrupt later with USR2_WAKE bit
+ * cleared. Note though that as we don't try to clear USR2_WAKE when we
+ * detected no activity, this race may hide actual activity only once.
+ *
+ * Yet another case where receive interrupt may occur without RxD
+ * activity is expiration of aging timer, so we consider this as well.
+ *
+ * We use 'idle_counter' to ensure that we got at least so many RX
+ * interrupts without any detected activity on RxD line. 2 cases
+ * described plus 1 to be on the safe side gives us a margin of 3,
+ * below. In practice I was not able to produce a false positive to
+ * induce soft reset at regular data transfers even using 1 as the
+ * margin, so 3 is actually very strong.
+ *
+ * We count interrupts, not chars in 'idle-counter' for simplicity.
+ */
+
+ if (usr2 & USR2_WAKE) {
+ imx_uart_writel(sport, USR2_WAKE, USR2);
+ sport->idle_counter = 0;
+ } else if (++sport->idle_counter > 3) {
+ dev_warn(sport->port.dev, "RX flood detected: soft reset.");
+ imx_uart_soft_reset(sport); /* also clears 'sport->idle_counter' */
+ }
+}
+
static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
+ u32 usr2, rx;
- while (imx_uart_readl(sport, USR2) & USR2_RDR) {
- u32 usr2;
+ /* If we received something, check for 0xff flood */
+ usr2 = imx_uart_readl(sport, USR2);
+ if (usr2 & USR2_RDR)
+ imx_uart_check_flood(sport, usr2);
- flg = TTY_NORMAL;
+ while ((rx = imx_uart_readl(sport, URXD0)) & URXD_CHARRDY) {
+ unsigned int flg = TTY_NORMAL;
sport->port.icount.rx++;
- rx = imx_uart_readl(sport, URXD0);
-
- usr2 = imx_uart_readl(sport, USR2);
- if (usr2 & USR2_BRCD) {
- imx_uart_writel(sport, USR2_BRCD, USR2);
- if (uart_handle_break(&sport->port))
- continue;
- }
-
- if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
- continue;
-
if (unlikely(rx & URXD_ERR)) {
- if (rx & URXD_BRK)
+ if (rx & URXD_BRK) {
sport->port.icount.brk++;
+ if (uart_handle_break(&sport->port))
+ continue;
+ }
else if (rx & URXD_PRERR)
sport->port.icount.parity++;
else if (rx & URXD_FRMERR)
@@ -835,11 +863,8 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
if (rx & URXD_OVRRUN)
sport->port.icount.overrun++;
- if (rx & sport->port.ignore_status_mask) {
- if (++ignored > 100)
- goto out;
+ if (rx & sport->port.ignore_status_mask)
continue;
- }
rx &= (sport->port.read_status_mask | 0xFF);
@@ -853,16 +878,17 @@ static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
flg = TTY_OVERRUN;
sport->port.sysrq = 0;
+ } else if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) {
+ continue;
}
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
- goto out;
+ continue;
if (tty_insert_flip_char(port, rx, flg) == 0)
sport->port.icount.buf_overrun++;
}
-out:
tty_flip_buffer_push(port);
return IRQ_HANDLED;
@@ -1147,55 +1173,62 @@ static void imx_uart_dma_rx_callback(void *data)
status = dmaengine_tx_status(chan, sport->rx_cookie, &state);
if (status == DMA_ERROR) {
+ spin_lock(&sport->port.lock);
imx_uart_clear_rx_errors(sport);
+ spin_unlock(&sport->port.lock);
return;
}
- if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
+ /*
+ * The state-residue variable represents the empty space
+ * relative to the entire buffer. Taking this in consideration
+ * the head is always calculated base on the buffer total
+ * length - DMA transaction residue. The UART script from the
+ * SDMA firmware will jump to the next buffer descriptor,
+ * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
+ * Taking this in consideration the tail is always at the
+ * beginning of the buffer descriptor that contains the head.
+ */
- /*
- * The state-residue variable represents the empty space
- * relative to the entire buffer. Taking this in consideration
- * the head is always calculated base on the buffer total
- * length - DMA transaction residue. The UART script from the
- * SDMA firmware will jump to the next buffer descriptor,
- * once a DMA transaction if finalized (IMX53 RM - A.4.1.2.4).
- * Taking this in consideration the tail is always at the
- * beginning of the buffer descriptor that contains the head.
- */
+ /* Calculate the head */
+ rx_ring->head = sg_dma_len(sgl) - state.residue;
+
+ /* Calculate the tail. */
+ bd_size = sg_dma_len(sgl) / sport->rx_periods;
+ rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
- /* Calculate the head */
- rx_ring->head = sg_dma_len(sgl) - state.residue;
+ if (rx_ring->head <= sg_dma_len(sgl) &&
+ rx_ring->head > rx_ring->tail) {
- /* Calculate the tail. */
- bd_size = sg_dma_len(sgl) / sport->rx_periods;
- rx_ring->tail = ((rx_ring->head-1) / bd_size) * bd_size;
+ /* Move data from tail to head */
+ r_bytes = rx_ring->head - rx_ring->tail;
- if (rx_ring->head <= sg_dma_len(sgl) &&
- rx_ring->head > rx_ring->tail) {
+ /* If we received something, check for 0xff flood */
+ spin_lock(&sport->port.lock);
+ imx_uart_check_flood(sport, imx_uart_readl(sport, USR2));
+ spin_unlock(&sport->port.lock);
- /* Move data from tail to head */
- r_bytes = rx_ring->head - rx_ring->tail;
+ if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ)) {
/* CPU claims ownership of RX DMA buffer */
dma_sync_sg_for_cpu(sport->port.dev, sgl, 1,
- DMA_FROM_DEVICE);
+ DMA_FROM_DEVICE);
w_bytes = tty_insert_flip_string(port,
- sport->rx_buf + rx_ring->tail, r_bytes);
+ sport->rx_buf + rx_ring->tail, r_bytes);
/* UART retrieves ownership of RX DMA buffer */
dma_sync_sg_for_device(sport->port.dev, sgl, 1,
- DMA_FROM_DEVICE);
+ DMA_FROM_DEVICE);
if (w_bytes != r_bytes)
sport->port.icount.buf_overrun++;
sport->port.icount.rx += w_bytes;
- } else {
- WARN_ON(rx_ring->head > sg_dma_len(sgl));
- WARN_ON(rx_ring->head <= rx_ring->tail);
}
+ } else {
+ WARN_ON(rx_ring->head > sg_dma_len(sgl));
+ WARN_ON(rx_ring->head <= rx_ring->tail);
}
if (w_bytes) {
@@ -1271,6 +1304,8 @@ static void imx_uart_clear_rx_errors(struct imx_port *sport)
imx_uart_writel(sport, USR2_ORE, USR2);
}
+ sport->idle_counter = 0;
+
}
#define TXTL_DEFAULT 2 /* reset default */
@@ -1398,7 +1433,7 @@ static void imx_uart_disable_dma(struct imx_port *sport)
static int imx_uart_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
- int retval, i;
+ int retval;
unsigned long flags;
int dma_is_inited = 0;
u32 ucr1, ucr2, ucr3, ucr4, uts;
@@ -1430,15 +1465,9 @@ static int imx_uart_startup(struct uart_port *port)
dma_is_inited = 1;
spin_lock_irqsave(&sport->port.lock, flags);
- /* Reset fifo's and state machines */
- i = 100;
- ucr2 = imx_uart_readl(sport, UCR2);
- ucr2 &= ~UCR2_SRST;
- imx_uart_writel(sport, ucr2, UCR2);
-
- while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
- udelay(1);
+ /* Reset fifo's and state machines */
+ imx_uart_soft_reset(sport);
/*
* Finally, clear and enable interrupts
@@ -1564,7 +1593,8 @@ 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_RXDMAEN | UCR1_ATDMAEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_RXDMAEN |
+ UCR1_ATDMAEN | UCR1_SNDBRK);
/* 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 &&
@@ -1593,8 +1623,6 @@ static void imx_uart_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
struct scatterlist *sgl = &sport->tx_sgl[0];
- u32 ucr2;
- int i = 100, ubir, ubmr, uts;
if (!sport->dma_chan_tx)
return;
@@ -1612,32 +1640,8 @@ static void imx_uart_flush_buffer(struct uart_port *port)
sport->dma_is_txing = 0;
}
- /*
- * According to the Reference Manual description of the UART SRST bit:
- *
- * "Reset the transmit and receive state machines,
- * all FIFOs and register USR1, USR2, UBIR, UBMR, UBRC, URXD, UTXD
- * and UTS[6-3]".
- *
- * We don't need to restore the old values from USR1, USR2, URXD and
- * UTXD. UBRC is read only, so only save/restore the other three
- * registers.
- */
- ubir = imx_uart_readl(sport, UBIR);
- ubmr = imx_uart_readl(sport, UBMR);
- uts = imx_uart_readl(sport, IMX21_UTS);
-
- ucr2 = imx_uart_readl(sport, UCR2);
- ucr2 &= ~UCR2_SRST;
- imx_uart_writel(sport, ucr2, UCR2);
-
- while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
- udelay(1);
+ imx_uart_soft_reset(sport);
- /* Restore the registers */
- imx_uart_writel(sport, ubir, UBIR);
- imx_uart_writel(sport, ubmr, UBMR);
- imx_uart_writel(sport, uts, IMX21_UTS);
}
static void
@@ -1955,6 +1959,10 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio
rs485conf->flags & SER_RS485_RX_DURING_TX)
imx_uart_start_rx(port);
+ if (port->rs485_rx_during_tx_gpio)
+ gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
+ !!(rs485conf->flags & SER_RS485_RX_DURING_TX));
+
return 0;
}
@@ -2340,13 +2348,6 @@ static int imx_uart_probe(struct platform_device *pdev)
return ret;
}
- /* initialize shadow register values */
- sport->ucr1 = readl(sport->port.membase + UCR1);
- sport->ucr2 = readl(sport->port.membase + UCR2);
- sport->ucr3 = readl(sport->port.membase + UCR3);
- sport->ucr4 = readl(sport->port.membase + UCR4);
- sport->ufcr = readl(sport->port.membase + UFCR);
-
ret = uart_get_rs485_mode(&sport->port);
if (ret) {
clk_disable_unprepare(sport->clk_ipg);
@@ -2374,6 +2375,11 @@ 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);
+ /* Disable Ageing Timer interrupt */
+ ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 &= ~UCR2_ATEN;
+ imx_uart_writel(sport, ucr2, UCR2);
+
/*
* In case RS485 is enabled without GPIO RTS control, the UART IP
* is used to control CTS signal. Keep both the UART and Receiver
diff --git a/drivers/tty/serial/liteuart.c b/drivers/tty/serial/liteuart.c
index 062812fe1b09..80de3a42b67b 100644
--- a/drivers/tty/serial/liteuart.c
+++ b/drivers/tty/serial/liteuart.c
@@ -5,7 +5,9 @@
* Copyright (C) 2019-2020 Antmicro <www.antmicro.com>
*/
+#include <linux/bits.h>
#include <linux/console.h>
+#include <linux/interrupt.h>
#include <linux/litex.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -38,13 +40,13 @@
#define OFF_EV_ENABLE 0x14
/* events */
-#define EV_TX 0x1
-#define EV_RX 0x2
+#define EV_TX BIT(0)
+#define EV_RX BIT(1)
struct liteuart_port {
struct uart_port port;
struct timer_list timer;
- u32 id;
+ u8 irq_reg;
};
#define to_liteuart_port(port) container_of(port, struct liteuart_port, port)
@@ -57,7 +59,7 @@ static struct console liteuart_console;
static struct uart_driver liteuart_driver = {
.owner = THIS_MODULE,
- .driver_name = "liteuart",
+ .driver_name = KBUILD_MODNAME,
.dev_name = "ttyLXU",
.major = 0,
.minor = 0,
@@ -67,38 +69,95 @@ static struct uart_driver liteuart_driver = {
#endif
};
-static void liteuart_timer(struct timer_list *t)
+static void liteuart_update_irq_reg(struct uart_port *port, bool set, u8 mask)
+{
+ struct liteuart_port *uart = to_liteuart_port(port);
+
+ if (set)
+ uart->irq_reg |= mask;
+ else
+ uart->irq_reg &= ~mask;
+
+ if (port->irq)
+ litex_write8(port->membase + OFF_EV_ENABLE, uart->irq_reg);
+}
+
+static void liteuart_stop_tx(struct uart_port *port)
+{
+ liteuart_update_irq_reg(port, false, EV_TX);
+}
+
+static void liteuart_start_tx(struct uart_port *port)
+{
+ liteuart_update_irq_reg(port, true, EV_TX);
+}
+
+static void liteuart_stop_rx(struct uart_port *port)
+{
+ struct liteuart_port *uart = to_liteuart_port(port);
+
+ /* just delete timer */
+ del_timer(&uart->timer);
+}
+
+static void liteuart_rx_chars(struct uart_port *port)
{
- struct liteuart_port *uart = from_timer(uart, t, timer);
- struct uart_port *port = &uart->port;
unsigned char __iomem *membase = port->membase;
- unsigned int flg = TTY_NORMAL;
- int ch;
- unsigned long status;
+ u8 ch;
- while ((status = !litex_read8(membase + OFF_RXEMPTY)) == 1) {
+ while (!litex_read8(membase + OFF_RXEMPTY)) {
ch = litex_read8(membase + OFF_RXTX);
port->icount.rx++;
/* necessary for RXEMPTY to refresh its value */
- litex_write8(membase + OFF_EV_PENDING, EV_TX | EV_RX);
+ litex_write8(membase + OFF_EV_PENDING, EV_RX);
/* no overflow bits in status */
if (!(uart_handle_sysrq_char(port, ch)))
- uart_insert_char(port, status, 0, ch, flg);
-
- tty_flip_buffer_push(&port->state->port);
+ uart_insert_char(port, 1, 0, ch, TTY_NORMAL);
}
- mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
+ tty_flip_buffer_push(&port->state->port);
}
-static void liteuart_putchar(struct uart_port *port, unsigned char ch)
+static void liteuart_tx_chars(struct uart_port *port)
{
- while (litex_read8(port->membase + OFF_TXFULL))
- cpu_relax();
+ u8 ch;
- litex_write8(port->membase + OFF_RXTX, ch);
+ uart_port_tx(port, ch,
+ !litex_read8(port->membase + OFF_TXFULL),
+ litex_write8(port->membase + OFF_RXTX, ch));
+}
+
+static irqreturn_t liteuart_interrupt(int irq, void *data)
+{
+ struct liteuart_port *uart = data;
+ struct uart_port *port = &uart->port;
+ unsigned long flags;
+ u8 isr;
+
+ /*
+ * if polling, the context would be "in_serving_softirq", so use
+ * irq[save|restore] spin_lock variants to cover all possibilities
+ */
+ spin_lock_irqsave(&port->lock, flags);
+ isr = litex_read8(port->membase + OFF_EV_PENDING) & uart->irq_reg;
+ if (isr & EV_RX)
+ liteuart_rx_chars(port);
+ if (isr & EV_TX)
+ liteuart_tx_chars(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_RETVAL(isr);
+}
+
+static void liteuart_timer(struct timer_list *t)
+{
+ struct liteuart_port *uart = from_timer(uart, t, timer);
+ struct uart_port *port = &uart->port;
+
+ liteuart_interrupt(0, port);
+ mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
}
static unsigned int liteuart_tx_empty(struct uart_port *port)
@@ -120,60 +179,49 @@ static unsigned int liteuart_get_mctrl(struct uart_port *port)
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
-static void liteuart_stop_tx(struct uart_port *port)
-{
-}
-
-static void liteuart_start_tx(struct uart_port *port)
+static int liteuart_startup(struct uart_port *port)
{
- struct circ_buf *xmit = &port->state->xmit;
- unsigned char ch;
-
- if (unlikely(port->x_char)) {
- litex_write8(port->membase + OFF_RXTX, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- } else if (!uart_circ_empty(xmit)) {
- while (xmit->head != xmit->tail) {
- ch = xmit->buf[xmit->tail];
- uart_xmit_advance(port, 1);
- liteuart_putchar(port, ch);
+ struct liteuart_port *uart = to_liteuart_port(port);
+ unsigned long flags;
+ int ret;
+
+ if (port->irq) {
+ ret = request_irq(port->irq, liteuart_interrupt, 0,
+ KBUILD_MODNAME, uart);
+ if (ret) {
+ dev_warn(port->dev,
+ "line %d irq %d failed: switch to polling\n",
+ port->line, port->irq);
+ port->irq = 0;
}
}
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-}
-
-static void liteuart_stop_rx(struct uart_port *port)
-{
- struct liteuart_port *uart = to_liteuart_port(port);
+ spin_lock_irqsave(&port->lock, flags);
+ /* only enabling rx irqs during startup */
+ liteuart_update_irq_reg(port, true, EV_RX);
+ spin_unlock_irqrestore(&port->lock, flags);
- /* just delete timer */
- del_timer(&uart->timer);
-}
+ if (!port->irq) {
+ timer_setup(&uart->timer, liteuart_timer, 0);
+ mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
+ }
-static void liteuart_break_ctl(struct uart_port *port, int break_state)
-{
- /* LiteUART doesn't support sending break signal */
+ return 0;
}
-static int liteuart_startup(struct uart_port *port)
+static void liteuart_shutdown(struct uart_port *port)
{
struct liteuart_port *uart = to_liteuart_port(port);
+ unsigned long flags;
- /* disable events */
- litex_write8(port->membase + OFF_EV_ENABLE, 0);
-
- /* prepare timer for polling */
- timer_setup(&uart->timer, liteuart_timer, 0);
- mod_timer(&uart->timer, jiffies + uart_poll_timeout(port));
-
- return 0;
-}
+ spin_lock_irqsave(&port->lock, flags);
+ liteuart_update_irq_reg(port, false, EV_RX | EV_TX);
+ spin_unlock_irqrestore(&port->lock, flags);
-static void liteuart_shutdown(struct uart_port *port)
-{
+ if (port->irq)
+ free_irq(port->irq, port);
+ else
+ del_timer_sync(&uart->timer);
}
static void liteuart_set_termios(struct uart_port *port, struct ktermios *new,
@@ -196,15 +244,6 @@ static const char *liteuart_type(struct uart_port *port)
return "liteuart";
}
-static void liteuart_release_port(struct uart_port *port)
-{
-}
-
-static int liteuart_request_port(struct uart_port *port)
-{
- return 0;
-}
-
static void liteuart_config_port(struct uart_port *port, int flags)
{
/*
@@ -231,13 +270,10 @@ static const struct uart_ops liteuart_ops = {
.stop_tx = liteuart_stop_tx,
.start_tx = liteuart_start_tx,
.stop_rx = liteuart_stop_rx,
- .break_ctl = liteuart_break_ctl,
.startup = liteuart_startup,
.shutdown = liteuart_shutdown,
.set_termios = liteuart_set_termios,
.type = liteuart_type,
- .release_port = liteuart_release_port,
- .request_port = liteuart_request_port,
.config_port = liteuart_config_port,
.verify_port = liteuart_verify_port,
};
@@ -249,6 +285,23 @@ static int liteuart_probe(struct platform_device *pdev)
struct xa_limit limit;
int dev_id, ret;
+ uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL);
+ if (!uart)
+ return -ENOMEM;
+
+ port = &uart->port;
+
+ /* get membase */
+ port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(port->membase))
+ return PTR_ERR(port->membase);
+
+ ret = platform_get_irq_optional(pdev, 0);
+ if (ret < 0 && ret != -ENXIO)
+ return ret;
+ if (ret > 0)
+ port->irq = ret;
+
/* look for aliases; auto-enumerate for free index if not found */
dev_id = of_alias_get_id(pdev->dev.of_node, "serial");
if (dev_id < 0)
@@ -256,32 +309,16 @@ static int liteuart_probe(struct platform_device *pdev)
else
limit = XA_LIMIT(dev_id, dev_id);
- uart = devm_kzalloc(&pdev->dev, sizeof(struct liteuart_port), GFP_KERNEL);
- if (!uart)
- return -ENOMEM;
-
ret = xa_alloc(&liteuart_array, &dev_id, uart, limit, GFP_KERNEL);
if (ret)
return ret;
- uart->id = dev_id;
- port = &uart->port;
-
- /* get membase */
- port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(port->membase)) {
- ret = PTR_ERR(port->membase);
- goto err_erase_id;
- }
-
/* values not from device tree */
port->dev = &pdev->dev;
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &liteuart_ops;
- port->regshift = 2;
port->fifosize = 16;
- port->iobase = 1;
port->type = PORT_UNKNOWN;
port->line = dev_id;
spin_lock_init(&port->lock);
@@ -295,7 +332,7 @@ static int liteuart_probe(struct platform_device *pdev)
return 0;
err_erase_id:
- xa_erase(&liteuart_array, uart->id);
+ xa_erase(&liteuart_array, dev_id);
return ret;
}
@@ -303,10 +340,10 @@ err_erase_id:
static int liteuart_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
- struct liteuart_port *uart = to_liteuart_port(port);
+ unsigned int line = port->line;
uart_remove_one_port(&liteuart_driver, port);
- xa_erase(&liteuart_array, uart->id);
+ xa_erase(&liteuart_array, line);
return 0;
}
@@ -321,13 +358,21 @@ static struct platform_driver liteuart_platform_driver = {
.probe = liteuart_probe,
.remove = liteuart_remove,
.driver = {
- .name = "liteuart",
+ .name = KBUILD_MODNAME,
.of_match_table = liteuart_of_match,
},
};
#ifdef CONFIG_SERIAL_LITEUART_CONSOLE
+static void liteuart_putchar(struct uart_port *port, unsigned char ch)
+{
+ while (litex_read8(port->membase + OFF_TXFULL))
+ cpu_relax();
+
+ litex_write8(port->membase + OFF_RXTX, ch);
+}
+
static void liteuart_console_write(struct console *co, const char *s,
unsigned int count)
{
@@ -367,7 +412,7 @@ static int liteuart_console_setup(struct console *co, char *options)
}
static struct console liteuart_console = {
- .name = "liteuart",
+ .name = KBUILD_MODNAME,
.write = liteuart_console_write,
.device = uart_console_device,
.setup = liteuart_console_setup,
@@ -415,12 +460,10 @@ static int __init liteuart_init(void)
return res;
res = platform_driver_register(&liteuart_platform_driver);
- if (res) {
+ if (res)
uart_unregister_driver(&liteuart_driver);
- return res;
- }
- return 0;
+ return res;
}
static void __exit liteuart_exit(void)
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index bb74f23251fe..86dcbff8faa3 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -247,7 +247,7 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx)
cts = (rx & MAX3100_CTS) > 0;
if (s->cts != cts) {
s->cts = cts;
- uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0);
+ uart_handle_cts_change(&s->port, cts);
}
return ret;
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 4eb24e3407f8..e9cacfe7e032 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -819,8 +819,7 @@ static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno)
if (ists & MAX310X_IRQ_CTS_BIT) {
lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
- uart_handle_cts_change(port,
- !!(lsr & MAX310X_LSR_CTS_BIT));
+ uart_handle_cts_change(port, lsr & MAX310X_LSR_CTS_BIT);
}
if (rxlen)
max310x_handle_rx(port, rxlen);
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 843798e63084..90953e679e38 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1120,6 +1120,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud,
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud,
unsigned long *saved_flags)
+ __must_hold(&port->lock)
{
unsigned int rxstale, watermark, mask;
struct msm_port *msm_port = to_msm_port(port);
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 9576ba8bbc40..cc83b772b7ca 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -1775,7 +1775,7 @@ static void pch_uart_exit_port(struct eg20t_port *priv)
char name[32];
snprintf(name, sizeof(name), "uart%d_regs", priv->port.line);
- debugfs_remove(debugfs_lookup(name, NULL));
+ debugfs_lookup_and_remove(name, NULL);
uart_remove_one_port(&pch_uart_driver, &priv->port);
free_page((unsigned long)priv->rxbuf.buf);
}
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
index ba3435263c1f..196a4e678451 100644
--- a/drivers/tty/serial/pic32_uart.c
+++ b/drivers/tty/serial/pic32_uart.c
@@ -889,6 +889,8 @@ static int pic32_uart_probe(struct platform_device *pdev)
sport->irq_rx = irq_of_parse_and_map(np, 1);
sport->irq_tx = irq_of_parse_and_map(np, 2);
sport->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sport->clk))
+ return PTR_ERR(sport->clk);
sport->dev = &pdev->dev;
/* Hardware flow control: gpios
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 57f04f8bf504..d69592e5e2ec 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -16,7 +16,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
-#include <linux/qcom-geni-se.h>
+#include <linux/soc/qcom/geni-se.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
@@ -39,76 +39,70 @@
#define SE_UART_MANUAL_RFR 0x2ac
/* SE_UART_TRANS_CFG */
-#define UART_TX_PAR_EN BIT(0)
-#define UART_CTS_MASK BIT(1)
-
-/* SE_UART_TX_WORD_LEN */
-#define TX_WORD_LEN_MSK GENMASK(9, 0)
+#define UART_TX_PAR_EN BIT(0)
+#define UART_CTS_MASK BIT(1)
/* SE_UART_TX_STOP_BIT_LEN */
-#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0)
-#define TX_STOP_BIT_LEN_1 0
-#define TX_STOP_BIT_LEN_1_5 1
-#define TX_STOP_BIT_LEN_2 2
-
-/* SE_UART_TX_TRANS_LEN */
-#define TX_TRANS_LEN_MSK GENMASK(23, 0)
+#define TX_STOP_BIT_LEN_1 0
+#define TX_STOP_BIT_LEN_2 2
/* SE_UART_RX_TRANS_CFG */
-#define UART_RX_INS_STATUS_BIT BIT(2)
-#define UART_RX_PAR_EN BIT(3)
+#define UART_RX_PAR_EN BIT(3)
/* SE_UART_RX_WORD_LEN */
-#define RX_WORD_LEN_MASK GENMASK(9, 0)
+#define RX_WORD_LEN_MASK GENMASK(9, 0)
/* SE_UART_RX_STALE_CNT */
-#define RX_STALE_CNT GENMASK(23, 0)
+#define RX_STALE_CNT GENMASK(23, 0)
/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
-#define PAR_CALC_EN BIT(0)
-#define PAR_MODE_MSK GENMASK(2, 1)
-#define PAR_MODE_SHFT 1
-#define PAR_EVEN 0x00
-#define PAR_ODD 0x01
-#define PAR_SPACE 0x10
-#define PAR_MARK 0x11
+#define PAR_CALC_EN BIT(0)
+#define PAR_EVEN 0x00
+#define PAR_ODD 0x01
+#define PAR_SPACE 0x10
/* SE_UART_MANUAL_RFR register fields */
-#define UART_MANUAL_RFR_EN BIT(31)
-#define UART_RFR_NOT_READY BIT(1)
-#define UART_RFR_READY BIT(0)
+#define UART_MANUAL_RFR_EN BIT(31)
+#define UART_RFR_NOT_READY BIT(1)
+#define UART_RFR_READY BIT(0)
/* UART M_CMD OP codes */
-#define UART_START_TX 0x1
-#define UART_START_BREAK 0x4
-#define UART_STOP_BREAK 0x5
+#define UART_START_TX 0x1
/* UART S_CMD OP codes */
-#define UART_START_READ 0x1
-#define UART_PARAM 0x1
-
-#define UART_OVERSAMPLING 32
-#define STALE_TIMEOUT 16
-#define DEFAULT_BITS_PER_CHAR 10
-#define GENI_UART_CONS_PORTS 1
-#define GENI_UART_PORTS 3
-#define DEF_FIFO_DEPTH_WORDS 16
-#define DEF_TX_WM 2
-#define DEF_FIFO_WIDTH_BITS 32
-#define UART_RX_WM 2
+#define UART_START_READ 0x1
+#define UART_PARAM 0x1
+#define UART_PARAM_RFR_OPEN BIT(7)
+
+#define UART_OVERSAMPLING 32
+#define STALE_TIMEOUT 16
+#define DEFAULT_BITS_PER_CHAR 10
+#define GENI_UART_CONS_PORTS 1
+#define GENI_UART_PORTS 3
+#define DEF_FIFO_DEPTH_WORDS 16
+#define DEF_TX_WM 2
+#define DEF_FIFO_WIDTH_BITS 32
+#define UART_RX_WM 2
/* SE_UART_LOOPBACK_CFG */
-#define RX_TX_SORTED BIT(0)
-#define CTS_RTS_SORTED BIT(1)
-#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED)
+#define RX_TX_SORTED BIT(0)
+#define CTS_RTS_SORTED BIT(1)
+#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED)
/* UART pin swap value */
-#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0)
+#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0)
#define IO_MACRO_IO0_SEL 0x3
-#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4)
+#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4)
#define IO_MACRO_IO2_IO3_SWAP 0x4640
/* We always configure 4 bytes per FIFO word */
-#define BYTES_PER_FIFO_WORD 4
+#define BYTES_PER_FIFO_WORD 4U
+
+#define DMA_RX_BUF_SIZE 2048
+
+struct qcom_geni_device_data {
+ bool console;
+ enum geni_se_xfer_mode mode;
+};
struct qcom_geni_private_data {
/* NOTE: earlycon port will have NULL here */
@@ -128,10 +122,11 @@ struct qcom_geni_serial_port {
u32 tx_fifo_depth;
u32 tx_fifo_width;
u32 rx_fifo_depth;
+ dma_addr_t tx_dma_addr;
+ dma_addr_t rx_dma_addr;
bool setup;
- int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
unsigned int baud;
- void *rx_fifo;
+ void *rx_buf;
u32 loopback;
bool brk;
@@ -141,44 +136,42 @@ struct qcom_geni_serial_port {
bool cts_rts_swap;
struct qcom_geni_private_data private_data;
+ const struct qcom_geni_device_data *dev_data;
};
static const struct uart_ops qcom_geni_console_pops;
static const struct uart_ops qcom_geni_uart_pops;
static struct uart_driver qcom_geni_console_driver;
static struct uart_driver qcom_geni_uart_driver;
-static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
-static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop);
-static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
-static void qcom_geni_serial_stop_rx(struct uart_port *uport);
-static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop);
-#define to_dev_port(ptr, member) \
- container_of(ptr, struct qcom_geni_serial_port, member)
+static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
+{
+ return container_of(uport, struct qcom_geni_serial_port, uport);
+}
static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = {
[0] = {
.uport = {
- .iotype = UPIO_MEM,
- .ops = &qcom_geni_uart_pops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
+ .iotype = UPIO_MEM,
+ .ops = &qcom_geni_uart_pops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 0,
},
},
[1] = {
.uport = {
- .iotype = UPIO_MEM,
- .ops = &qcom_geni_uart_pops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
+ .iotype = UPIO_MEM,
+ .ops = &qcom_geni_uart_pops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 1,
},
},
[2] = {
.uport = {
- .iotype = UPIO_MEM,
- .ops = &qcom_geni_uart_pops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
+ .iotype = UPIO_MEM,
+ .ops = &qcom_geni_uart_pops,
+ .flags = UPF_BOOT_AUTOCONF,
+ .line = 2,
},
},
};
@@ -195,7 +188,7 @@ static struct qcom_geni_serial_port qcom_geni_console_port = {
static int qcom_geni_serial_request_port(struct uart_port *uport)
{
struct platform_device *pdev = to_platform_device(uport->dev);
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
uport->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(uport->membase))
@@ -232,7 +225,7 @@ static void qcom_geni_serial_set_mctrl(struct uart_port *uport,
unsigned int mctrl)
{
u32 uart_manual_rfr = 0;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
if (uart_console(uport))
return;
@@ -262,6 +255,16 @@ static struct qcom_geni_serial_port *get_port_from_line(int line, bool console)
return port;
}
+static bool qcom_geni_serial_main_active(struct uart_port *uport)
+{
+ return readl(uport->membase + SE_GENI_STATUS) & M_GENI_CMD_ACTIVE;
+}
+
+static bool qcom_geni_serial_secondary_active(struct uart_port *uport)
+{
+ return readl(uport->membase + SE_GENI_STATUS) & S_GENI_CMD_ACTIVE;
+}
+
static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
int offset, int field, bool set)
{
@@ -273,7 +276,7 @@ static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
struct qcom_geni_private_data *private_data = uport->private_data;
if (private_data->drv) {
- port = to_dev_port(uport, uport);
+ port = to_dev_port(uport);
baud = port->baud;
if (!baud)
baud = 115200;
@@ -338,7 +341,6 @@ static void qcom_geni_serial_abort_rx(struct uart_port *uport)
}
#ifdef CONFIG_CONSOLE_POLL
-
static int qcom_geni_serial_get_char(struct uart_port *uport)
{
struct qcom_geni_private_data *private_data = uport->private_data;
@@ -521,12 +523,12 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s,
spin_unlock_irqrestore(&uport->lock, flags);
}
-static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
{
u32 i;
unsigned char buf[sizeof(u32)];
struct tty_port *tport;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
tport = &uport->state->port;
for (i = 0; i < bytes; ) {
@@ -556,30 +558,21 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
}
if (!drop)
tty_flip_buffer_push(tport);
- return 0;
}
#else
-static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
{
- return -EPERM;
-}
+}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
-static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
+static void handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
{
- struct tty_port *tport;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE;
- u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw;
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ struct tty_port *tport = &uport->state->port;
int ret;
- tport = &uport->state->port;
- ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words);
- if (drop)
- return 0;
-
- ret = tty_insert_flip_string(tport, port->rx_fifo, bytes);
+ ret = tty_insert_flip_string(tport, port->rx_buf, bytes);
if (ret != bytes) {
dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n",
__func__, ret, bytes);
@@ -587,19 +580,82 @@ static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop)
}
uport->icount.rx += ret;
tty_flip_buffer_push(tport);
- return ret;
}
-static void qcom_geni_serial_start_tx(struct uart_port *uport)
+static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
{
- u32 irq_en;
- u32 status;
+ return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
+}
+
+static void qcom_geni_serial_stop_tx_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ bool done;
+ u32 m_irq_en;
- status = readl(uport->membase + SE_GENI_STATUS);
- if (status & M_GENI_CMD_ACTIVE)
+ if (!qcom_geni_serial_main_active(uport))
return;
- if (!qcom_geni_serial_tx_empty(uport))
+ if (port->rx_dma_addr) {
+ geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr,
+ port->tx_remaining);
+ port->tx_dma_addr = 0;
+ port->tx_remaining = 0;
+ }
+
+ m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ writel(m_irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ geni_se_cancel_m_cmd(&port->se);
+
+ done = qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
+ S_CMD_CANCEL_EN, true);
+ if (!done) {
+ geni_se_abort_m_cmd(&port->se);
+ done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ if (!done)
+ dev_err_ratelimited(uport->dev, "M_CMD_ABORT_EN not set");
+ writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+ }
+
+ writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_serial_start_tx_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ struct circ_buf *xmit = &uport->state->xmit;
+ unsigned int xmit_size;
+ int ret;
+
+ if (port->tx_dma_addr)
+ return;
+
+ xmit_size = uart_circ_chars_pending(xmit);
+ if (xmit_size < WAKEUP_CHARS)
+ uart_write_wakeup(uport);
+
+ xmit_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ qcom_geni_serial_setup_tx(uport, xmit_size);
+
+ ret = geni_se_tx_dma_prep(&port->se, &xmit->buf[xmit->tail],
+ xmit_size, &port->tx_dma_addr);
+ if (ret) {
+ dev_err(uport->dev, "unable to start TX SE DMA: %d\n", ret);
+ qcom_geni_serial_stop_tx_dma(uport);
+ return;
+ }
+
+ port->tx_remaining = xmit_size;
+}
+
+static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport)
+{
+ u32 irq_en;
+
+ if (qcom_geni_serial_main_active(uport) ||
+ !qcom_geni_serial_tx_empty(uport))
return;
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
@@ -609,19 +665,17 @@ static void qcom_geni_serial_start_tx(struct uart_port *uport)
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
-static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+static void qcom_geni_serial_stop_tx_fifo(struct uart_port *uport)
{
u32 irq_en;
- u32 status;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
irq_en &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN);
writel(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- status = readl(uport->membase + SE_GENI_STATUS);
/* Possible stop tx is called multiple times. */
- if (!(status & M_GENI_CMD_ACTIVE))
+ if (!qcom_geni_serial_main_active(uport))
return;
geni_se_cancel_m_cmd(&port->se);
@@ -635,32 +689,34 @@ static void qcom_geni_serial_stop_tx(struct uart_port *uport)
writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
-static void qcom_geni_serial_start_rx(struct uart_port *uport)
+static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
{
- u32 irq_en;
u32 status;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
-
- status = readl(uport->membase + SE_GENI_STATUS);
- if (status & S_GENI_CMD_ACTIVE)
- qcom_geni_serial_stop_rx(uport);
-
- geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
+ u32 word_cnt;
+ u32 last_word_byte_cnt;
+ u32 last_word_partial;
+ u32 total_bytes;
- irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN);
- irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
- writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+ status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ word_cnt = status & RX_FIFO_WC_MSK;
+ last_word_partial = status & RX_LAST;
+ last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
+ RX_LAST_BYTE_VALID_SHFT;
- irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
- writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+ if (!word_cnt)
+ return;
+ total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1);
+ if (last_word_partial && last_word_byte_cnt)
+ total_bytes += last_word_byte_cnt;
+ else
+ total_bytes += BYTES_PER_FIFO_WORD;
+ handle_rx_console(uport, total_bytes, drop);
}
-static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport)
{
u32 irq_en;
- u32 status;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
u32 s_irq_status;
irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN);
@@ -671,9 +727,7 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- status = readl(uport->membase + SE_GENI_STATUS);
- /* Possible stop rx is called multiple times. */
- if (!(status & S_GENI_CMD_ACTIVE))
+ if (!qcom_geni_serial_secondary_active(uport))
return;
geni_se_cancel_s_cmd(&port->se);
@@ -686,52 +740,154 @@ static void qcom_geni_serial_stop_rx(struct uart_port *uport)
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
/* Flush the Rx buffer */
if (s_irq_status & S_RX_FIFO_LAST_EN)
- qcom_geni_serial_handle_rx(uport, true);
+ qcom_geni_serial_handle_rx_fifo(uport, true);
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
- status = readl(uport->membase + SE_GENI_STATUS);
- if (status & S_GENI_CMD_ACTIVE)
+ if (qcom_geni_serial_secondary_active(uport))
qcom_geni_serial_abort_rx(uport);
}
-static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
+static void qcom_geni_serial_start_rx_fifo(struct uart_port *uport)
{
- u32 status;
- u32 word_cnt;
- u32 last_word_byte_cnt;
- u32 last_word_partial;
- u32 total_bytes;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ u32 irq_en;
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
- status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
- word_cnt = status & RX_FIFO_WC_MSK;
- last_word_partial = status & RX_LAST;
- last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
- RX_LAST_BYTE_VALID_SHFT;
+ if (qcom_geni_serial_secondary_active(uport))
+ qcom_geni_serial_stop_rx_fifo(uport);
- if (!word_cnt)
+ geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
+
+ irq_en = readl(uport->membase + SE_GENI_S_IRQ_EN);
+ irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
+ writel(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
+ writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
+}
+
+static void qcom_geni_serial_stop_rx_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+
+ if (!qcom_geni_serial_secondary_active(uport))
return;
- total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1);
- if (last_word_partial && last_word_byte_cnt)
- total_bytes += last_word_byte_cnt;
- else
- total_bytes += BYTES_PER_FIFO_WORD;
- port->handle_rx(uport, total_bytes, drop);
+
+ geni_se_cancel_s_cmd(&port->se);
+ qcom_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS,
+ S_CMD_CANCEL_EN, true);
+
+ if (qcom_geni_serial_secondary_active(uport))
+ qcom_geni_serial_abort_rx(uport);
+
+ if (port->rx_dma_addr) {
+ geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr,
+ DMA_RX_BUF_SIZE);
+ port->rx_dma_addr = 0;
+ }
+}
+
+static void qcom_geni_serial_start_rx_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ int ret;
+
+ if (qcom_geni_serial_secondary_active(uport))
+ qcom_geni_serial_stop_rx_dma(uport);
+
+ geni_se_setup_s_cmd(&port->se, UART_START_READ, UART_PARAM_RFR_OPEN);
+
+ ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
+ DMA_RX_BUF_SIZE,
+ &port->rx_dma_addr);
+ if (ret) {
+ dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret);
+ qcom_geni_serial_stop_rx_dma(uport);
+ }
+}
+
+static void qcom_geni_serial_handle_rx_dma(struct uart_port *uport, bool drop)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ u32 rx_in;
+ int ret;
+
+ if (!qcom_geni_serial_secondary_active(uport))
+ return;
+
+ if (!port->rx_dma_addr)
+ return;
+
+ geni_se_rx_dma_unprep(&port->se, port->rx_dma_addr, DMA_RX_BUF_SIZE);
+ port->rx_dma_addr = 0;
+
+ rx_in = readl(uport->membase + SE_DMA_RX_LEN_IN);
+ if (!rx_in) {
+ dev_warn(uport->dev, "serial engine reports 0 RX bytes in!\n");
+ return;
+ }
+
+ if (!drop)
+ handle_rx_uart(uport, rx_in, drop);
+
+ ret = geni_se_rx_dma_prep(&port->se, port->rx_buf,
+ DMA_RX_BUF_SIZE,
+ &port->rx_dma_addr);
+ if (ret) {
+ dev_err(uport->dev, "unable to start RX SE DMA: %d\n", ret);
+ qcom_geni_serial_stop_rx_dma(uport);
+ }
+}
+
+static void qcom_geni_serial_start_rx(struct uart_port *uport)
+{
+ uport->ops->start_rx(uport);
+}
+
+static void qcom_geni_serial_stop_rx(struct uart_port *uport)
+{
+ uport->ops->stop_rx(uport);
+}
+
+static void qcom_geni_serial_stop_tx(struct uart_port *uport)
+{
+ uport->ops->stop_tx(uport);
+}
+
+static void qcom_geni_serial_send_chunk_fifo(struct uart_port *uport,
+ unsigned int chunk)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ struct circ_buf *xmit = &uport->state->xmit;
+ unsigned int tx_bytes, c, remaining = chunk;
+ u8 buf[BYTES_PER_FIFO_WORD];
+
+ while (remaining) {
+ memset(buf, 0, sizeof(buf));
+ tx_bytes = min(remaining, BYTES_PER_FIFO_WORD);
+
+ for (c = 0; c < tx_bytes ; c++) {
+ buf[c] = xmit->buf[xmit->tail];
+ uart_xmit_advance(uport, 1);
+ }
+
+ iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);
+
+ remaining -= tx_bytes;
+ port->tx_remaining -= tx_bytes;
+ }
}
-static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
- bool active)
+static void qcom_geni_serial_handle_tx_fifo(struct uart_port *uport,
+ bool done, bool active)
{
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
struct circ_buf *xmit = &uport->state->xmit;
size_t avail;
- size_t remaining;
size_t pending;
- int i;
u32 status;
u32 irq_en;
unsigned int chunk;
- int tail;
status = readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
@@ -743,14 +899,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
/* All data has been transmitted and acknowledged as received */
if (!pending && !status && done) {
- qcom_geni_serial_stop_tx(uport);
+ qcom_geni_serial_stop_tx_fifo(uport);
goto out_write_wakeup;
}
avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
avail *= BYTES_PER_FIFO_WORD;
- tail = xmit->tail;
chunk = min(avail, pending);
if (!chunk)
goto out_write_wakeup;
@@ -765,29 +920,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
uport->membase + SE_GENI_M_IRQ_EN);
}
- remaining = chunk;
- for (i = 0; i < chunk; ) {
- unsigned int tx_bytes;
- u8 buf[sizeof(u32)];
- int c;
-
- memset(buf, 0, sizeof(buf));
- tx_bytes = min_t(size_t, remaining, BYTES_PER_FIFO_WORD);
-
- for (c = 0; c < tx_bytes ; c++) {
- buf[c] = xmit->buf[tail++];
- tail &= UART_XMIT_SIZE - 1;
- }
-
- iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);
-
- i += tx_bytes;
- uport->icount.tx += tx_bytes;
- remaining -= tx_bytes;
- port->tx_remaining -= tx_bytes;
- }
-
- xmit->tail = tail;
+ qcom_geni_serial_send_chunk_fifo(uport, chunk);
/*
* The tx fifo watermark is level triggered and latched. Though we had
@@ -809,16 +942,36 @@ out_write_wakeup:
uart_write_wakeup(uport);
}
+static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ struct circ_buf *xmit = &uport->state->xmit;
+
+ uart_xmit_advance(uport, port->tx_remaining);
+ geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining);
+ port->tx_dma_addr = 0;
+ port->tx_remaining = 0;
+
+ if (!uart_circ_empty(xmit))
+ qcom_geni_serial_start_tx_dma(uport);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(uport);
+}
+
static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
{
u32 m_irq_en;
u32 m_irq_status;
u32 s_irq_status;
u32 geni_status;
+ u32 dma;
+ u32 dma_tx_status;
+ u32 dma_rx_status;
struct uart_port *uport = dev;
bool drop_rx = false;
struct tty_port *tport = &uport->state->port;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
if (uport->suspended)
return IRQ_NONE;
@@ -827,10 +980,15 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
+ dma_tx_status = readl(uport->membase + SE_DMA_TX_IRQ_STAT);
+ dma_rx_status = readl(uport->membase + SE_DMA_RX_IRQ_STAT);
geni_status = readl(uport->membase + SE_GENI_STATUS);
+ dma = readl(uport->membase + SE_GENI_DMA_MODE_EN);
m_irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
writel(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ writel(dma_tx_status, uport->membase + SE_DMA_TX_IRQ_CLR);
+ writel(dma_rx_status, uport->membase + SE_DMA_RX_IRQ_CLR);
if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
goto out_unlock;
@@ -840,23 +998,44 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
}
- if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
- qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN,
- geni_status & M_GENI_CMD_ACTIVE);
-
- if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) {
+ if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) {
if (s_irq_status & S_GP_IRQ_0_EN)
uport->icount.parity++;
drop_rx = true;
- } else if (s_irq_status & S_GP_IRQ_2_EN ||
- s_irq_status & S_GP_IRQ_3_EN) {
+ } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) {
uport->icount.brk++;
port->brk = true;
}
- if (s_irq_status & S_RX_FIFO_WATERMARK_EN ||
- s_irq_status & S_RX_FIFO_LAST_EN)
- qcom_geni_serial_handle_rx(uport, drop_rx);
+ if (dma) {
+ if (dma_tx_status & TX_DMA_DONE)
+ qcom_geni_serial_handle_tx_dma(uport);
+
+ if (dma_rx_status) {
+ if (dma_rx_status & RX_RESET_DONE)
+ goto out_unlock;
+
+ if (dma_rx_status & RX_DMA_PARITY_ERR) {
+ uport->icount.parity++;
+ drop_rx = true;
+ }
+
+ if (dma_rx_status & RX_DMA_BREAK)
+ uport->icount.brk++;
+
+ if (dma_rx_status & (RX_DMA_DONE | RX_EOT))
+ qcom_geni_serial_handle_rx_dma(uport, drop_rx);
+ }
+ } else {
+ if (m_irq_status & m_irq_en &
+ (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
+ qcom_geni_serial_handle_tx_fifo(uport,
+ m_irq_status & M_CMD_DONE_EN,
+ geni_status & M_GENI_CMD_ACTIVE);
+
+ if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN))
+ qcom_geni_serial_handle_rx_fifo(uport, drop_rx);
+ }
out_unlock:
uart_unlock_and_check_sysrq(uport);
@@ -876,11 +1055,11 @@ static int setup_fifos(struct qcom_geni_serial_port *port)
uport->fifosize =
(port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
- if (port->rx_fifo && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) {
- port->rx_fifo = devm_krealloc(uport->dev, port->rx_fifo,
- port->rx_fifo_depth * sizeof(u32),
- GFP_KERNEL);
- if (!port->rx_fifo)
+ if (port->rx_buf && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) {
+ port->rx_buf = devm_krealloc(uport->dev, port->rx_buf,
+ port->rx_fifo_depth * sizeof(u32),
+ GFP_KERNEL);
+ if (!port->rx_buf)
return -ENOMEM;
}
@@ -891,11 +1070,13 @@ static int setup_fifos(struct qcom_geni_serial_port *port)
static void qcom_geni_serial_shutdown(struct uart_port *uport)
{
disable_irq(uport->irq);
+ qcom_geni_serial_stop_tx(uport);
+ qcom_geni_serial_stop_rx(uport);
}
static int qcom_geni_serial_port_setup(struct uart_port *uport)
{
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
u32 proto;
u32 pin_swap;
@@ -937,7 +1118,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
geni_se_config_packing(&port->se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD,
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);
+ geni_se_select_mode(&port->se, port->dev_data->mode);
qcom_geni_serial_start_rx(uport);
port->setup = true;
@@ -947,7 +1128,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
static int qcom_geni_serial_startup(struct uart_port *uport)
{
int ret;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
if (!port->setup) {
ret = qcom_geni_serial_port_setup(uport);
@@ -1033,7 +1214,7 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport,
u32 stop_bit_len;
unsigned int clk_div;
u32 ser_clk_cfg;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
unsigned long clk_rate;
u32 ver, sampling_rate;
unsigned int avg_bw_core;
@@ -1137,11 +1318,6 @@ out_restart_rx:
qcom_geni_serial_start_rx(uport);
}
-static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
-{
- return !readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
-}
-
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
static int qcom_geni_console_setup(struct console *co, char *options)
{
@@ -1323,7 +1499,7 @@ static struct uart_driver qcom_geni_uart_driver = {
static void qcom_geni_serial_pm(struct uart_port *uport,
unsigned int new_state, unsigned int old_state)
{
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
/* If we've never been called, treat it as off */
if (old_state == UART_PM_STATE_UNDEFINED)
@@ -1341,10 +1517,10 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
static const struct uart_ops qcom_geni_console_pops = {
.tx_empty = qcom_geni_serial_tx_empty,
- .stop_tx = qcom_geni_serial_stop_tx,
- .start_tx = qcom_geni_serial_start_tx,
- .stop_rx = qcom_geni_serial_stop_rx,
- .start_rx = qcom_geni_serial_start_rx,
+ .stop_tx = qcom_geni_serial_stop_tx_fifo,
+ .start_tx = qcom_geni_serial_start_tx_fifo,
+ .stop_rx = qcom_geni_serial_stop_rx_fifo,
+ .start_rx = qcom_geni_serial_start_rx_fifo,
.set_termios = qcom_geni_serial_set_termios,
.startup = qcom_geni_serial_startup,
.request_port = qcom_geni_serial_request_port,
@@ -1362,9 +1538,10 @@ static const struct uart_ops qcom_geni_console_pops = {
static const struct uart_ops qcom_geni_uart_pops = {
.tx_empty = qcom_geni_serial_tx_empty,
- .stop_tx = qcom_geni_serial_stop_tx,
- .start_tx = qcom_geni_serial_start_tx,
- .stop_rx = qcom_geni_serial_stop_rx,
+ .stop_tx = qcom_geni_serial_stop_tx_dma,
+ .start_tx = qcom_geni_serial_start_tx_dma,
+ .start_rx = qcom_geni_serial_start_rx_dma,
+ .stop_rx = qcom_geni_serial_stop_rx_dma,
.set_termios = qcom_geni_serial_set_termios,
.startup = qcom_geni_serial_startup,
.request_port = qcom_geni_serial_request_port,
@@ -1384,13 +1561,14 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
struct uart_port *uport;
struct resource *res;
int irq;
- bool console = false;
struct uart_driver *drv;
+ const struct qcom_geni_device_data *data;
- if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart"))
- console = true;
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data)
+ return -EINVAL;
- if (console) {
+ if (data->console) {
drv = &qcom_geni_console_driver;
line = of_alias_get_id(pdev->dev.of_node, "serial");
} else {
@@ -1400,7 +1578,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
line = of_alias_get_id(pdev->dev.of_node, "hsuart");
}
- port = get_port_from_line(line, console);
+ port = get_port_from_line(line, data->console);
if (IS_ERR(port)) {
dev_err(&pdev->dev, "Invalid line %d\n", line);
return PTR_ERR(port);
@@ -1412,6 +1590,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
return -ENODEV;
uport->dev = &pdev->dev;
+ port->dev_data = data;
port->se.dev = &pdev->dev;
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
port->se.clk = devm_clk_get(&pdev->dev, "se");
@@ -1430,10 +1609,10 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
- if (!console) {
- port->rx_fifo = devm_kcalloc(uport->dev,
- port->rx_fifo_depth, sizeof(u32), GFP_KERNEL);
- if (!port->rx_fifo)
+ if (!data->console) {
+ port->rx_buf = devm_kzalloc(uport->dev,
+ DMA_RX_BUF_SIZE, GFP_KERNEL);
+ if (!port->rx_buf)
return -ENOMEM;
}
@@ -1460,7 +1639,7 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
uport->irq = irq;
uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE);
- if (!console)
+ if (!data->console)
port->wakeup_irq = platform_get_irq_optional(pdev, 1);
if (of_property_read_bool(pdev->dev.of_node, "rx-tx-swap"))
@@ -1482,7 +1661,6 @@ static int qcom_geni_serial_probe(struct platform_device *pdev)
port->private_data.drv = drv;
uport->private_data = &port->private_data;
platform_set_drvdata(pdev, port);
- port->handle_rx = console ? handle_rx_console : handle_rx_uart;
ret = uart_add_one_port(drv, uport);
if (ret)
@@ -1594,6 +1772,16 @@ static int qcom_geni_serial_sys_hib_resume(struct device *dev)
return ret;
}
+static const struct qcom_geni_device_data qcom_geni_console_data = {
+ .console = true,
+ .mode = GENI_SE_FIFO,
+};
+
+static const struct qcom_geni_device_data qcom_geni_uart_data = {
+ .console = false,
+ .mode = GENI_SE_DMA,
+};
+
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
.suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
.resume = pm_sleep_ptr(qcom_geni_serial_sys_resume),
@@ -1604,8 +1792,14 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
};
static const struct of_device_id qcom_geni_serial_match_table[] = {
- { .compatible = "qcom,geni-debug-uart", },
- { .compatible = "qcom,geni-uart", },
+ {
+ .compatible = "qcom,geni-debug-uart",
+ .data = &qcom_geni_console_data,
+ },
+ {
+ .compatible = "qcom,geni-uart",
+ .data = &qcom_geni_uart_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 39f92eb1e698..29c94be09159 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1423,25 +1423,6 @@ static int sc16is7xx_probe(struct device *dev,
}
sched_set_fifo(s->kworker_task);
-#ifdef CONFIG_GPIOLIB
- if (devtype->nr_gpio) {
- /* Setup GPIO cotroller */
- s->gpio.owner = THIS_MODULE;
- s->gpio.parent = dev;
- s->gpio.label = dev_name(dev);
- s->gpio.direction_input = sc16is7xx_gpio_direction_input;
- s->gpio.get = sc16is7xx_gpio_get;
- s->gpio.direction_output = sc16is7xx_gpio_direction_output;
- s->gpio.set = sc16is7xx_gpio_set;
- s->gpio.base = -1;
- s->gpio.ngpio = devtype->nr_gpio;
- s->gpio.can_sleep = 1;
- ret = gpiochip_add_data(&s->gpio, s);
- if (ret)
- goto out_thread;
- }
-#endif
-
/* reset device, purging any pending irq / data */
regmap_write(s->regmap, SC16IS7XX_IOCONTROL_REG << SC16IS7XX_REG_SHIFT,
SC16IS7XX_IOCONTROL_SRESET_BIT);
@@ -1518,6 +1499,25 @@ static int sc16is7xx_probe(struct device *dev,
s->p[u].irda_mode = true;
}
+#ifdef CONFIG_GPIOLIB
+ if (devtype->nr_gpio) {
+ /* Setup GPIO cotroller */
+ s->gpio.owner = THIS_MODULE;
+ s->gpio.parent = dev;
+ s->gpio.label = dev_name(dev);
+ s->gpio.direction_input = sc16is7xx_gpio_direction_input;
+ s->gpio.get = sc16is7xx_gpio_get;
+ s->gpio.direction_output = sc16is7xx_gpio_direction_output;
+ s->gpio.set = sc16is7xx_gpio_set;
+ s->gpio.base = -1;
+ s->gpio.ngpio = devtype->nr_gpio;
+ s->gpio.can_sleep = 1;
+ ret = gpiochip_add_data(&s->gpio, s);
+ if (ret)
+ goto out_thread;
+ }
+#endif
+
/*
* Setup interrupt. We first try to acquire the IRQ line as level IRQ.
* If that succeeds, we can allow sharing the interrupt as well.
@@ -1537,18 +1537,19 @@ static int sc16is7xx_probe(struct device *dev,
if (!ret)
return 0;
-out_ports:
- for (i--; i >= 0; i--) {
- uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
- clear_bit(s->p[i].port.line, &sc16is7xx_lines);
- }
-
#ifdef CONFIG_GPIOLIB
if (devtype->nr_gpio)
gpiochip_remove(&s->gpio);
out_thread:
#endif
+
+out_ports:
+ for (i--; i >= 0; i--) {
+ uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
+ clear_bit(s->p[i].port.line, &sc16is7xx_lines);
+ }
+
kthread_stop(s->kworker_task);
out_clk:
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index 7df687822634..4f2fc5f7bb19 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -913,23 +913,13 @@ static int sccnxp_probe(struct platform_device *pdev)
} else if (PTR_ERR(s->regulator) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- clk = devm_clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
if (ret == -EPROBE_DEFER)
goto err_out;
uartclk = 0;
} else {
- ret = clk_prepare_enable(clk);
- if (ret)
- goto err_out;
-
- ret = devm_add_action_or_reset(&pdev->dev,
- (void(*)(void *))clk_disable_unprepare,
- clk);
- if (ret)
- goto err_out;
-
uartclk = clk_get_rate(clk);
}
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index e5b9773db5e3..1cf08b33456c 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -1046,6 +1046,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
if (tup->cdata->fifo_mode_enable_status) {
ret = tegra_uart_wait_fifo_mode_enabled(tup);
if (ret < 0) {
+ clk_disable_unprepare(tup->uart_clk);
dev_err(tup->uport.dev,
"Failed to enable FIFO mode: %d\n", ret);
return ret;
@@ -1067,6 +1068,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
*/
ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
if (ret < 0) {
+ clk_disable_unprepare(tup->uart_clk);
dev_err(tup->uport.dev, "Failed to set baud rate\n");
return ret;
}
@@ -1226,10 +1228,13 @@ static int tegra_uart_startup(struct uart_port *u)
dev_name(u->dev), tup);
if (ret < 0) {
dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);
- goto fail_hw_init;
+ goto fail_request_irq;
}
return 0;
+fail_request_irq:
+ /* tup->uart_clk is already enabled in tegra_uart_hw_init */
+ clk_disable_unprepare(tup->uart_clk);
fail_hw_init:
if (!tup->use_rx_pio)
tegra_uart_dma_channel_free(tup, true);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index ec874f3a567c..2bd32c8ece39 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -169,9 +169,9 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
#define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0)
#define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear)
-static void uart_port_dtr_rts(struct uart_port *uport, int raise)
+static void uart_port_dtr_rts(struct uart_port *uport, bool active)
{
- if (raise)
+ if (active)
uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
else
uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
@@ -182,7 +182,7 @@ static void uart_port_dtr_rts(struct uart_port *uport, int raise)
* will be serialised by the per-port mutex.
*/
static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
- int init_hw)
+ bool init_hw)
{
struct uart_port *uport = uart_port_check(state);
unsigned long flags;
@@ -239,7 +239,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
* port is open and ready to respond.
*/
if (init_hw && C_BAUD(tty))
- uart_port_dtr_rts(uport, 1);
+ uart_port_dtr_rts(uport, true);
}
/*
@@ -254,7 +254,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
}
static int uart_startup(struct tty_struct *tty, struct uart_state *state,
- int init_hw)
+ bool init_hw)
{
struct tty_port *port = &state->port;
int retval;
@@ -290,7 +290,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
set_bit(TTY_IO_ERROR, &tty->flags);
if (tty_port_initialized(port)) {
- tty_port_set_initialized(port, 0);
+ tty_port_set_initialized(port, false);
/*
* Turn off DTR and RTS early.
@@ -302,7 +302,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
}
if (!tty || C_HUPCL(tty))
- uart_port_dtr_rts(uport, 0);
+ uart_port_dtr_rts(uport, false);
uart_port_shutdown(port);
}
@@ -312,7 +312,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
* a DCD drop (hangup) at just the right time. Clear suspended bit so
* we don't try to resume a port that has been shutdown.
*/
- tty_port_set_suspended(port, 0);
+ tty_port_set_suspended(port, false);
/*
* Do not free() the transmit buffer page under the port lock since
@@ -997,7 +997,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
uart_change_speed(tty, state, NULL);
}
} else {
- retval = uart_startup(tty, state, 1);
+ retval = uart_startup(tty, state, true);
if (retval == 0)
tty_port_set_initialized(port, true);
if (retval > 0)
@@ -1165,7 +1165,7 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state)
*/
uport->ops->config_port(uport, flags);
- ret = uart_startup(tty, state, 1);
+ ret = uart_startup(tty, state, true);
if (ret == 0)
tty_port_set_initialized(port, true);
if (ret > 0)
@@ -1725,7 +1725,7 @@ static void uart_tty_port_shutdown(struct tty_port *port)
* a DCD drop (hangup) at just the right time. Clear suspended bit so
* we don't try to resume a port that has been shutdown.
*/
- tty_port_set_suspended(port, 0);
+ tty_port_set_suspended(port, false);
/*
* Free the transmit buffer.
@@ -1827,7 +1827,7 @@ static void uart_hangup(struct tty_struct *tty)
spin_lock_irqsave(&port->lock, flags);
port->count = 0;
spin_unlock_irqrestore(&port->lock, flags);
- tty_port_set_active(port, 0);
+ tty_port_set_active(port, false);
tty_port_tty_set(port, NULL);
if (uport && !uart_console(uport))
uart_change_pm(state, UART_PM_STATE_OFF);
@@ -1861,7 +1861,7 @@ static void uart_port_shutdown(struct tty_port *port)
}
}
-static int uart_carrier_raised(struct tty_port *port)
+static bool uart_carrier_raised(struct tty_port *port)
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
@@ -1875,18 +1875,17 @@ static int uart_carrier_raised(struct tty_port *port)
* continue and not sleep
*/
if (WARN_ON(!uport))
- return 1;
+ return true;
spin_lock_irq(&uport->lock);
uart_enable_ms(uport);
mctrl = uport->ops->get_mctrl(uport);
spin_unlock_irq(&uport->lock);
uart_port_deref(uport);
- if (mctrl & TIOCM_CAR)
- return 1;
- return 0;
+
+ return mctrl & TIOCM_CAR;
}
-static void uart_dtr_rts(struct tty_port *port, int raise)
+static void uart_dtr_rts(struct tty_port *port, bool active)
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
@@ -1894,7 +1893,7 @@ static void uart_dtr_rts(struct tty_port *port, int raise)
uport = uart_port_ref(state);
if (!uport)
return;
- uart_port_dtr_rts(uport, raise);
+ uart_port_dtr_rts(uport, active);
uart_port_deref(uport);
}
@@ -1943,9 +1942,9 @@ static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
/*
* Start up the serial port.
*/
- ret = uart_startup(tty, state, 0);
+ ret = uart_startup(tty, state, false);
if (ret > 0)
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
return ret;
}
@@ -2349,8 +2348,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
int tries;
unsigned int mctrl;
- tty_port_set_suspended(port, 1);
- tty_port_set_initialized(port, 0);
+ tty_port_set_suspended(port, true);
+ tty_port_set_initialized(port, false);
spin_lock_irq(&uport->lock);
ops->stop_tx(uport);
@@ -2461,7 +2460,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
uart_rs485_config(uport);
ops->start_tx(uport);
spin_unlock_irq(&uport->lock);
- tty_port_set_initialized(port, 1);
+ tty_port_set_initialized(port, true);
} else {
/*
* Failed to resume - maybe hardware went away?
@@ -2472,7 +2471,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
}
}
- tty_port_set_suspended(port, 0);
+ tty_port_set_suspended(port, false);
}
mutex_unlock(&port->mutex);
@@ -3258,11 +3257,11 @@ EXPORT_SYMBOL(uart_match_port);
/**
* uart_handle_dcd_change - handle a change of carrier detect state
* @uport: uart_port structure for the open port
- * @status: new carrier detect status, nonzero if active
+ * @active: new carrier detect status
*
* Caller must hold uport->lock.
*/
-void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
+void uart_handle_dcd_change(struct uart_port *uport, bool active)
{
struct tty_port *port = &uport->state->port;
struct tty_struct *tty = port->tty;
@@ -3274,7 +3273,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
ld = tty_ldisc_ref(tty);
if (ld) {
if (ld->ops->dcd_change)
- ld->ops->dcd_change(tty, status);
+ ld->ops->dcd_change(tty, active);
tty_ldisc_deref(ld);
}
}
@@ -3282,7 +3281,7 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
uport->icount.dcd++;
if (uart_dcd_enabled(uport)) {
- if (status)
+ if (active)
wake_up_interruptible(&port->open_wait);
else if (tty)
tty_hangup(tty);
@@ -3293,11 +3292,11 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change);
/**
* uart_handle_cts_change - handle a change of clear-to-send state
* @uport: uart_port structure for the open port
- * @status: new clear to send status, nonzero if active
+ * @active: new clear-to-send status
*
* Caller must hold uport->lock.
*/
-void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
+void uart_handle_cts_change(struct uart_port *uport, bool active)
{
lockdep_assert_held_once(&uport->lock);
@@ -3305,13 +3304,13 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
if (uart_softcts_mode(uport)) {
if (uport->hw_stopped) {
- if (status) {
+ if (active) {
uport->hw_stopped = 0;
uport->ops->start_tx(uport);
uart_write_wakeup(uport);
}
} else {
- if (!status) {
+ if (!active) {
uport->hw_stopped = 1;
uport->ops->stop_tx(uport);
}
@@ -3415,6 +3414,7 @@ int uart_get_rs485_mode(struct uart_port *port)
struct device *dev = port->dev;
u32 rs485_delay[2];
int ret;
+ int rx_during_tx_gpio_flag;
ret = device_property_read_u32_array(dev, "rs485-rts-delay",
rs485_delay, 2);
@@ -3463,6 +3463,17 @@ int uart_get_rs485_mode(struct uart_port *port)
if (port->rs485_term_gpio)
port->rs485_supported.flags |= SER_RS485_TERMINATE_BUS;
+ rx_during_tx_gpio_flag = (rs485conf->flags & SER_RS485_RX_DURING_TX) ?
+ GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
+ port->rs485_rx_during_tx_gpio = devm_gpiod_get_optional(dev,
+ "rs485-rx-during-tx",
+ rx_during_tx_gpio_flag);
+ if (IS_ERR(port->rs485_rx_during_tx_gpio)) {
+ ret = PTR_ERR(port->rs485_rx_during_tx_gpio);
+ port->rs485_rx_during_tx_gpio = NULL;
+ return dev_err_probe(dev, ret, "Cannot get rs485-rx-during-tx-gpios\n");
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(uart_get_rs485_mode);
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index 409e91d6829a..767ff9fdb2e5 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -226,7 +226,11 @@ static int stm32_usart_config_rs485(struct uart_port *port, struct ktermios *ter
stm32_usart_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
- rs485conf->flags |= SER_RS485_RX_DURING_TX;
+ if (port->rs485_rx_during_tx_gpio)
+ gpiod_set_value_cansleep(port->rs485_rx_during_tx_gpio,
+ !!(rs485conf->flags & SER_RS485_RX_DURING_TX));
+ else
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
if (rs485conf->flags & SER_RS485_ENABLED) {
cr1 = readl_relaxed(port->membase + ofs->cr1);
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 16c746a63258..7d38c33ef506 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -87,10 +87,10 @@ static int receive_chars_getchar(struct uart_port *port)
if (c == CON_HUP) {
hung_up = 1;
- uart_handle_dcd_change(port, 0);
+ uart_handle_dcd_change(port, false);
} else if (hung_up) {
hung_up = 0;
- uart_handle_dcd_change(port, 1);
+ uart_handle_dcd_change(port, true);
}
if (port->state == NULL) {
@@ -133,7 +133,7 @@ static int receive_chars_read(struct uart_port *port)
bytes_read = 1;
} else if (stat == CON_HUP) {
hung_up = 1;
- uart_handle_dcd_change(port, 0);
+ uart_handle_dcd_change(port, false);
continue;
} else {
/* HV_EWOULDBLOCK, etc. */
@@ -143,7 +143,7 @@ static int receive_chars_read(struct uart_port *port)
if (hung_up) {
hung_up = 0;
- uart_handle_dcd_change(port, 1);
+ uart_handle_dcd_change(port, true);
}
if (port->sysrq != 0 && *con_read_page) {
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index b09b6496ee3e..32c7a5b43f8e 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1468,6 +1468,8 @@ static int ucc_uart_remove(struct platform_device *ofdev)
uart_remove_one_port(&ucc_uart_driver, &qe_port->port);
+ of_node_put(qe_port->np);
+
kfree(qe_port);
return 0;
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index 72b76cdde534..33f258d6fef9 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -694,7 +694,7 @@ static void hangup(struct tty_struct *tty)
info->port.count = 0;
info->port.tty = NULL;
spin_unlock_irqrestore(&info->port.lock, flags);
- tty_port_set_active(&info->port, 0);
+ tty_port_set_active(&info->port, false);
mutex_unlock(&info->port.mutex);
wake_up_interruptible(&info->port.open_wait);
@@ -2354,7 +2354,7 @@ static int startup(struct slgt_info *info)
if (info->port.tty)
clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
- tty_port_set_initialized(&info->port, 1);
+ tty_port_set_initialized(&info->port, true);
return 0;
}
@@ -2401,7 +2401,7 @@ static void shutdown(struct slgt_info *info)
if (info->port.tty)
set_bit(TTY_IO_ERROR, &info->port.tty->flags);
- tty_port_set_initialized(&info->port, 0);
+ tty_port_set_initialized(&info->port, false);
}
static void program_hw(struct slgt_info *info)
@@ -3126,7 +3126,7 @@ static int tiocmset(struct tty_struct *tty,
return 0;
}
-static int carrier_raised(struct tty_port *port)
+static bool carrier_raised(struct tty_port *port)
{
unsigned long flags;
struct slgt_info *info = container_of(port, struct slgt_info, port);
@@ -3134,16 +3134,17 @@ static int carrier_raised(struct tty_port *port)
spin_lock_irqsave(&info->lock,flags);
get_gtsignals(info);
spin_unlock_irqrestore(&info->lock,flags);
- return (info->signals & SerialSignal_DCD) ? 1 : 0;
+
+ return info->signals & SerialSignal_DCD;
}
-static void dtr_rts(struct tty_port *port, int on)
+static void dtr_rts(struct tty_port *port, bool active)
{
unsigned long flags;
struct slgt_info *info = container_of(port, struct slgt_info, port);
spin_lock_irqsave(&info->lock,flags);
- if (on)
+ if (active)
info->signals |= SerialSignal_RTS | SerialSignal_DTR;
else
info->signals &= ~(SerialSignal_RTS | SerialSignal_DTR);
@@ -3162,14 +3163,14 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
int retval;
bool do_clocal = false;
unsigned long flags;
- int cd;
+ bool cd;
struct tty_port *port = &info->port;
DBGINFO(("%s block_til_ready\n", tty->driver->name));
if (filp->f_flags & O_NONBLOCK || tty_io_error(tty)) {
/* nonblock mode is set or port is not enabled */
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
return 0;
}
@@ -3226,7 +3227,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
port->blocked_open--;
if (!retval)
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
DBGINFO(("%s block_til_ready ready, rc=%d\n", tty->driver->name, retval));
return retval;
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 3149114bf130..36fb945fdad4 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1224,14 +1224,16 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
{
struct tty_struct *tty;
- if (driver->ops->lookup)
+ if (driver->ops->lookup) {
if (!file)
tty = ERR_PTR(-EIO);
else
tty = driver->ops->lookup(driver, file, idx);
- else
+ } else {
+ if (idx >= driver->num)
+ return ERR_PTR(-EINVAL);
tty = driver->ttys[idx];
-
+ }
if (!IS_ERR(tty))
tty_kref_get(tty);
return tty;
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index ce511557b98b..12983ce4e43e 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -270,13 +270,13 @@ EXPORT_SYMBOL(tty_termios_copy_hw);
* between the two termios structures, or a speed change is needed.
*/
-int tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
+bool tty_termios_hw_change(const struct ktermios *a, const struct ktermios *b)
{
if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed)
- return 1;
+ return true;
if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL))
- return 1;
- return 0;
+ return true;
+ return false;
}
EXPORT_SYMBOL(tty_termios_hw_change);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index dce08a6d7b5e..a788a6bf487d 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -367,7 +367,7 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
goto out;
if (tty_port_initialized(port)) {
- tty_port_set_initialized(port, 0);
+ tty_port_set_initialized(port, false);
/*
* Drop DTR/RTS if HUPCL is set. This causes any attached
* modem to hang up the line.
@@ -403,7 +403,7 @@ void tty_port_hangup(struct tty_port *port)
set_bit(TTY_IO_ERROR, &tty->flags);
port->tty = NULL;
spin_unlock_irqrestore(&port->lock, flags);
- tty_port_set_active(port, 0);
+ tty_port_set_active(port, false);
tty_port_shutdown(port, tty);
tty_kref_put(tty);
wake_up_interruptible(&port->open_wait);
@@ -444,10 +444,10 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup);
* to hide some internal details. This will eventually become entirely
* internal to the tty port.
*/
-int tty_port_carrier_raised(struct tty_port *port)
+bool tty_port_carrier_raised(struct tty_port *port)
{
if (port->ops->carrier_raised == NULL)
- return 1;
+ return true;
return port->ops->carrier_raised(port);
}
EXPORT_SYMBOL(tty_port_carrier_raised);
@@ -463,7 +463,7 @@ EXPORT_SYMBOL(tty_port_carrier_raised);
void tty_port_raise_dtr_rts(struct tty_port *port)
{
if (port->ops->dtr_rts)
- port->ops->dtr_rts(port, 1);
+ port->ops->dtr_rts(port, true);
}
EXPORT_SYMBOL(tty_port_raise_dtr_rts);
@@ -478,7 +478,7 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts);
void tty_port_lower_dtr_rts(struct tty_port *port)
{
if (port->ops->dtr_rts)
- port->ops->dtr_rts(port, 0);
+ port->ops->dtr_rts(port, false);
}
EXPORT_SYMBOL(tty_port_lower_dtr_rts);
@@ -518,14 +518,14 @@ int tty_port_block_til_ready(struct tty_port *port,
* the port has just hung up or is in another error state.
*/
if (tty_io_error(tty)) {
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
return 0;
}
if (filp == NULL || (filp->f_flags & O_NONBLOCK)) {
/* Indicate we are open */
if (C_BAUD(tty))
tty_port_raise_dtr_rts(port);
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
return 0;
}
@@ -588,7 +588,7 @@ int tty_port_block_til_ready(struct tty_port *port,
port->blocked_open--;
spin_unlock_irqrestore(&port->lock, flags);
if (retval == 0)
- tty_port_set_active(port, 1);
+ tty_port_set_active(port, true);
return retval;
}
EXPORT_SYMBOL(tty_port_block_til_ready);
@@ -695,7 +695,7 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
wake_up_interruptible(&port->open_wait);
}
spin_unlock_irqrestore(&port->lock, flags);
- tty_port_set_active(port, 0);
+ tty_port_set_active(port, false);
}
EXPORT_SYMBOL(tty_port_close_end);
@@ -788,7 +788,7 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
return retval;
}
}
- tty_port_set_initialized(port, 1);
+ tty_port_set_initialized(port, true);
}
mutex_unlock(&port->mutex);
return tty_port_block_til_ready(port, tty, filp);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 981d2bfcf9a5..57a5c23b51d4 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -316,73 +316,55 @@ void schedule_console_callback(void)
* Code to manage unicode-based screen buffers
*/
-#ifdef NO_VC_UNI_SCREEN
-/* this disables and optimizes related code away at compile time */
-#define get_vc_uniscr(vc) NULL
-#else
-#define get_vc_uniscr(vc) vc->vc_uni_screen
-#endif
-
-#define VC_UNI_SCREEN_DEBUG 0
-
-typedef uint32_t char32_t;
-
/*
* Our screen buffer is preceded by an array of line pointers so that
* scrolling only implies some pointer shuffling.
*/
-struct uni_screen {
- char32_t *lines[0];
-};
-static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+static u32 **vc_uniscr_alloc(unsigned int cols, unsigned int rows)
{
- struct uni_screen *uniscr;
+ u32 **uni_lines;
void *p;
- unsigned int memsize, i;
+ unsigned int memsize, i, col_size = cols * sizeof(**uni_lines);
/* allocate everything in one go */
- memsize = cols * rows * sizeof(char32_t);
- memsize += rows * sizeof(char32_t *);
- p = vzalloc(memsize);
- if (!p)
+ memsize = col_size * rows;
+ memsize += rows * sizeof(*uni_lines);
+ uni_lines = vzalloc(memsize);
+ if (!uni_lines)
return NULL;
/* initial line pointers */
- uniscr = p;
- p = uniscr->lines + rows;
+ p = uni_lines + rows;
for (i = 0; i < rows; i++) {
- uniscr->lines[i] = p;
- p += cols * sizeof(char32_t);
+ uni_lines[i] = p;
+ p += col_size;
}
- return uniscr;
+
+ return uni_lines;
}
-static void vc_uniscr_free(struct uni_screen *uniscr)
+static void vc_uniscr_free(u32 **uni_lines)
{
- vfree(uniscr);
+ vfree(uni_lines);
}
-static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+static void vc_uniscr_set(struct vc_data *vc, u32 **new_uni_lines)
{
- vc_uniscr_free(vc->vc_uni_screen);
- vc->vc_uni_screen = new_uniscr;
+ vc_uniscr_free(vc->vc_uni_lines);
+ vc->vc_uni_lines = new_uni_lines;
}
-static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
+static void vc_uniscr_putc(struct vc_data *vc, u32 uc)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr)
- uniscr->lines[vc->state.y][vc->state.x] = uc;
+ if (vc->vc_uni_lines)
+ vc->vc_uni_lines[vc->state.y][vc->state.x] = uc;
}
static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
+ if (vc->vc_uni_lines) {
+ u32 *ln = vc->vc_uni_lines[vc->state.y];
unsigned int x = vc->state.x, cols = vc->vc_cols;
memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
@@ -392,10 +374,8 @@ static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
+ if (vc->vc_uni_lines) {
+ u32 *ln = vc->vc_uni_lines[vc->state.y];
unsigned int x = vc->state.x, cols = vc->vc_cols;
memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
@@ -406,86 +386,84 @@ static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
-
- memset32(&ln[x], ' ', nr);
- }
+ if (vc->vc_uni_lines)
+ memset32(&vc->vc_uni_lines[vc->state.y][x], ' ', nr);
}
static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ if (vc->vc_uni_lines)
+ while (nr--)
+ memset32(vc->vc_uni_lines[y++], ' ', vc->vc_cols);
+}
+
+/* juggling array rotation algorithm (complexity O(N), size complexity O(1)) */
+static void juggle_array(u32 **array, unsigned int size, unsigned int nr)
+{
+ unsigned int gcd_idx;
- if (uniscr) {
- unsigned int cols = vc->vc_cols;
+ for (gcd_idx = 0; gcd_idx < gcd(nr, size); gcd_idx++) {
+ u32 *gcd_idx_val = array[gcd_idx];
+ unsigned int dst_idx = gcd_idx;
- while (nr--)
- memset32(uniscr->lines[y++], ' ', cols);
+ while (1) {
+ unsigned int src_idx = (dst_idx + nr) % size;
+ if (src_idx == gcd_idx)
+ break;
+
+ array[dst_idx] = array[src_idx];
+ dst_idx = src_idx;
+ }
+
+ array[dst_idx] = gcd_idx_val;
}
}
-static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
- enum con_scroll dir, unsigned int nr)
+static void vc_uniscr_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ u32 **uni_lines = vc->vc_uni_lines;
+ unsigned int size = bottom - top;
- if (uniscr) {
- unsigned int i, j, k, sz, d, clear;
+ if (!uni_lines)
+ return;
- sz = b - t;
- clear = b - nr;
- d = nr;
- if (dir == SM_DOWN) {
- clear = t;
- d = sz - nr;
- }
- for (i = 0; i < gcd(d, sz); i++) {
- char32_t *tmp = uniscr->lines[t + i];
- j = i;
- while (1) {
- k = j + d;
- if (k >= sz)
- k -= sz;
- if (k == i)
- break;
- uniscr->lines[t + j] = uniscr->lines[t + k];
- j = k;
- }
- uniscr->lines[t + j] = tmp;
- }
- vc_uniscr_clear_lines(vc, clear, nr);
+ if (dir == SM_DOWN) {
+ juggle_array(&uni_lines[top], size, size - nr);
+ vc_uniscr_clear_lines(vc, top, nr);
+ } else {
+ juggle_array(&uni_lines[top], size, nr);
+ vc_uniscr_clear_lines(vc, bottom - nr, nr);
}
}
-static void vc_uniscr_copy_area(struct uni_screen *dst,
+static void vc_uniscr_copy_area(u32 **dst_lines,
unsigned int dst_cols,
unsigned int dst_rows,
- struct uni_screen *src,
+ u32 **src_lines,
unsigned int src_cols,
unsigned int src_top_row,
unsigned int src_bot_row)
{
unsigned int dst_row = 0;
- if (!dst)
+ if (!dst_lines)
return;
while (src_top_row < src_bot_row) {
- char32_t *src_line = src->lines[src_top_row];
- char32_t *dst_line = dst->lines[dst_row];
+ u32 *src_line = src_lines[src_top_row];
+ u32 *dst_line = dst_lines[dst_row];
- memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
+ memcpy(dst_line, src_line, src_cols * sizeof(*src_line));
if (dst_cols - src_cols)
memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
src_top_row++;
dst_row++;
}
while (dst_row < dst_rows) {
- char32_t *dst_line = dst->lines[dst_row];
+ u32 *dst_line = dst_lines[dst_row];
memset32(dst_line, ' ', dst_cols);
dst_row++;
@@ -500,23 +478,20 @@ static void vc_uniscr_copy_area(struct uni_screen *dst,
*/
int vc_uniscr_check(struct vc_data *vc)
{
- struct uni_screen *uniscr;
+ u32 **uni_lines;
unsigned short *p;
int x, y, mask;
- if (__is_defined(NO_VC_UNI_SCREEN))
- return -EOPNOTSUPP;
-
WARN_CONSOLE_UNLOCKED();
if (!vc->vc_utf)
return -ENODATA;
- if (vc->vc_uni_screen)
+ if (vc->vc_uni_lines)
return 0;
- uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
- if (!uniscr)
+ uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
+ if (!uni_lines)
return -ENOMEM;
/*
@@ -528,14 +503,15 @@ int vc_uniscr_check(struct vc_data *vc)
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
+ u32 *line = uni_lines[y];
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
- vc->vc_uni_screen = uniscr;
+ vc->vc_uni_lines = uni_lines;
+
return 0;
}
@@ -547,11 +523,12 @@ int vc_uniscr_check(struct vc_data *vc)
void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
unsigned int row, unsigned int col, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ u32 **uni_lines = vc->vc_uni_lines;
int offset = row * vc->vc_size_row + col * 2;
unsigned long pos;
- BUG_ON(!uniscr);
+ if (WARN_ON_ONCE(!uni_lines))
+ return;
pos = (unsigned long)screenpos(vc, offset, viewed);
if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
@@ -562,7 +539,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
- memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+ memcpy(dest, &uni_lines[row][col], nr * sizeof(u32));
} else {
/*
* Scrollback is active. For now let's simply backtranslate
@@ -572,7 +549,7 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
*/
u16 *p = (u16 *)pos;
int mask = vc->vc_hi_font_mask | 0xff;
- char32_t *uni_buf = dest;
+ u32 *uni_buf = dest;
while (nr--) {
u16 glyph = scr_readw(p++) & mask;
*uni_buf++ = inverse_translate(vc, glyph, true);
@@ -580,64 +557,31 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
}
}
-/* this is for validation and debugging only */
-static void vc_uniscr_debug_check(struct vc_data *vc)
+static void con_scroll(struct vc_data *vc, unsigned int top,
+ unsigned int bottom, enum con_scroll dir,
+ unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
- unsigned short *p;
- int x, y, mask;
+ unsigned int rows = bottom - top;
+ u16 *clear, *dst, *src;
- if (!VC_UNI_SCREEN_DEBUG || !uniscr)
+ if (top + nr >= bottom)
+ nr = rows - 1;
+ if (bottom > vc->vc_rows || top >= bottom || nr < 1)
return;
- WARN_CONSOLE_UNLOCKED();
-
- /*
- * Make sure our unicode screen translates into the same glyphs
- * as the actual screen. This is brutal indeed.
- */
- p = (unsigned short *)vc->vc_origin;
- mask = vc->vc_hi_font_mask | 0xff;
- for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
- for (x = 0; x < vc->vc_cols; x++) {
- u16 glyph = scr_readw(p++) & mask;
- char32_t uc = line[x];
- int tc = conv_uni_to_pc(vc, uc);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, 0xfffd);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, '?');
- if (tc != glyph)
- pr_err_ratelimited(
- "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n",
- __func__, x, y, glyph, tc);
- }
- }
-}
-
-
-static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
- enum con_scroll dir, unsigned int nr)
-{
- u16 *clear, *d, *s;
-
- if (t + nr >= b)
- nr = b - t - 1;
- if (b > vc->vc_rows || t >= b || nr < 1)
- return;
- vc_uniscr_scroll(vc, t, b, dir, nr);
- if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
+ vc_uniscr_scroll(vc, top, bottom, dir, nr);
+ if (con_is_visible(vc) &&
+ vc->vc_sw->con_scroll(vc, top, bottom, dir, nr))
return;
- s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t);
- d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr));
+ src = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * top);
+ dst = (u16 *)(vc->vc_origin + vc->vc_size_row * (top + nr));
if (dir == SM_UP) {
- clear = s + (b - t - nr) * vc->vc_cols;
- swap(s, d);
+ clear = src + (rows - nr) * vc->vc_cols;
+ swap(src, dst);
}
- scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row);
+ scr_memmovew(dst, src, (rows - nr) * vc->vc_size_row);
scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
}
@@ -1201,7 +1145,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
unsigned int new_cols, new_rows, new_row_size, new_screen_size;
unsigned int user;
unsigned short *oldscreen, *newscreen;
- struct uni_screen *new_uniscr = NULL;
+ u32 **new_uniscr = NULL;
WARN_CONSOLE_UNLOCKED();
@@ -1245,7 +1189,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
if (!newscreen)
return -ENOMEM;
- if (get_vc_uniscr(vc)) {
+ if (vc->vc_uni_lines) {
new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
if (!new_uniscr) {
kfree(newscreen);
@@ -1297,7 +1241,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
end = old_origin + old_row_size * min(old_rows, new_rows);
vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
- get_vc_uniscr(vc), rlth/2, first_copied_row,
+ vc->vc_uni_lines, rlth/2, first_copied_row,
min(old_rows, new_rows));
vc_uniscr_set(vc, new_uniscr);
@@ -2959,7 +2903,6 @@ rescan_last_byte:
goto rescan_last_byte;
}
con_flush(vc, &draw);
- vc_uniscr_debug_check(vc);
console_conditional_schedule();
notify_update(vc);
console_unlock();
@@ -3156,8 +3099,14 @@ static struct tty_driver *vt_console_device(struct console *c, int *index)
return console_driver;
}
+static int vt_console_setup(struct console *co, char *options)
+{
+ return co->index >= MAX_NR_CONSOLES ? -EINVAL : 0;
+}
+
static struct console vt_console_driver = {
.name = "tty",
+ .setup = vt_console_setup,
.write = vt_console_print,
.device = vt_console_device,
.unblank = unblank_screen,
@@ -4574,26 +4523,30 @@ void reset_palette(struct vc_data *vc)
/*
* Font switching
*
- * Currently we only support fonts up to 32 pixels wide, at a maximum height
- * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints,
- * depending on width) reserved for each character which is kinda wasty, but
- * this is done in order to maintain compatibility with the EGA/VGA fonts. It
- * is up to the actual low-level console-driver convert data into its favorite
- * format (maybe we should add a `fontoffset' field to the `display'
- * structure so we won't have to convert the fontdata all the time.
+ * Currently we only support fonts up to 128 pixels wide, at a maximum height
+ * of 128 pixels. Userspace fontdata may have to be stored with 32 bytes
+ * (shorts/ints, depending on width) reserved for each character which is
+ * kinda wasty, but this is done in order to maintain compatibility with the
+ * EGA/VGA fonts. It is up to the actual low-level console-driver convert data
+ * into its favorite format (maybe we should add a `fontoffset' field to the
+ * `display' structure so we won't have to convert the fontdata all the time.
* /Jes
*/
-#define max_font_size 65536
+#define max_font_width 64
+#define max_font_height 128
+#define max_font_glyphs 512
+#define max_font_size (max_font_glyphs*max_font_width*max_font_height)
static int con_font_get(struct vc_data *vc, struct console_font_op *op)
{
struct console_font font;
int rc = -EINVAL;
int c;
+ unsigned int vpitch = op->op == KD_FONT_OP_GET_TALL ? op->height : 32;
if (op->data) {
- font.data = kmalloc(max_font_size, GFP_KERNEL);
+ font.data = kvmalloc(max_font_size, GFP_KERNEL);
if (!font.data)
return -ENOMEM;
} else
@@ -4603,7 +4556,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
if (vc->vc_mode != KD_TEXT)
rc = -EINVAL;
else if (vc->vc_sw->con_font_get)
- rc = vc->vc_sw->con_font_get(vc, &font);
+ rc = vc->vc_sw->con_font_get(vc, &font, vpitch);
else
rc = -ENOSYS;
console_unlock();
@@ -4611,7 +4564,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
if (rc)
goto out;
- c = (font.width+7)/8 * 32 * font.charcount;
+ c = (font.width+7)/8 * vpitch * font.charcount;
if (op->data && font.charcount > op->charcount)
rc = -ENOSPC;
@@ -4628,7 +4581,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
rc = -EFAULT;
out:
- kfree(font.data);
+ kvfree(font.data);
return rc;
}
@@ -4637,16 +4590,20 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op)
struct console_font font;
int rc = -EINVAL;
int size;
+ unsigned int vpitch = op->op == KD_FONT_OP_SET_TALL ? op->height : 32;
if (vc->vc_mode != KD_TEXT)
return -EINVAL;
if (!op->data)
return -EINVAL;
- if (op->charcount > 512)
+ if (op->charcount > max_font_glyphs)
+ return -EINVAL;
+ if (op->width <= 0 || op->width > max_font_width || !op->height ||
+ op->height > max_font_height)
return -EINVAL;
- if (op->width <= 0 || op->width > 32 || !op->height || op->height > 32)
+ if (vpitch < op->height)
return -EINVAL;
- size = (op->width+7)/8 * 32 * op->charcount;
+ size = (op->width+7)/8 * vpitch * op->charcount;
if (size > max_font_size)
return -ENOSPC;
@@ -4664,7 +4621,7 @@ static int con_font_set(struct vc_data *vc, struct console_font_op *op)
else if (vc->vc_sw->con_font_set) {
if (vc_is_sel(vc))
clear_selection();
- rc = vc->vc_sw->con_font_set(vc, &font, op->flags);
+ rc = vc->vc_sw->con_font_set(vc, &font, vpitch, op->flags);
} else
rc = -ENOSYS;
console_unlock();
@@ -4710,8 +4667,10 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op)
{
switch (op->op) {
case KD_FONT_OP_SET:
+ case KD_FONT_OP_SET_TALL:
return con_font_set(vc, op);
case KD_FONT_OP_GET:
+ case KD_FONT_OP_GET_TALL:
return con_font_get(vc, op);
case KD_FONT_OP_SET_DEFAULT:
return con_font_default(vc, op);
@@ -4740,10 +4699,11 @@ EXPORT_SYMBOL_GPL(screen_glyph);
u32 screen_glyph_unicode(const struct vc_data *vc, int n)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ u32 **uni_lines = vc->vc_uni_lines;
+
+ if (uni_lines)
+ return uni_lines[n / vc->vc_cols][n % vc->vc_cols];
- if (uniscr)
- return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
return inverse_translate(vc, screen_glyph(vc, n * 2), true);
}
EXPORT_SYMBOL_GPL(screen_glyph_unicode);