Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 0840d27..c7623f9 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
config TTY
bool "Enable TTY" if EXPERT
default y
@@ -83,7 +84,6 @@
config VT_HW_CONSOLE_BINDING
bool "Support for binding and unbinding console drivers"
depends on HW_CONSOLE
- default n
---help---
The virtual terminal is the device that interacts with the physical
terminal through console drivers. On these systems, at least one
@@ -93,9 +93,9 @@
select the console driver that will serve as the backend for the
virtual terminals.
- See <file:Documentation/console/console.txt> for more
+ See <file:Documentation/driver-api/console.rst> for more
information. For framebuffer console users, please refer to
- <file:Documentation/fb/fbcon.txt>.
+ <file:Documentation/fb/fbcon.rst>.
config UNIX98_PTYS
bool "Unix98 PTY support" if EXPERT
@@ -175,7 +175,7 @@
This driver supports Comtrol RocketPort and RocketModem PCI boards.
These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or
modems. For information about the RocketPort/RocketModem boards
- and this driver read <file:Documentation/serial/rocket.txt>.
+ and this driver read <file:Documentation/driver-api/serial/rocket.rst>.
To compile this driver as a module, choose M here: the
module will be called rocket.
@@ -193,7 +193,7 @@
your Linux box, for instance in order to become a dial-in server.
For information about the Cyclades-Z card, read
- <file:Documentation/serial/README.cycladesZ>.
+ <file:Documentation/driver-api/serial/cyclades_z.rst>.
To compile this driver as a module, choose M here: the
module will be called cyclades.
@@ -312,7 +312,6 @@
config TRACE_ROUTER
tristate "Trace data router for MIPI P1149.7 cJTAG standard"
depends on TRACE_SINK
- default n
help
The trace router uses the Linux tty line discipline framework to
route trace data coming from a tty port (say UART for example) to
@@ -328,7 +327,6 @@
config TRACE_SINK
tristate "Trace data sink for MIPI P1149.7 cJTAG standard"
- default n
help
The trace sink uses the Linux line discipline framework to receive
trace data coming from the trace router line discipline driver
@@ -376,6 +374,20 @@
there simply will be no early console output. This is true also
if you don't boot under a hypervisor at all.
+config NULL_TTY
+ tristate "NULL TTY driver"
+ help
+ Say Y here if you want a NULL TTY which simply discards messages.
+
+ This is useful to allow userspace applications which expect a console
+ device to work without modifications even when no console is
+ available or desired.
+
+ In order to use this driver, you should redirect the console to this
+ TTY, or boot the kernel with console=ttynull.
+
+ If unsure, say N.
+
config GOLDFISH_TTY
tristate "Goldfish TTY Driver"
depends on GOLDFISH
@@ -441,4 +453,28 @@
depends on SUN_LDOMS
help
Support for Sun logical domain consoles.
+
+config LDISC_AUTOLOAD
+ bool "Automatically load TTY Line Disciplines"
+ default y
+ help
+ Historically the kernel has always automatically loaded any
+ line discipline that is in a kernel module when a user asks
+ for it to be loaded with the TIOCSETD ioctl, or through other
+ means. This is not always the best thing to do on systems
+ where you know you will not be using some of the more
+ "ancient" line disciplines, so prevent the kernel from doing
+ this unless the request is coming from a process with the
+ CAP_SYS_MODULE permissions.
+
+ Say 'Y' here if you trust your userspace users to do the right
+ thing, or if you have only provided the line disciplines that
+ you know you will be using, or if you wish to continue to use
+ the traditional method of on-demand loading of these modules
+ by any user.
+
+ This functionality can be changed at runtime with the
+ dev.tty.ldisc_autoload sysctl, this configuration option will
+ only set the default value of this functionality.
+
endif # TTY
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index c72cafd..020b1cd 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -25,6 +25,7 @@
obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
obj-$(CONFIG_NOZOMI) += nozomi.o
+obj-$(CONFIG_NULL_TTY) += ttynull.o
obj-$(CONFIG_ROCKETPORT) += rocket.o
obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o
obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 34dead6..8330fd8 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -996,63 +996,55 @@
* ------------------------------------------------------------
*/
-static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
- struct serial_struct __user * retinfo)
+static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
{
- struct serial_struct tmp;
-
- memset(&tmp, 0, sizeof(tmp));
+ struct serial_state *state = tty->driver_data;
+
tty_lock(tty);
- tmp.line = tty->index;
- tmp.port = state->port;
- tmp.flags = state->tport.flags;
- tmp.xmit_fifo_size = state->xmit_fifo_size;
- tmp.baud_base = state->baud_base;
- tmp.close_delay = state->tport.close_delay;
- tmp.closing_wait = state->tport.closing_wait;
- tmp.custom_divisor = state->custom_divisor;
+ ss->line = tty->index;
+ ss->port = state->port;
+ ss->flags = state->tport.flags;
+ ss->xmit_fifo_size = state->xmit_fifo_size;
+ ss->baud_base = state->baud_base;
+ ss->close_delay = state->tport.close_delay;
+ ss->closing_wait = state->tport.closing_wait;
+ ss->custom_divisor = state->custom_divisor;
tty_unlock(tty);
- if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
- return -EFAULT;
return 0;
}
-static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
- struct serial_struct __user * new_info)
+static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss)
{
+ struct serial_state *state = tty->driver_data;
struct tty_port *port = &state->tport;
- struct serial_struct new_serial;
bool change_spd;
int retval = 0;
- if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
- return -EFAULT;
-
tty_lock(tty);
- change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
- new_serial.custom_divisor != state->custom_divisor;
- if (new_serial.irq || new_serial.port != state->port ||
- new_serial.xmit_fifo_size != state->xmit_fifo_size) {
+ change_spd = ((ss->flags ^ port->flags) & ASYNC_SPD_MASK) ||
+ ss->custom_divisor != state->custom_divisor;
+ if (ss->irq || ss->port != state->port ||
+ ss->xmit_fifo_size != state->xmit_fifo_size) {
tty_unlock(tty);
return -EINVAL;
}
if (!serial_isroot()) {
- if ((new_serial.baud_base != state->baud_base) ||
- (new_serial.close_delay != port->close_delay) ||
- (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
+ if ((ss->baud_base != state->baud_base) ||
+ (ss->close_delay != port->close_delay) ||
+ (ss->xmit_fifo_size != state->xmit_fifo_size) ||
+ ((ss->flags & ~ASYNC_USR_MASK) !=
(port->flags & ~ASYNC_USR_MASK))) {
tty_unlock(tty);
return -EPERM;
}
port->flags = ((port->flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- state->custom_divisor = new_serial.custom_divisor;
+ (ss->flags & ASYNC_USR_MASK));
+ state->custom_divisor = ss->custom_divisor;
goto check_and_exit;
}
- if (new_serial.baud_base < 9600) {
+ if (ss->baud_base < 9600) {
tty_unlock(tty);
return -EINVAL;
}
@@ -1062,19 +1054,19 @@
* At this point, we start making changes.....
*/
- state->baud_base = new_serial.baud_base;
+ state->baud_base = ss->baud_base;
port->flags = ((port->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- state->custom_divisor = new_serial.custom_divisor;
- port->close_delay = new_serial.close_delay * HZ/100;
- port->closing_wait = new_serial.closing_wait * HZ/100;
+ (ss->flags & ASYNC_FLAGS));
+ state->custom_divisor = ss->custom_divisor;
+ port->close_delay = ss->close_delay * HZ/100;
+ port->closing_wait = ss->closing_wait * HZ/100;
port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
check_and_exit:
if (tty_port_initialized(port)) {
if (change_spd) {
/* warn about deprecation unless clearing */
- if (new_serial.flags & ASYNC_SPD_MASK)
+ if (ss->flags & ASYNC_SPD_MASK)
dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
change_speed(tty, state, NULL);
}
@@ -1084,7 +1076,6 @@
return retval;
}
-
/*
* get_lsr_info - get line status register info
*
@@ -1224,30 +1215,19 @@
if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
return -ENODEV;
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+ if ((cmd != TIOCSERCONFIG) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty_io_error(tty))
return -EIO;
}
switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(tty, info, argp);
- case TIOCSSERIAL:
- return set_serial_info(tty, info, argp);
case TIOCSERCONFIG:
return 0;
case TIOCSERGETLSR: /* Get line status register */
return get_lsr_info(info, argp);
- case TIOCSERGSTRUCT:
- if (copy_to_user(argp,
- info, sizeof(struct serial_state)))
- return -EFAULT;
- return 0;
-
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
@@ -1288,12 +1268,6 @@
finish_wait(&info->tport.delta_msr_wait, &wait);
return ret;
- case TIOCSERGWILD:
- case TIOCSERSWILD:
- /* "setserial -W" is called in Debian boot */
- printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
- return 0;
-
default:
return -ENOIOCTLCMD;
}
@@ -1607,6 +1581,8 @@
.tiocmget = rs_tiocmget,
.tiocmset = rs_tiocmset,
.get_icount = rs_get_icount,
+ .set_serial = set_serial_info,
+ .get_serial = get_serial_info,
.proc_show = rs_proc_show,
};
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index 6d3c580..4562c80 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -2257,44 +2257,45 @@
}
} /* set_line_char */
-static int cy_get_serial_info(struct cyclades_port *info,
- struct serial_struct __user *retinfo)
+static int cy_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
+ struct cyclades_port *info = tty->driver_data;
struct cyclades_card *cinfo = info->card;
- struct serial_struct tmp = {
- .type = info->type,
- .line = info->line,
- .port = (info->card - cy_card) * 0x100 + info->line -
- cinfo->first_line,
- .irq = cinfo->irq,
- .flags = info->port.flags,
- .close_delay = info->port.close_delay,
- .closing_wait = info->port.closing_wait,
- .baud_base = info->baud,
- .custom_divisor = info->custom_divisor,
- };
- return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
+
+ if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
+ return -ENODEV;
+ ss->type = info->type;
+ ss->line = info->line;
+ ss->port = (info->card - cy_card) * 0x100 + info->line -
+ cinfo->first_line;
+ ss->irq = cinfo->irq;
+ ss->flags = info->port.flags;
+ ss->close_delay = info->port.close_delay;
+ ss->closing_wait = info->port.closing_wait;
+ ss->baud_base = info->baud;
+ ss->custom_divisor = info->custom_divisor;
+ return 0;
}
-static int
-cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty,
- struct serial_struct __user *new_info)
+static int cy_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct cyclades_port *info = tty->driver_data;
int old_flags;
int ret;
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
+ if (serial_paranoia_check(info, tty->name, "cy_ioctl"))
+ return -ENODEV;
mutex_lock(&info->port.mutex);
old_flags = info->port.flags;
if (!capable(CAP_SYS_ADMIN)) {
- if (new_serial.close_delay != info->port.close_delay ||
- new_serial.baud_base != info->baud ||
- (new_serial.flags & ASYNC_FLAGS &
+ if (ss->close_delay != info->port.close_delay ||
+ ss->baud_base != info->baud ||
+ (ss->flags & ASYNC_FLAGS &
~ASYNC_USR_MASK) !=
(info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK))
{
@@ -2302,9 +2303,9 @@
return -EPERM;
}
info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK);
- info->baud = new_serial.baud_base;
- info->custom_divisor = new_serial.custom_divisor;
+ (ss->flags & ASYNC_USR_MASK);
+ info->baud = ss->baud_base;
+ info->custom_divisor = ss->custom_divisor;
goto check_and_exit;
}
@@ -2313,18 +2314,18 @@
* At this point, we start making changes.....
*/
- info->baud = new_serial.baud_base;
- info->custom_divisor = new_serial.custom_divisor;
+ info->baud = ss->baud_base;
+ info->custom_divisor = ss->custom_divisor;
info->port.flags = (info->port.flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS);
- info->port.close_delay = new_serial.close_delay * HZ / 100;
- info->port.closing_wait = new_serial.closing_wait * HZ / 100;
+ (ss->flags & ASYNC_FLAGS);
+ info->port.close_delay = ss->close_delay * HZ / 100;
+ info->port.closing_wait = ss->closing_wait * HZ / 100;
check_and_exit:
if (tty_port_initialized(&info->port)) {
- if ((new_serial.flags ^ old_flags) & ASYNC_SPD_MASK) {
+ if ((ss->flags ^ old_flags) & ASYNC_SPD_MASK) {
/* warn about deprecation unless clearing */
- if (new_serial.flags & ASYNC_SPD_MASK)
+ if (ss->flags & ASYNC_SPD_MASK)
dev_warn_ratelimited(tty->dev, "use of SPD flags is deprecated\n");
}
cy_set_line_char(info, tty);
@@ -2698,12 +2699,6 @@
case CYGETWAIT:
ret_val = info->port.closing_wait / (HZ / 100);
break;
- case TIOCGSERIAL:
- ret_val = cy_get_serial_info(info, argp);
- break;
- case TIOCSSERIAL:
- ret_val = cy_set_serial_info(info, tty, argp);
- break;
case TIOCSERGETLSR: /* Get line status register */
ret_val = get_lsr_info(info, argp);
break;
@@ -4011,6 +4006,8 @@
.tiocmget = cy_tiocmget,
.tiocmset = cy_tiocmset,
.get_icount = cy_get_icount,
+ .set_serial = cy_set_serial_info,
+ .get_serial = cy_get_serial_info,
.proc_show = cyclades_proc_show,
};
diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c
index eea4049..769e0a5 100644
--- a/drivers/tty/ehv_bytechan.c
+++ b/drivers/tty/ehv_bytechan.c
@@ -128,8 +128,8 @@
*/
iprop = of_get_property(np, "hv-handle", NULL);
if (!iprop) {
- pr_err("ehv-bc: no 'hv-handle' property in %s node\n",
- np->name);
+ pr_err("ehv-bc: no 'hv-handle' property in %pOFn node\n",
+ np);
return 0;
}
stdout_bc = be32_to_cpu(*iprop);
@@ -661,8 +661,8 @@
iprop = of_get_property(np, "hv-handle", NULL);
if (!iprop) {
- dev_err(&pdev->dev, "no 'hv-handle' property in %s node\n",
- np->name);
+ dev_err(&pdev->dev, "no 'hv-handle' property in %pOFn node\n",
+ np);
return -ENODEV;
}
@@ -682,8 +682,8 @@
bc->rx_irq = irq_of_parse_and_map(np, 0);
bc->tx_irq = irq_of_parse_and_map(np, 1);
if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) {
- dev_err(&pdev->dev, "no 'interrupts' property in %s node\n",
- np->name);
+ dev_err(&pdev->dev, "no 'interrupts' property in %pOFn node\n",
+ np);
ret = -ENODEV;
goto error;
}
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 4293c17..4d22b91 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
if TTY
config HVC_DRIVER
@@ -24,7 +25,6 @@
config HVC_OLD_HVSI
bool "Old driver for pSeries serial port (/dev/hvsi*)"
depends on HVC_CONSOLE
- default n
config HVC_OPAL
bool "OPAL Console support"
@@ -73,7 +73,6 @@
bool "udbg based fake hypervisor console"
depends on PPC
select HVC_DRIVER
- default n
help
This is meant to be used during HW bring up or debugging when
no other console mechanism exist but udbg, to get you a quick
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
index 77baf89..c664125 100644
--- a/drivers/tty/hvc/hvc_opal.c
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -353,7 +353,7 @@
if (!opal)
return;
for_each_child_of_node(opal, np) {
- if (!strcmp(np->name, "serial")) {
+ if (of_node_name_eq(np, "serial")) {
stdout_node = np;
break;
}
diff --git a/drivers/tty/hvc/hvc_riscv_sbi.c b/drivers/tty/hvc/hvc_riscv_sbi.c
index 75155bd..31f53fa 100644
--- a/drivers/tty/hvc/hvc_riscv_sbi.c
+++ b/drivers/tty/hvc/hvc_riscv_sbi.c
@@ -53,7 +53,6 @@
static int __init hvc_sbi_console_init(void)
{
hvc_instantiate(0, 0, &hvc_sbi_ops);
- add_preferred_console("hvc", 0, NULL);
return 0;
}
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 59eaa62..7af54d6 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -107,6 +107,14 @@
return got;
}
+/**
+ * hvterm_raw_put_chars: send characters to firmware for given vterm adapter
+ * @vtermno: The virtual terminal number.
+ * @buf: The characters to send. Because of the underlying hypercall in
+ * hvc_put_chars(), this buffer must be at least 16 bytes long, even if
+ * you are sending fewer chars.
+ * @count: number of chars to send.
+ */
static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count)
{
struct hvterm_priv *pv = hvterm_privs[vtermno];
@@ -219,6 +227,7 @@
static void udbg_hvc_putc(char c)
{
int count = -1;
+ unsigned char bounce_buffer[16];
if (!hvterm_privs[0])
return;
@@ -229,7 +238,12 @@
do {
switch(hvterm_privs[0]->proto) {
case HV_PROTOCOL_RAW:
- count = hvterm_raw_put_chars(0, &c, 1);
+ /*
+ * hvterm_raw_put_chars requires at least a 16-byte
+ * buffer, so go via the bounce buffer
+ */
+ bounce_buffer[0] = c;
+ count = hvterm_raw_put_chars(0, bounce_buffer, 1);
break;
case HV_PROTOCOL_HVSI:
count = hvterm_hvsi_put_chars(0, &c, 1);
@@ -371,20 +385,11 @@
void __init hvc_vio_init_early(void)
{
const __be32 *termno;
- const char *name;
const struct hv_ops *ops;
/* find the boot console from /chosen/stdout */
- if (!of_stdout)
- return;
- name = of_get_property(of_stdout, "name", NULL);
- if (!name) {
- printk(KERN_WARNING "stdout node missing 'name' property!\n");
- return;
- }
-
/* Check if it's a virtual terminal */
- if (strncmp(name, "vty", 3) != 0)
+ if (!of_node_name_prefix(of_stdout, "vty"))
return;
termno = of_get_property(of_stdout, "reg", NULL);
if (termno == NULL)
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index dc43fa9..5ef0890 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -492,7 +492,7 @@
case XenbusStateClosed:
if (dev->state == XenbusStateClosed)
break;
- /* Missed the backend's CLOSING state -- fallthrough */
+ /* fall through - Missed the backend's CLOSING state. */
case XenbusStateClosing:
xenbus_frontend_closed(dev);
break;
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index cb4db1b..ee0604c 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -47,7 +47,7 @@
* using the 2.6 Linux kernel kref construct.
*
* For direction on installation and usage of this driver please reference
- * Documentation/powerpc/hvcs.txt.
+ * Documentation/powerpc/hvcs.rst.
*/
#include <linux/device.h>
@@ -871,8 +871,8 @@
hvcsd->p_partition_ID = pi->partition_ID;
/* copy the null-term char too */
- strlcpy(&hvcsd->p_location_code[0],
- &pi->location_code[0], sizeof(hvcsd->p_location_code));
+ strlcpy(hvcsd->p_location_code, pi->location_code,
+ sizeof(hvcsd->p_location_code));
}
/*
diff --git a/drivers/tty/ipwireless/Makefile b/drivers/tty/ipwireless/Makefile
index fe2e173..a665d02 100644
--- a/drivers/tty/ipwireless/Makefile
+++ b/drivers/tty/ipwireless/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the IPWireless driver
#
diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c
index b0baa4c..6bbf356 100644
--- a/drivers/tty/ipwireless/hardware.c
+++ b/drivers/tty/ipwireless/hardware.c
@@ -1516,6 +1516,8 @@
sizeof(struct ipw_setup_get_version_query_packet),
ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP,
TL_SETUP_SIGNO_GET_VERSION_QRY);
+ if (!ver_packet)
+ return;
ver_packet->header.length = sizeof(struct tl_setup_get_version_qry);
/*
diff --git a/drivers/tty/ipwireless/main.c b/drivers/tty/ipwireless/main.c
index 3475e84..4c18bbf 100644
--- a/drivers/tty/ipwireless/main.c
+++ b/drivers/tty/ipwireless/main.c
@@ -114,6 +114,10 @@
ipw->common_memory = ioremap(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]));
+ if (!ipw->common_memory) {
+ ret = -ENOMEM;
+ goto exit1;
+ }
if (!request_mem_region(p_dev->resource[2]->start,
resource_size(p_dev->resource[2]),
IPWIRELESS_PCCARD_NAME)) {
@@ -134,6 +138,10 @@
ipw->attr_memory = ioremap(p_dev->resource[3]->start,
resource_size(p_dev->resource[3]));
+ if (!ipw->attr_memory) {
+ ret = -ENOMEM;
+ goto exit3;
+ }
if (!request_mem_region(p_dev->resource[3]->start,
resource_size(p_dev->resource[3]),
IPWIRELESS_PCCARD_NAME)) {
diff --git a/drivers/tty/ipwireless/tty.c b/drivers/tty/ipwireless/tty.c
index 1ef751c..fad3401 100644
--- a/drivers/tty/ipwireless/tty.c
+++ b/drivers/tty/ipwireless/tty.c
@@ -248,22 +248,29 @@
return room;
}
-static int ipwireless_get_serial_info(struct ipw_tty *tty,
- struct serial_struct __user *retinfo)
+static int ipwireless_get_serial_info(struct tty_struct *linux_tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
+ struct ipw_tty *tty = linux_tty->driver_data;
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = PORT_UNKNOWN;
- tmp.line = tty->index;
- tmp.baud_base = 115200;
+ if (!tty)
+ return -ENODEV;
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ if (!tty->port.count)
+ return -EINVAL;
+ ss->type = PORT_UNKNOWN;
+ ss->line = tty->index;
+ ss->baud_base = 115200;
return 0;
}
+static int ipwireless_set_serial_info(struct tty_struct *linux_tty,
+ struct serial_struct *ss)
+{
+ return 0; /* Keeps the PCMCIA scripts happy. */
+}
+
static int ipw_chars_in_buffer(struct tty_struct *linux_tty)
{
struct ipw_tty *tty = linux_tty->driver_data;
@@ -386,15 +393,6 @@
return -EINVAL;
/* FIXME: Exactly how is the tty object locked here .. */
-
- switch (cmd) {
- case TIOCGSERIAL:
- return ipwireless_get_serial_info(tty, (void __user *) arg);
-
- case TIOCSSERIAL:
- return 0; /* Keeps the PCMCIA scripts happy. */
- }
-
if (tty->tty_type == TTYTYPE_MODEM) {
switch (cmd) {
case PPPIOCGCHAN:
@@ -561,6 +559,8 @@
.chars_in_buffer = ipw_chars_in_buffer,
.tiocmget = ipw_tiocmget,
.tiocmset = ipw_tiocmset,
+ .set_serial = ipwireless_set_serial_info,
+ .get_serial = ipwireless_get_serial_info,
};
int ipwireless_tty_init(void)
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
index 8d96e86..fc38f96 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -553,7 +553,6 @@
tty = tty_port_tty_get(&port->port);
if (tty == NULL) {
- word_count = byte_count >> 1;
while (byte_count > 1) {
inw(base);
byte_count -= 2;
@@ -1091,34 +1090,33 @@
}
static int isicom_set_serial_info(struct tty_struct *tty,
- struct serial_struct __user *info)
+ struct serial_struct *ss)
{
struct isi_port *port = tty->driver_data;
- struct serial_struct newinfo;
int reconfig_port;
- if (copy_from_user(&newinfo, info, sizeof(newinfo)))
- return -EFAULT;
+ if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
+ return -ENODEV;
mutex_lock(&port->port.mutex);
reconfig_port = ((port->port.flags & ASYNC_SPD_MASK) !=
- (newinfo.flags & ASYNC_SPD_MASK));
+ (ss->flags & ASYNC_SPD_MASK));
if (!capable(CAP_SYS_ADMIN)) {
- if ((newinfo.close_delay != port->port.close_delay) ||
- (newinfo.closing_wait != port->port.closing_wait) ||
- ((newinfo.flags & ~ASYNC_USR_MASK) !=
+ if ((ss->close_delay != port->port.close_delay) ||
+ (ss->closing_wait != port->port.closing_wait) ||
+ ((ss->flags & ~ASYNC_USR_MASK) !=
(port->port.flags & ~ASYNC_USR_MASK))) {
mutex_unlock(&port->port.mutex);
return -EPERM;
}
port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) |
- (newinfo.flags & ASYNC_USR_MASK));
+ (ss->flags & ASYNC_USR_MASK));
} else {
- port->port.close_delay = newinfo.close_delay;
- port->port.closing_wait = newinfo.closing_wait;
+ port->port.close_delay = ss->close_delay;
+ port->port.closing_wait = ss->closing_wait;
port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) |
- (newinfo.flags & ASYNC_FLAGS));
+ (ss->flags & ASYNC_FLAGS));
}
if (reconfig_port) {
unsigned long flags;
@@ -1130,46 +1128,24 @@
return 0;
}
-static int isicom_get_serial_info(struct isi_port *port,
- struct serial_struct __user *info)
-{
- struct serial_struct out_info;
-
- mutex_lock(&port->port.mutex);
- memset(&out_info, 0, sizeof(out_info));
-/* out_info.type = ? */
- out_info.line = port - isi_ports;
- out_info.port = port->card->base;
- out_info.irq = port->card->irq;
- out_info.flags = port->port.flags;
-/* out_info.baud_base = ? */
- out_info.close_delay = port->port.close_delay;
- out_info.closing_wait = port->port.closing_wait;
- mutex_unlock(&port->port.mutex);
- if (copy_to_user(info, &out_info, sizeof(out_info)))
- return -EFAULT;
- return 0;
-}
-
-static int isicom_ioctl(struct tty_struct *tty,
- unsigned int cmd, unsigned long arg)
+static int isicom_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
struct isi_port *port = tty->driver_data;
- void __user *argp = (void __user *)arg;
if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
return -ENODEV;
- switch (cmd) {
- case TIOCGSERIAL:
- return isicom_get_serial_info(port, argp);
-
- case TIOCSSERIAL:
- return isicom_set_serial_info(tty, argp);
-
- default:
- return -ENOIOCTLCMD;
- }
+ mutex_lock(&port->port.mutex);
+/* ss->type = ? */
+ ss->line = port - isi_ports;
+ ss->port = port->card->base;
+ ss->irq = port->card->irq;
+ ss->flags = port->port.flags;
+/* ss->baud_base = ? */
+ ss->close_delay = port->port.close_delay;
+ ss->closing_wait = port->port.closing_wait;
+ mutex_unlock(&port->port.mutex);
return 0;
}
@@ -1273,7 +1249,6 @@
.flush_chars = isicom_flush_chars,
.write_room = isicom_write_room,
.chars_in_buffer = isicom_chars_in_buffer,
- .ioctl = isicom_ioctl,
.set_termios = isicom_set_termios,
.throttle = isicom_throttle,
.unthrottle = isicom_unthrottle,
@@ -1284,6 +1259,8 @@
.tiocmget = isicom_tiocmget,
.tiocmset = isicom_tiocmset,
.break_ctl = isicom_send_break,
+ .get_serial = isicom_get_serial_info,
+ .set_serial = isicom_set_serial_info,
};
static const struct tty_port_operations isicom_port_ops = {
diff --git a/drivers/tty/moxa.c b/drivers/tty/moxa.c
index 250a19f..3a1a5e0 100644
--- a/drivers/tty/moxa.c
+++ b/drivers/tty/moxa.c
@@ -221,8 +221,8 @@
static int MoxaPortTxFree(struct moxa_port *);
static void MoxaPortTxDisable(struct moxa_port *);
static void MoxaPortTxEnable(struct moxa_port *);
-static int moxa_get_serial_info(struct moxa_port *, struct serial_struct __user *);
-static int moxa_set_serial_info(struct moxa_port *, struct serial_struct __user *);
+static int moxa_get_serial_info(struct tty_struct *, struct serial_struct *);
+static int moxa_set_serial_info(struct tty_struct *, struct serial_struct *);
static void MoxaSetFifo(struct moxa_port *port, int enable);
/*
@@ -375,16 +375,6 @@
}
break;
}
- case TIOCGSERIAL:
- mutex_lock(&ch->port.mutex);
- ret = moxa_get_serial_info(ch, argp);
- mutex_unlock(&ch->port.mutex);
- break;
- case TIOCSSERIAL:
- mutex_lock(&ch->port.mutex);
- ret = moxa_set_serial_info(ch, argp);
- mutex_unlock(&ch->port.mutex);
- break;
default:
ret = -ENOIOCTLCMD;
}
@@ -415,6 +405,8 @@
.break_ctl = moxa_break_ctl,
.tiocmget = moxa_tiocmget,
.tiocmset = moxa_tiocmset,
+ .set_serial = moxa_set_serial_info,
+ .get_serial = moxa_get_serial_info,
};
static const struct tty_port_operations moxa_port_ops = {
@@ -2034,46 +2026,55 @@
moxafunc(port->tableAddr, FC_SetXonState, Magic_code);
}
-static int moxa_get_serial_info(struct moxa_port *info,
- struct serial_struct __user *retinfo)
+static int moxa_get_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp = {
- .type = info->type,
- .line = info->port.tty->index,
- .flags = info->port.flags,
- .baud_base = 921600,
- .close_delay = info->port.close_delay
- };
- return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
+ struct moxa_port *info = tty->driver_data;
+
+ if (tty->index == MAX_PORTS)
+ return -EINVAL;
+ if (!info)
+ return -ENODEV;
+ mutex_lock(&info->port.mutex);
+ ss->type = info->type,
+ ss->line = info->port.tty->index,
+ ss->flags = info->port.flags,
+ ss->baud_base = 921600,
+ ss->close_delay = info->port.close_delay;
+ mutex_unlock(&info->port.mutex);
+ return 0;
}
-static int moxa_set_serial_info(struct moxa_port *info,
- struct serial_struct __user *new_info)
+static int moxa_set_serial_info(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct moxa_port *info = tty->driver_data;
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
+ if (tty->index == MAX_PORTS)
+ return -EINVAL;
+ if (!info)
+ return -ENODEV;
- if (new_serial.irq != 0 || new_serial.port != 0 ||
- new_serial.custom_divisor != 0 ||
- new_serial.baud_base != 921600)
+ if (ss->irq != 0 || ss->port != 0 ||
+ ss->custom_divisor != 0 ||
+ ss->baud_base != 921600)
return -EPERM;
+ mutex_lock(&info->port.mutex);
if (!capable(CAP_SYS_ADMIN)) {
- if (((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->port.flags & ~ASYNC_USR_MASK)))
+ if (((ss->flags & ~ASYNC_USR_MASK) !=
+ (info->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&info->port.mutex);
return -EPERM;
- } else
- info->port.close_delay = new_serial.close_delay * HZ / 100;
+ }
+ }
+ info->port.close_delay = ss->close_delay * HZ / 100;
- new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS);
- new_serial.flags |= (info->port.flags & ASYNC_FLAGS);
+ MoxaSetFifo(info, ss->type == PORT_16550A);
- MoxaSetFifo(info, new_serial.type == PORT_16550A);
-
- info->type = new_serial.type;
+ info->type = ss->type;
+ mutex_unlock(&info->port.mutex);
return 0;
}
diff --git a/drivers/tty/mxser.c b/drivers/tty/mxser.c
index 8bc15cb..9d00ff5 100644
--- a/drivers/tty/mxser.c
+++ b/drivers/tty/mxser.c
@@ -1207,76 +1207,90 @@
* ------------------------------------------------------------
*/
static int mxser_get_serial_info(struct tty_struct *tty,
- struct serial_struct __user *retinfo)
+ struct serial_struct *ss)
{
struct mxser_port *info = tty->driver_data;
- struct serial_struct tmp = {
- .type = info->type,
- .line = tty->index,
- .port = info->ioaddr,
- .irq = info->board->irq,
- .flags = info->port.flags,
- .baud_base = info->baud_base,
- .close_delay = info->port.close_delay,
- .closing_wait = info->port.closing_wait,
- .custom_divisor = info->custom_divisor,
- };
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
+ struct tty_port *port = &info->port;
+
+ if (tty->index == MXSER_PORTS)
+ return -ENOTTY;
+
+ mutex_lock(&port->mutex);
+ ss->type = info->type,
+ ss->line = tty->index,
+ ss->port = info->ioaddr,
+ ss->irq = info->board->irq,
+ ss->flags = info->port.flags,
+ ss->baud_base = info->baud_base,
+ ss->close_delay = info->port.close_delay,
+ ss->closing_wait = info->port.closing_wait,
+ ss->custom_divisor = info->custom_divisor,
+ mutex_unlock(&port->mutex);
return 0;
}
static int mxser_set_serial_info(struct tty_struct *tty,
- struct serial_struct __user *new_info)
+ struct serial_struct *ss)
{
struct mxser_port *info = tty->driver_data;
struct tty_port *port = &info->port;
- struct serial_struct new_serial;
speed_t baud;
unsigned long sl_flags;
unsigned int flags;
int retval = 0;
- if (!new_info || !info->ioaddr)
- return -ENODEV;
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
+ if (tty->index == MXSER_PORTS)
+ return -ENOTTY;
+ if (tty_io_error(tty))
+ return -EIO;
- if (new_serial.irq != info->board->irq ||
- new_serial.port != info->ioaddr)
+ mutex_lock(&port->mutex);
+ if (!info->ioaddr) {
+ mutex_unlock(&port->mutex);
+ return -ENODEV;
+ }
+
+ if (ss->irq != info->board->irq ||
+ ss->port != info->ioaddr) {
+ mutex_unlock(&port->mutex);
return -EINVAL;
+ }
flags = port->flags & ASYNC_SPD_MASK;
if (!capable(CAP_SYS_ADMIN)) {
- if ((new_serial.baud_base != info->baud_base) ||
- (new_serial.close_delay != info->port.close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK)))
+ if ((ss->baud_base != info->baud_base) ||
+ (ss->close_delay != info->port.close_delay) ||
+ ((ss->flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) {
+ mutex_unlock(&port->mutex);
return -EPERM;
+ }
info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
+ (ss->flags & ASYNC_USR_MASK));
} else {
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
port->flags = ((port->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- port->close_delay = new_serial.close_delay * HZ / 100;
- port->closing_wait = new_serial.closing_wait * HZ / 100;
+ (ss->flags & ASYNC_FLAGS));
+ port->close_delay = ss->close_delay * HZ / 100;
+ port->closing_wait = ss->closing_wait * HZ / 100;
port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
- (new_serial.baud_base != info->baud_base ||
- new_serial.custom_divisor !=
+ (ss->baud_base != info->baud_base ||
+ ss->custom_divisor !=
info->custom_divisor)) {
- if (new_serial.custom_divisor == 0)
+ if (ss->custom_divisor == 0) {
+ mutex_unlock(&port->mutex);
return -EINVAL;
- baud = new_serial.baud_base / new_serial.custom_divisor;
+ }
+ baud = ss->baud_base / ss->custom_divisor;
tty_encode_baud_rate(tty, baud, baud);
}
}
- info->type = new_serial.type;
+ info->type = ss->type;
process_txrx_fifo(info);
@@ -1291,6 +1305,7 @@
if (retval == 0)
tty_port_set_initialized(port, 1);
}
+ mutex_unlock(&port->mutex);
return retval;
}
@@ -1660,11 +1675,9 @@
unsigned int cmd, unsigned long arg)
{
struct mxser_port *info = tty->driver_data;
- struct tty_port *port = &info->port;
struct async_icount cnow;
unsigned long flags;
void __user *argp = (void __user *)arg;
- int retval;
if (tty->index == MXSER_PORTS)
return mxser_ioctl_special(cmd, argp);
@@ -1708,20 +1721,10 @@
return 0;
}
- if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && tty_io_error(tty))
+ if (cmd != TIOCMIWAIT && tty_io_error(tty))
return -EIO;
switch (cmd) {
- case TIOCGSERIAL:
- mutex_lock(&port->mutex);
- retval = mxser_get_serial_info(tty, argp);
- mutex_unlock(&port->mutex);
- return retval;
- case TIOCSSERIAL:
- mutex_lock(&port->mutex);
- retval = mxser_set_serial_info(tty, argp);
- mutex_unlock(&port->mutex);
- return retval;
case TIOCSERGETLSR: /* Get line status register */
return mxser_get_lsr_info(info, argp);
/*
@@ -2325,6 +2328,8 @@
.wait_until_sent = mxser_wait_until_sent,
.tiocmget = mxser_tiocmget,
.tiocmset = mxser_tiocmset,
+ .set_serial = mxser_set_serial_info,
+ .get_serial = mxser_get_serial_info,
.get_icount = mxser_get_icount,
};
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 86b7e20..36a3eb4 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -143,8 +143,8 @@
struct sk_buff *skb; /* Frame being sent */
struct sk_buff_head skb_list; /* Queued frames */
/* Data handling callback */
- void (*data)(struct gsm_dlci *dlci, u8 *data, int len);
- void (*prev_data)(struct gsm_dlci *dlci, u8 *data, int len);
+ void (*data)(struct gsm_dlci *dlci, const u8 *data, int len);
+ void (*prev_data)(struct gsm_dlci *dlci, const u8 *data, int len);
struct net_device *net; /* network interface, if created */
};
@@ -988,7 +988,7 @@
* Encode up and queue a UI/UIH frame containing our response.
*/
-static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data,
+static void gsm_control_reply(struct gsm_mux *gsm, int cmd, const u8 *data,
int dlen)
{
struct gsm_msg *msg;
@@ -1073,14 +1073,14 @@
* and if need be stuff a break message down the tty.
*/
-static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen)
+static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
{
unsigned int addr = 0;
unsigned int modem = 0;
unsigned int brk = 0;
struct gsm_dlci *dlci;
int len = clen;
- u8 *dp = data;
+ const u8 *dp = data;
struct tty_struct *tty;
while (gsm_read_ea(&addr, *dp++) == 0) {
@@ -1134,13 +1134,13 @@
* this into the uplink tty if present
*/
-static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen)
+static void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
{
struct tty_port *port;
unsigned int addr = 0;
u8 bits;
int len = clen;
- u8 *dp = data;
+ const u8 *dp = data;
while (gsm_read_ea(&addr, *dp++) == 0) {
len--;
@@ -1189,7 +1189,7 @@
*/
static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
- u8 *data, int clen)
+ const u8 *data, int clen)
{
u8 buf[1];
unsigned long flags;
@@ -1261,7 +1261,7 @@
*/
static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
- u8 *data, int clen)
+ const u8 *data, int clen)
{
struct gsm_control *ctrl;
unsigned long flags;
@@ -1553,7 +1553,7 @@
* open we shovel the bits down it, if not we drop them.
*/
-static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int clen)
+static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
{
/* krefs .. */
struct tty_port *port = &dlci->port;
@@ -1565,14 +1565,11 @@
pr_debug("%d bytes for tty\n", len);
switch (dlci->adaption) {
/* Unsupported types */
- /* Packetised interruptible data */
- case 4:
+ case 4: /* Packetised interruptible data */
break;
- /* Packetised uininterruptible voice/data */
- case 3:
+ case 3: /* Packetised uininterruptible voice/data */
break;
- /* Asynchronous serial with line state in each frame */
- case 2:
+ case 2: /* Asynchronous serial with line state in each frame */
while (gsm_read_ea(&modem, *data++) == 0) {
len--;
if (len == 0)
@@ -1583,8 +1580,8 @@
gsm_process_modem(tty, dlci, modem, clen);
tty_kref_put(tty);
}
- /* Line state will go via DLCI 0 controls only */
- case 1:
+ /* Fall through */
+ case 1: /* Line state will go via DLCI 0 controls only */
default:
tty_insert_flip_string(port, data, len);
tty_flip_buffer_push(port);
@@ -1603,7 +1600,7 @@
* and we divide up the work accordingly.
*/
-static void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len)
+static void gsm_dlci_command(struct gsm_dlci *dlci, const u8 *data, int len)
{
/* See what command is involved */
unsigned int command = 0;
@@ -1719,7 +1716,7 @@
gsm_destroy_network(dlci);
mutex_unlock(&dlci->mutex);
- tty_vhangup(tty);
+ tty_hangup(tty);
tty_port_tty_set(&dlci->port, NULL);
tty_kref_put(tty);
@@ -1979,7 +1976,7 @@
gsm->address = 0;
gsm->state = GSM_ADDRESS;
gsm->fcs = INIT_FCS;
- /* Drop through */
+ /* Fall through */
case GSM_ADDRESS: /* Address continuation */
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
if (gsm_read_ea(&gsm->address, c))
@@ -2174,6 +2171,16 @@
kref_put(&gsm->ref, gsm_free_muxr);
}
+static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
+{
+ return gsm->num * NUM_DLCI;
+}
+
+static inline unsigned int mux_line_to_num(unsigned int line)
+{
+ return line / NUM_DLCI;
+}
+
/**
* gsm_alloc_mux - allocate a mux
*
@@ -2214,6 +2221,111 @@
return gsm;
}
+static void gsm_copy_config_values(struct gsm_mux *gsm,
+ struct gsm_config *c)
+{
+ memset(c, 0, sizeof(*c));
+ c->adaption = gsm->adaption;
+ c->encapsulation = gsm->encoding;
+ c->initiator = gsm->initiator;
+ c->t1 = gsm->t1;
+ c->t2 = gsm->t2;
+ c->t3 = 0; /* Not supported */
+ c->n2 = gsm->n2;
+ if (gsm->ftype == UIH)
+ c->i = 1;
+ else
+ c->i = 2;
+ pr_debug("Ftype %d i %d\n", gsm->ftype, c->i);
+ c->mru = gsm->mru;
+ c->mtu = gsm->mtu;
+ c->k = 0;
+}
+
+static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
+{
+ int need_close = 0;
+ int need_restart = 0;
+
+ /* Stuff we don't support yet - UI or I frame transport, windowing */
+ if ((c->adaption != 1 && c->adaption != 2) || c->k)
+ return -EOPNOTSUPP;
+ /* Check the MRU/MTU range looks sane */
+ if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
+ return -EINVAL;
+ if (c->n2 < 3)
+ return -EINVAL;
+ if (c->encapsulation > 1) /* Basic, advanced, no I */
+ return -EINVAL;
+ if (c->initiator > 1)
+ return -EINVAL;
+ if (c->i == 0 || c->i > 2) /* UIH and UI only */
+ return -EINVAL;
+ /*
+ * See what is needed for reconfiguration
+ */
+
+ /* Timing fields */
+ if (c->t1 != 0 && c->t1 != gsm->t1)
+ need_restart = 1;
+ if (c->t2 != 0 && c->t2 != gsm->t2)
+ need_restart = 1;
+ if (c->encapsulation != gsm->encoding)
+ need_restart = 1;
+ if (c->adaption != gsm->adaption)
+ need_restart = 1;
+ /* Requires care */
+ if (c->initiator != gsm->initiator)
+ need_close = 1;
+ if (c->mru != gsm->mru)
+ need_restart = 1;
+ if (c->mtu != gsm->mtu)
+ need_restart = 1;
+
+ /*
+ * Close down what is needed, restart and initiate the new
+ * configuration
+ */
+
+ if (need_close || need_restart) {
+ int ret;
+
+ ret = gsm_disconnect(gsm);
+
+ if (ret)
+ return ret;
+ }
+ if (need_restart)
+ gsm_cleanup_mux(gsm);
+
+ gsm->initiator = c->initiator;
+ gsm->mru = c->mru;
+ gsm->mtu = c->mtu;
+ gsm->encoding = c->encapsulation;
+ gsm->adaption = c->adaption;
+ gsm->n2 = c->n2;
+
+ if (c->i == 1)
+ gsm->ftype = UIH;
+ else if (c->i == 2)
+ gsm->ftype = UI;
+
+ if (c->t1)
+ gsm->t1 = c->t1;
+ if (c->t2)
+ gsm->t2 = c->t2;
+
+ /*
+ * FIXME: We need to separate activation/deactivation from adding
+ * and removing from the mux array
+ */
+ if (need_restart)
+ gsm_activate_mux(gsm);
+ if (gsm->initiator && need_close)
+ gsm_dlci_begin_open(gsm->dlci[0]);
+ return 0;
+}
+
/**
* gsmld_output - write to link
* @gsm: our mux
@@ -2249,7 +2361,8 @@
static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
{
- int ret, i, base;
+ unsigned int base;
+ int ret, i;
gsm->tty = tty_kref_get(tty);
gsm->output = gsmld_output;
@@ -2259,7 +2372,7 @@
else {
/* Don't register device 0 - this is the control channel and not
a usable tty interface */
- base = gsm->num << 6; /* Base for this MUX */
+ base = mux_num_to_base(gsm); /* Base for this MUX */
for (i = 1; i < NUM_DLCI; i++)
tty_register_device(gsm_tty_driver, base + i, NULL);
}
@@ -2277,8 +2390,8 @@
static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
{
+ unsigned int base = mux_num_to_base(gsm); /* Base for this MUX */
int i;
- int base = gsm->num << 6; /* Base for this MUX */
WARN_ON(tty != gsm->tty);
for (i = 1; i < NUM_DLCI; i++)
@@ -2495,133 +2608,31 @@
return mask;
}
-static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm,
- struct gsm_config *c)
-{
- int need_close = 0;
- int need_restart = 0;
-
- /* Stuff we don't support yet - UI or I frame transport, windowing */
- if ((c->adaption != 1 && c->adaption != 2) || c->k)
- return -EOPNOTSUPP;
- /* Check the MRU/MTU range looks sane */
- if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
- return -EINVAL;
- if (c->n2 < 3)
- return -EINVAL;
- if (c->encapsulation > 1) /* Basic, advanced, no I */
- return -EINVAL;
- if (c->initiator > 1)
- return -EINVAL;
- if (c->i == 0 || c->i > 2) /* UIH and UI only */
- return -EINVAL;
- /*
- * See what is needed for reconfiguration
- */
-
- /* Timing fields */
- if (c->t1 != 0 && c->t1 != gsm->t1)
- need_restart = 1;
- if (c->t2 != 0 && c->t2 != gsm->t2)
- need_restart = 1;
- if (c->encapsulation != gsm->encoding)
- need_restart = 1;
- if (c->adaption != gsm->adaption)
- need_restart = 1;
- /* Requires care */
- if (c->initiator != gsm->initiator)
- need_close = 1;
- if (c->mru != gsm->mru)
- need_restart = 1;
- if (c->mtu != gsm->mtu)
- need_restart = 1;
-
- /*
- * Close down what is needed, restart and initiate the new
- * configuration
- */
-
- if (need_close || need_restart) {
- int ret;
-
- ret = gsm_disconnect(gsm);
-
- if (ret)
- return ret;
- }
- if (need_restart)
- gsm_cleanup_mux(gsm);
-
- gsm->initiator = c->initiator;
- gsm->mru = c->mru;
- gsm->mtu = c->mtu;
- gsm->encoding = c->encapsulation;
- gsm->adaption = c->adaption;
- gsm->n2 = c->n2;
-
- if (c->i == 1)
- gsm->ftype = UIH;
- else if (c->i == 2)
- gsm->ftype = UI;
-
- if (c->t1)
- gsm->t1 = c->t1;
- if (c->t2)
- gsm->t2 = c->t2;
-
- /* FIXME: We need to separate activation/deactivation from adding
- and removing from the mux array */
- if (need_restart)
- gsm_activate_mux(gsm);
- if (gsm->initiator && need_close)
- gsm_dlci_begin_open(gsm->dlci[0]);
- return 0;
-}
-
static int gsmld_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct gsm_config c;
struct gsm_mux *gsm = tty->disc_data;
+ unsigned int base;
switch (cmd) {
case GSMIOC_GETCONF:
- memset(&c, 0, sizeof(c));
- c.adaption = gsm->adaption;
- c.encapsulation = gsm->encoding;
- c.initiator = gsm->initiator;
- c.t1 = gsm->t1;
- c.t2 = gsm->t2;
- c.t3 = 0; /* Not supported */
- c.n2 = gsm->n2;
- if (gsm->ftype == UIH)
- c.i = 1;
- else
- c.i = 2;
- pr_debug("Ftype %d i %d\n", gsm->ftype, c.i);
- c.mru = gsm->mru;
- c.mtu = gsm->mtu;
- c.k = 0;
+ gsm_copy_config_values(gsm, &c);
if (copy_to_user((void *)arg, &c, sizeof(c)))
return -EFAULT;
return 0;
case GSMIOC_SETCONF:
if (copy_from_user(&c, (void *)arg, sizeof(c)))
return -EFAULT;
- return gsmld_config(tty, gsm, &c);
+ return gsm_config(gsm, &c);
+ case GSMIOC_GETFIRST:
+ base = mux_num_to_base(gsm);
+ return put_user(base + 1, (__u32 __user *)arg);
default:
return n_tty_ioctl_helper(tty, file, cmd, arg);
}
}
-#ifdef CONFIG_COMPAT
-static long gsmld_compat_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return gsmld_ioctl(tty, file, cmd, arg);
-}
-#endif
-
/*
* Network interface
*
@@ -2703,7 +2714,7 @@
}
static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
- unsigned char *in_buf, int size)
+ const unsigned char *in_buf, int size)
{
struct net_device *net = dlci->net;
struct sk_buff *skb;
@@ -2833,9 +2844,6 @@
.flush_buffer = gsmld_flush_buffer,
.read = gsmld_read,
.write = gsmld_write,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = gsmld_compat_ioctl,
-#endif
.ioctl = gsmld_ioctl,
.poll = gsmld_poll,
.receive_buf = gsmld_receive_buf,
@@ -2915,7 +2923,7 @@
struct gsm_mux *gsm;
struct gsm_dlci *dlci;
unsigned int line = tty->index;
- unsigned int mux = line >> 6;
+ unsigned int mux = mux_line_to_num(line);
bool alloc = false;
int ret;
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index dabb391..98361ac 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -573,7 +573,7 @@
return -EIO;
/* verify user access to buffer */
- if (!access_ok(VERIFY_WRITE, buf, nr)) {
+ if (!access_ok(buf, nr)) {
printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user "
"buffer\n", __FILE__, __LINE__);
return -EFAULT;
@@ -597,6 +597,7 @@
/* too large for caller's buffer */
ret = -EOVERFLOW;
} else {
+ __set_current_state(TASK_RUNNING);
if (copy_to_user(buf, rbuf->buf, rbuf->count))
ret = -EFAULT;
else
@@ -612,7 +613,7 @@
}
/* no data */
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN;
break;
}
@@ -679,7 +680,7 @@
if (tbuf)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
error = -EAGAIN;
break;
}
@@ -776,7 +777,7 @@
case TCOFLUSH:
flush_tx_queue(tty);
}
- /* fall through to default */
+ /* fall through - to default */
default:
error = n_tty_ioctl_helper(tty, file, cmd, arg);
@@ -967,6 +968,11 @@
} /* end of init_module() */
+#ifdef CONFIG_SPARC
+#undef __exitdata
+#define __exitdata
+#endif
+
static const char hdlc_unregister_ok[] __exitdata =
KERN_INFO "N_HDLC: line discipline unregistered\n";
static const char hdlc_unregister_fail[] __exitdata =
diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
index dbf1ab3..f75696f 100644
--- a/drivers/tty/n_r3964.c
+++ b/drivers/tty/n_r3964.c
@@ -134,6 +134,10 @@
const unsigned char *buf, size_t nr);
static int r3964_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+static int r3964_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+#endif
static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old);
static __poll_t r3964_poll(struct tty_struct *tty, struct file *file,
struct poll_table_struct *wait);
@@ -149,6 +153,9 @@
.read = r3964_read,
.write = r3964_write,
.ioctl = r3964_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = r3964_compat_ioctl,
+#endif
.set_termios = r3964_set_termios,
.poll = r3964_poll,
.receive_buf = r3964_receive_buf,
@@ -1078,7 +1085,7 @@
pMsg = remove_msg(pInfo, pClient);
if (pMsg == NULL) {
/* no messages available. */
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
ret = -EAGAIN;
goto unlock;
}
@@ -1210,6 +1217,21 @@
}
}
+#ifdef CONFIG_COMPAT
+static int r3964_compat_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case R3964_ENABLE_SIGNALS:
+ case R3964_SETPRIORITY:
+ case R3964_USE_BCC:
+ return r3964_ioctl(tty, file, cmd, arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+#endif
+
static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old)
{
TRACE_L("set_termios");
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 3ad4602..f9c5842 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -50,8 +50,10 @@
#include <linux/ratelimit.h>
#include <linux/vmalloc.h>
-
-/* number of characters left in xmit buffer before select has we have room */
+/*
+ * Until this number of characters is queued in the xmit buffer, select will
+ * return "we have room for writes".
+ */
#define WAKEUP_CHARS 256
/*
@@ -548,9 +550,9 @@
mutex_lock(&ldata->output_lock);
space = tty_write_room(tty);
- if (!space) {
+ if (space <= 0) {
mutex_unlock(&ldata->output_lock);
- return 0;
+ return space;
}
if (nr > space)
nr = space;
@@ -1702,7 +1704,7 @@
down_read(&tty->termios_rwsem);
- while (1) {
+ do {
/*
* When PARMRK is set, each input char may take up to 3 chars
* in the read buf; reduce the buffer space avail by 3x
@@ -1744,7 +1746,7 @@
fp += n;
count -= n;
rcvd += n;
- }
+ } while (!test_bit(TTY_LDISC_CHANGING, &tty->flags));
tty->receive_room = room;
@@ -2211,7 +2213,7 @@
break;
if (!timeout)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
@@ -2365,7 +2367,7 @@
}
if (!nr)
break;
- if (file->f_flags & O_NONBLOCK) {
+ if (tty_io_nonblock(tty, file)) {
retval = -EAGAIN;
break;
}
diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c
index fed820e..ed99948 100644
--- a/drivers/tty/nozomi.c
+++ b/drivers/tty/nozomi.c
@@ -1282,7 +1282,7 @@
static ssize_t card_type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+ const struct nozomi *dc = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", dc->card_type);
}
@@ -1291,7 +1291,7 @@
static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- const struct nozomi *dc = pci_get_drvdata(to_pci_dev(dev));
+ const struct nozomi *dc = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", dc->open_ttys);
}
@@ -1317,7 +1317,6 @@
static int nozomi_card_init(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
- resource_size_t start;
int ret;
struct nozomi *dc = NULL;
int ndev_idx;
@@ -1357,17 +1356,10 @@
goto err_disable_device;
}
- start = pci_resource_start(dc->pdev, 0);
- if (start == 0) {
- dev_err(&pdev->dev, "No I/O address for card detected\n");
- ret = -ENODEV;
- goto err_rel_regs;
- }
-
/* Find out what card type it is */
nozomi_get_card_type(dc);
- dc->base_addr = ioremap_nocache(start, dc->card_type);
+ dc->base_addr = pci_iomap(dc->pdev, 0, dc->card_type);
if (!dc->base_addr) {
dev_err(&pdev->dev, "Unable to map card MMIO\n");
ret = -ENODEV;
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 678406e..00099a8 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -28,6 +28,7 @@
#include <linux/mount.h>
#include <linux/file.h>
#include <linux/ioctl.h>
+#include <linux/compat.h>
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
@@ -488,6 +489,7 @@
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
static long pty_bsd_compat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -495,8 +497,11 @@
* PTY ioctls don't require any special translation between 32-bit and
* 64-bit userspace, they are already compatible.
*/
- return pty_bsd_ioctl(tty, cmd, arg);
+ return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
}
+#else
+#define pty_bsd_compat_ioctl NULL
+#endif
static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
/*
@@ -676,6 +681,7 @@
return -ENOIOCTLCMD;
}
+#ifdef CONFIG_COMPAT
static long pty_unix98_compat_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
@@ -683,8 +689,12 @@
* PTY ioctls don't require any special translation between 32-bit and
* 64-bit userspace, they are already compatible.
*/
- return pty_unix98_ioctl(tty, cmd, arg);
+ return pty_unix98_ioctl(tty, cmd,
+ cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
}
+#else
+#define pty_unix98_compat_ioctl NULL
+#endif
/**
* ptm_unix98_lookup - find a pty master
diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c
index b121d8f..5ba6816 100644
--- a/drivers/tty/rocket.c
+++ b/drivers/tty/rocket.c
@@ -266,7 +266,7 @@
module_param_array(pc104_4, ulong, NULL, 0);
MODULE_PARM_DESC(pc104_4, "set interface types for ISA(PC104) board #4 (e.g. pc104_4=232,232,485,485,...");
-static int rp_init(void);
+static int __init rp_init(void);
static void rp_cleanup_module(void);
module_init(rp_init);
@@ -1283,23 +1283,29 @@
return -ENXIO;
switch (cmd) {
- case RCKP_GET_STRUCT:
- if (copy_to_user(argp, info, sizeof (struct r_port)))
- ret = -EFAULT;
- break;
case RCKP_GET_CONFIG:
+ dev_warn_ratelimited(tty->dev,
+ "RCKP_GET_CONFIG option is deprecated\n");
ret = get_config(info, argp);
break;
case RCKP_SET_CONFIG:
+ dev_warn_ratelimited(tty->dev,
+ "RCKP_SET_CONFIG option is deprecated\n");
ret = set_config(tty, info, argp);
break;
case RCKP_GET_PORTS:
+ dev_warn_ratelimited(tty->dev,
+ "RCKP_GET_PORTS option is deprecated\n");
ret = get_ports(info, argp);
break;
case RCKP_RESET_RM2:
+ dev_warn_ratelimited(tty->dev,
+ "RCKP_RESET_RM2 option is deprecated\n");
ret = reset_rm2(info, argp);
break;
case RCKP_GET_VERSION:
+ dev_warn_ratelimited(tty->dev,
+ "RCKP_GET_VERSION option is deprecated\n");
ret = get_version(info, argp);
break;
default:
diff --git a/drivers/tty/rocket.h b/drivers/tty/rocket.h
index d056020..d62ed65 100644
--- a/drivers/tty/rocket.h
+++ b/drivers/tty/rocket.h
@@ -71,7 +71,6 @@
/*
* Rocketport ioctls -- "RP"
*/
-#define RCKP_GET_STRUCT 0x00525001
#define RCKP_GET_CONFIG 0x00525002
#define RCKP_SET_CONFIG 0x00525003
#define RCKP_GET_PORTS 0x00525004
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 1dbc835..46ae732 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Serial bus device driver configuration
#
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
index 0cbdb94..078417e 100644
--- a/drivers/tty/serdev/Makefile
+++ b/drivers/tty/serdev/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
serdev-objs := core.o
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index 9db93f5..a0ac16e 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -15,6 +15,7 @@
#include <linux/of_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/sched.h>
#include <linux/serdev.h>
#include <linux/slab.h>
@@ -216,6 +217,21 @@
}
EXPORT_SYMBOL_GPL(serdev_device_write_wakeup);
+/**
+ * serdev_device_write_buf() - write data asynchronously
+ * @serdev: serdev device
+ * @buf: data to be written
+ * @count: number of bytes to write
+ *
+ * Write data to the device asynchronously.
+ *
+ * Note that any accepted data has only been buffered by the controller; use
+ * serdev_device_wait_until_sent() to make sure the controller write buffer
+ * has actually been emptied.
+ *
+ * Return: The number of bytes written (less than count if not enough room in
+ * the write buffer), or a negative errno on errors.
+ */
int serdev_device_write_buf(struct serdev_device *serdev,
const unsigned char *buf, size_t count)
{
@@ -228,17 +244,42 @@
}
EXPORT_SYMBOL_GPL(serdev_device_write_buf);
+/**
+ * serdev_device_write() - write data synchronously
+ * @serdev: serdev device
+ * @buf: data to be written
+ * @count: number of bytes to write
+ * @timeout: timeout in jiffies, or 0 to wait indefinitely
+ *
+ * Write data to the device synchronously by repeatedly calling
+ * serdev_device_write() until the controller has accepted all data (unless
+ * interrupted by a timeout or a signal).
+ *
+ * Note that any accepted data has only been buffered by the controller; use
+ * serdev_device_wait_until_sent() to make sure the controller write buffer
+ * has actually been emptied.
+ *
+ * Note that this function depends on serdev_device_write_wakeup() being
+ * called in the serdev driver write_wakeup() callback.
+ *
+ * Return: The number of bytes written (less than count if interrupted),
+ * -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or
+ * a negative errno on errors.
+ */
int serdev_device_write(struct serdev_device *serdev,
const unsigned char *buf, size_t count,
- unsigned long timeout)
+ long timeout)
{
struct serdev_controller *ctrl = serdev->ctrl;
+ int written = 0;
int ret;
- if (!ctrl || !ctrl->ops->write_buf ||
- (timeout && !serdev->ops->write_wakeup))
+ if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup)
return -EINVAL;
+ if (timeout == 0)
+ timeout = MAX_SCHEDULE_TIMEOUT;
+
mutex_lock(&serdev->write_lock);
do {
reinit_completion(&serdev->write_comp);
@@ -247,14 +288,29 @@
if (ret < 0)
break;
+ written += ret;
buf += ret;
count -= ret;
- } while (count &&
- (timeout = wait_for_completion_timeout(&serdev->write_comp,
- timeout)));
+ if (count == 0)
+ break;
+
+ timeout = wait_for_completion_interruptible_timeout(&serdev->write_comp,
+ timeout);
+ } while (timeout > 0);
mutex_unlock(&serdev->write_lock);
- return ret < 0 ? ret : (count ? -ETIMEDOUT : 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (timeout <= 0 && written == 0) {
+ if (timeout == -ERESTARTSYS)
+ return -ERESTARTSYS;
+ else
+ return -ETIMEDOUT;
+ }
+
+ return written;
}
EXPORT_SYMBOL_GPL(serdev_device_write);
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
index fa16729..d1cdd2a 100644
--- a/drivers/tty/serdev/serdev-ttyport.c
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -233,7 +233,7 @@
if (!tty->ops->tiocmget)
return -ENOTSUPP;
- return tty->driver->ops->tiocmget(tty);
+ return tty->ops->tiocmget(tty);
}
static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear)
@@ -244,7 +244,7 @@
if (!tty->ops->tiocmset)
return -ENOTSUPP;
- return tty->driver->ops->tiocmset(tty, set, clear);
+ return tty->ops->tiocmset(tty, set, clear);
}
static const struct serdev_controller_ops ctrl_ops = {
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index ebfb0bd..33ad9d6 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -11,6 +11,8 @@
#include <linux/serial_reg.h>
#include <linux/dmaengine.h>
+#include "../serial_mctrl_gpio.h"
+
struct uart_8250_dma {
int (*tx_dma)(struct uart_8250_port *p);
int (*rx_dma)(struct uart_8250_port *p);
@@ -128,6 +130,24 @@
up->dl_write(up, value);
}
+static inline bool serial8250_set_THRI(struct uart_8250_port *up)
+{
+ if (up->ier & UART_IER_THRI)
+ return false;
+ up->ier |= UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ return true;
+}
+
+static inline bool serial8250_clear_THRI(struct uart_8250_port *up)
+{
+ if (!(up->ier & UART_IER_THRI))
+ return false;
+ up->ier &= ~UART_IER_THRI;
+ serial_out(up, UART_IER, up->ier);
+ return true;
+}
+
struct uart_8250_port *serial8250_get_port(int line);
void serial8250_rpm_get(struct uart_8250_port *p);
@@ -139,14 +159,82 @@
int serial8250_em485_init(struct uart_8250_port *p);
void serial8250_em485_destroy(struct uart_8250_port *p);
+/* MCR <-> TIOCM conversion */
+static inline int serial8250_TIOCM_to_MCR(int tiocm)
+{
+ int mcr = 0;
+
+ if (tiocm & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (tiocm & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (tiocm & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (tiocm & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (tiocm & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ return mcr;
+}
+
+static inline int serial8250_MCR_to_TIOCM(int mcr)
+{
+ int tiocm = 0;
+
+ if (mcr & UART_MCR_RTS)
+ tiocm |= TIOCM_RTS;
+ if (mcr & UART_MCR_DTR)
+ tiocm |= TIOCM_DTR;
+ if (mcr & UART_MCR_OUT1)
+ tiocm |= TIOCM_OUT1;
+ if (mcr & UART_MCR_OUT2)
+ tiocm |= TIOCM_OUT2;
+ if (mcr & UART_MCR_LOOP)
+ tiocm |= TIOCM_LOOP;
+
+ return tiocm;
+}
+
+/* MSR <-> TIOCM conversion */
+static inline int serial8250_MSR_to_TIOCM(int msr)
+{
+ int tiocm = 0;
+
+ if (msr & UART_MSR_DCD)
+ tiocm |= TIOCM_CAR;
+ if (msr & UART_MSR_RI)
+ tiocm |= TIOCM_RNG;
+ if (msr & UART_MSR_DSR)
+ tiocm |= TIOCM_DSR;
+ if (msr & UART_MSR_CTS)
+ tiocm |= TIOCM_CTS;
+
+ return tiocm;
+}
+
static inline void serial8250_out_MCR(struct uart_8250_port *up, int value)
{
serial_out(up, UART_MCR, value);
+
+ if (up->gpios)
+ mctrl_gpio_set(up->gpios, serial8250_MCR_to_TIOCM(value));
}
static inline int serial8250_in_MCR(struct uart_8250_port *up)
{
- return serial_in(up, UART_MCR);
+ int mctrl;
+
+ mctrl = serial_in(up, UART_MCR);
+
+ if (up->gpios) {
+ unsigned int mctrl_gpio = 0;
+
+ mctrl_gpio = mctrl_gpio_get_outputs(up->gpios, &mctrl_gpio);
+ mctrl |= serial8250_TIOCM_to_MCR(mctrl_gpio);
+ }
+
+ return mctrl;
}
#if defined(__alpha__) && !defined(CONFIG_PCI)
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c
index 435bec4..0438d9a 100644
--- a/drivers/tty/serial/8250/8250_aspeed_vuart.c
+++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c
@@ -5,6 +5,10 @@
* Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
* Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
*/
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of_address.h>
@@ -293,7 +297,7 @@
if (lsr & UART_LSR_THRE)
serial8250_tx_chars(up);
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_unlock_and_check_sysrq(port, flags);
return 1;
}
diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c
index bd53661..8ce700c 100644
--- a/drivers/tty/serial/8250/8250_bcm2835aux.c
+++ b/drivers/tty/serial/8250/8250_bcm2835aux.c
@@ -56,10 +56,8 @@
/* get the interrupt */
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "irq not found - %i", ret);
+ if (ret < 0)
return ret;
- }
data->uart.port.irq = ret;
/* map the main registers */
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 8fe3d0e..e682390 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -14,6 +14,7 @@
* serial8250_register_8250_port() ports
*/
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
@@ -130,12 +131,8 @@
l = l->next;
- if (l == i->head && pass_counter++ > PASS_LIMIT) {
- /* If we hit this, we're dead. */
- printk_ratelimited(KERN_ERR
- "serial8250: too much work for irq%d\n", irq);
+ if (l == i->head && pass_counter++ > PASS_LIMIT)
break;
- }
} while (l != end);
spin_unlock(&i->lock);
@@ -946,6 +943,21 @@
return NULL;
}
+static void serial_8250_overrun_backoff_work(struct work_struct *work)
+{
+ struct uart_8250_port *up =
+ container_of(to_delayed_work(work), struct uart_8250_port,
+ overrun_backoff);
+ struct uart_port *port = &up->port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ up->ier |= UART_IER_RLSI | UART_IER_RDI;
+ up->port.read_status_mask |= UART_LSR_DR;
+ serial_out(up, UART_IER, up->ier);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
/**
* serial8250_register_8250_port - register a serial port
* @up: serial port template
@@ -971,6 +983,8 @@
uart = serial8250_find_match_or_unused(&up->port);
if (uart && uart->port.type != PORT_8250_CIR) {
+ struct mctrl_gpios *gpios;
+
if (uart->port.dev)
uart_remove_one_port(&serial8250_reg, &uart->port);
@@ -1005,6 +1019,20 @@
if (up->port.flags & UPF_FIXED_TYPE)
uart->port.type = up->port.type;
+ /*
+ * Only call mctrl_gpio_init(), if the device has no ACPI
+ * companion device
+ */
+ if (!has_acpi_companion(uart->port.dev)) {
+ gpios = mctrl_gpio_init(&uart->port, 0);
+ if (IS_ERR(gpios)) {
+ ret = PTR_ERR(gpios);
+ goto out_unlock;
+ } else {
+ uart->gpios = gpios;
+ }
+ }
+
serial8250_set_defaults(uart);
/* Possibly override default I/O functions. */
@@ -1059,7 +1087,19 @@
ret = 0;
}
+
+ /* Initialise interrupt backoff work if required */
+ if (up->overrun_backoff_time_ms > 0) {
+ uart->overrun_backoff_time_ms =
+ up->overrun_backoff_time_ms;
+ INIT_DELAYED_WORK(&uart->overrun_backoff,
+ serial_8250_overrun_backoff_work);
+ } else {
+ uart->overrun_backoff_time_ms = 0;
+ }
}
+
+out_unlock:
mutex_unlock(&serial_mutex);
return ret;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index bfa1a85..890fa7d 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -34,10 +34,8 @@
uart_write_wakeup(&p->port);
ret = serial8250_tx_dma(p);
- if (ret) {
- p->ier |= UART_IER_THRI;
- serial_port_out(&p->port, UART_IER, p->ier);
- }
+ if (ret)
+ serial8250_set_THRI(p);
spin_unlock_irqrestore(&p->port.lock, flags);
}
@@ -100,10 +98,7 @@
dma_async_issue_pending(dma->txchan);
if (dma->tx_err) {
dma->tx_err = 0;
- if (p->ier & UART_IER_THRI) {
- p->ier &= ~UART_IER_THRI;
- serial_out(p, UART_IER, p->ier);
- }
+ serial8250_clear_THRI(p);
}
return 0;
err:
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index d31b975..1c72fdc 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -27,66 +27,36 @@
#include <asm/byteorder.h>
-#include "8250.h"
+#include "8250_dwlib.h"
/* Offsets for the DesignWare specific registers */
#define DW_UART_USR 0x1f /* UART Status Register */
-#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
-#define DW_UART_CPR 0xf4 /* Component Parameter Register */
-#define DW_UART_UCV 0xf8 /* UART Component Version */
-
-/* Component Parameter Register bits */
-#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
-#define DW_UART_CPR_AFCE_MODE (1 << 4)
-#define DW_UART_CPR_THRE_MODE (1 << 5)
-#define DW_UART_CPR_SIR_MODE (1 << 6)
-#define DW_UART_CPR_SIR_LP_MODE (1 << 7)
-#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
-#define DW_UART_CPR_FIFO_ACCESS (1 << 9)
-#define DW_UART_CPR_FIFO_STAT (1 << 10)
-#define DW_UART_CPR_SHADOW (1 << 11)
-#define DW_UART_CPR_ENCODED_PARMS (1 << 12)
-#define DW_UART_CPR_DMA_EXTRA (1 << 13)
-#define DW_UART_CPR_FIFO_MODE (0xff << 16)
-/* Helper for fifo size calculation */
-#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
/* DesignWare specific register fields */
#define DW_UART_MCR_SIRE BIT(6)
struct dw8250_data {
+ struct dw8250_port_data data;
+
u8 usr_reg;
- u8 dlf_size;
- int line;
int msr_mask_on;
int msr_mask_off;
struct clk *clk;
struct clk *pclk;
struct reset_control *rst;
- struct uart_8250_dma dma;
unsigned int skip_autocfg:1;
unsigned int uart_16550_compatible:1;
};
-static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
+static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data)
{
- if (p->iotype == UPIO_MEM32BE)
- return ioread32be(p->membase + offset);
- return readl(p->membase + offset);
-}
-
-static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
-{
- if (p->iotype == UPIO_MEM32BE)
- iowrite32be(reg, p->membase + offset);
- else
- writel(reg, p->membase + offset);
+ return container_of(data, struct dw8250_data, data);
}
static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
/* Override any modem control signals if needed */
if (offset == UART_MSR) {
@@ -160,7 +130,7 @@
static void dw8250_serial_out38x(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
/* Allow the TX to drain before we reconfigure */
if (offset == UART_LCR)
@@ -175,7 +145,7 @@
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
writeb(value, p->membase + (offset << p->regshift));
@@ -202,7 +172,7 @@
static void dw8250_serial_outq(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
value &= 0xff;
__raw_writeq(value, p->membase + (offset << p->regshift));
@@ -216,7 +186,7 @@
static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
writel(value, p->membase + (offset << p->regshift));
@@ -233,7 +203,7 @@
static void dw8250_serial_out32be(struct uart_port *p, int offset, int value)
{
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
iowrite32be(value, p->membase + (offset << p->regshift));
@@ -252,7 +222,7 @@
static int dw8250_handle_irq(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
unsigned int iir = p->serial_in(p, UART_IIR);
unsigned int status;
unsigned long flags;
@@ -306,7 +276,7 @@
struct ktermios *old)
{
unsigned int baud = tty_termios_baud_rate(termios);
- struct dw8250_data *d = p->private_data;
+ struct dw8250_data *d = to_dw8250_data(p->private_data);
long rate;
int ret;
@@ -365,38 +335,7 @@
static bool dw8250_idma_filter(struct dma_chan *chan, void *param)
{
- return param == chan->device->dev->parent;
-}
-
-/*
- * divisor = div(I) + div(F)
- * "I" means integer, "F" means fractional
- * quot = div(I) = clk / (16 * baud)
- * frac = div(F) * 2^dlf_size
- *
- * let rem = clk % (16 * baud)
- * we have: div(F) * (16 * baud) = rem
- * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
- */
-static unsigned int dw8250_get_divisor(struct uart_port *p,
- unsigned int baud,
- unsigned int *frac)
-{
- unsigned int quot, rem, base_baud = baud * 16;
- struct dw8250_data *d = p->private_data;
-
- quot = p->uartclk / base_baud;
- rem = p->uartclk % base_baud;
- *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
-
- return quot;
-}
-
-static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
- unsigned int quot, unsigned int quot_frac)
-{
- dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
- serial8250_do_set_divisor(p, baud, quot, quot_frac);
+ return param == chan->device->dev;
}
static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
@@ -434,68 +373,21 @@
data->uart_16550_compatible = true;
}
- /* Platforms with iDMA */
+ /* Platforms with iDMA 64-bit */
if (platform_get_resource_byname(to_platform_device(p->dev),
IORESOURCE_MEM, "lpss_priv")) {
- data->dma.rx_param = p->dev->parent;
- data->dma.tx_param = p->dev->parent;
- data->dma.fn = dw8250_idma_filter;
+ data->data.dma.rx_param = p->dev->parent;
+ data->data.dma.tx_param = p->dev->parent;
+ data->data.dma.fn = dw8250_idma_filter;
}
}
-static void dw8250_setup_port(struct uart_port *p)
-{
- struct uart_8250_port *up = up_to_u8250p(p);
- u32 reg;
-
- /*
- * If the Component Version Register returns zero, we know that
- * ADDITIONAL_FEATURES are not enabled. No need to go any further.
- */
- reg = dw8250_readl_ext(p, DW_UART_UCV);
- if (!reg)
- return;
-
- dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
- (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
-
- dw8250_writel_ext(p, DW_UART_DLF, ~0U);
- reg = dw8250_readl_ext(p, DW_UART_DLF);
- dw8250_writel_ext(p, DW_UART_DLF, 0);
-
- if (reg) {
- struct dw8250_data *d = p->private_data;
-
- d->dlf_size = fls(reg);
- p->get_divisor = dw8250_get_divisor;
- p->set_divisor = dw8250_set_divisor;
- }
-
- reg = dw8250_readl_ext(p, DW_UART_CPR);
- if (!reg)
- return;
-
- /* Select the type based on fifo */
- if (reg & DW_UART_CPR_FIFO_MODE) {
- p->type = PORT_16550A;
- p->flags |= UPF_FIXED_TYPE;
- p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
- up->capabilities = UART_CAP_FIFO;
- }
-
- if (reg & DW_UART_CPR_AFCE_MODE)
- up->capabilities |= UART_CAP_AFE;
-
- if (reg & DW_UART_CPR_SIR_MODE)
- up->capabilities |= UART_CAP_IRDA;
-}
-
static int dw8250_probe(struct platform_device *pdev)
{
- struct uart_8250_port uart = {};
+ struct uart_8250_port uart = {}, *up = &uart;
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
- struct uart_port *p = &uart.port;
+ struct uart_port *p = &up->port;
struct device *dev = &pdev->dev;
struct dw8250_data *data;
int err;
@@ -534,9 +426,9 @@
if (!data)
return -ENOMEM;
- data->dma.fn = dw8250_fallback_dma_filter;
+ data->data.dma.fn = dw8250_fallback_dma_filter;
data->usr_reg = DW_UART_USR;
- p->private_data = data;
+ p->private_data = &data->data;
data->uart_16550_compatible = device_property_read_bool(dev,
"snps,uart-16550-compatible");
@@ -632,14 +524,14 @@
/* If we have a valid fifosize, try hooking up DMA */
if (p->fifosize) {
- data->dma.rxconf.src_maxburst = p->fifosize / 4;
- data->dma.txconf.dst_maxburst = p->fifosize / 4;
- uart.dma = &data->dma;
+ data->data.dma.rxconf.src_maxburst = p->fifosize / 4;
+ data->data.dma.txconf.dst_maxburst = p->fifosize / 4;
+ up->dma = &data->data.dma;
}
- data->line = serial8250_register_8250_port(&uart);
- if (data->line < 0) {
- err = data->line;
+ data->data.line = serial8250_register_8250_port(up);
+ if (data->data.line < 0) {
+ err = data->data.line;
goto err_reset;
}
@@ -667,10 +559,11 @@
static int dw8250_remove(struct platform_device *pdev)
{
struct dw8250_data *data = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
- pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_sync(dev);
- serial8250_unregister_port(data->line);
+ serial8250_unregister_port(data->data.line);
reset_control_assert(data->rst);
@@ -680,8 +573,8 @@
if (!IS_ERR(data->clk))
clk_disable_unprepare(data->clk);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_put_noidle(&pdev->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
return 0;
}
@@ -691,7 +584,7 @@
{
struct dw8250_data *data = dev_get_drvdata(dev);
- serial8250_suspend_port(data->line);
+ serial8250_suspend_port(data->data.line);
return 0;
}
@@ -700,7 +593,7 @@
{
struct dw8250_data *data = dev_get_drvdata(dev);
- serial8250_resume_port(data->line);
+ serial8250_resume_port(data->data.line);
return 0;
}
diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
new file mode 100644
index 0000000..6d6a78e
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Synopsys DesignWare 8250 library. */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+
+#include "8250_dwlib.h"
+
+/* Offsets for the DesignWare specific registers */
+#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */
+#define DW_UART_CPR 0xf4 /* Component Parameter Register */
+#define DW_UART_UCV 0xf8 /* UART Component Version */
+
+/* Component Parameter Register bits */
+#define DW_UART_CPR_ABP_DATA_WIDTH (3 << 0)
+#define DW_UART_CPR_AFCE_MODE (1 << 4)
+#define DW_UART_CPR_THRE_MODE (1 << 5)
+#define DW_UART_CPR_SIR_MODE (1 << 6)
+#define DW_UART_CPR_SIR_LP_MODE (1 << 7)
+#define DW_UART_CPR_ADDITIONAL_FEATURES (1 << 8)
+#define DW_UART_CPR_FIFO_ACCESS (1 << 9)
+#define DW_UART_CPR_FIFO_STAT (1 << 10)
+#define DW_UART_CPR_SHADOW (1 << 11)
+#define DW_UART_CPR_ENCODED_PARMS (1 << 12)
+#define DW_UART_CPR_DMA_EXTRA (1 << 13)
+#define DW_UART_CPR_FIFO_MODE (0xff << 16)
+
+/* Helper for FIFO size calculation */
+#define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16)
+
+static inline u32 dw8250_readl_ext(struct uart_port *p, int offset)
+{
+ if (p->iotype == UPIO_MEM32BE)
+ return ioread32be(p->membase + offset);
+ return readl(p->membase + offset);
+}
+
+static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg)
+{
+ if (p->iotype == UPIO_MEM32BE)
+ iowrite32be(reg, p->membase + offset);
+ else
+ writel(reg, p->membase + offset);
+}
+
+/*
+ * divisor = div(I) + div(F)
+ * "I" means integer, "F" means fractional
+ * quot = div(I) = clk / (16 * baud)
+ * frac = div(F) * 2^dlf_size
+ *
+ * let rem = clk % (16 * baud)
+ * we have: div(F) * (16 * baud) = rem
+ * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud)
+ */
+static unsigned int dw8250_get_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int *frac)
+{
+ unsigned int quot, rem, base_baud = baud * 16;
+ struct dw8250_port_data *d = p->private_data;
+
+ quot = p->uartclk / base_baud;
+ rem = p->uartclk % base_baud;
+ *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud);
+
+ return quot;
+}
+
+static void dw8250_set_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int quot, unsigned int quot_frac)
+{
+ dw8250_writel_ext(p, DW_UART_DLF, quot_frac);
+ serial8250_do_set_divisor(p, baud, quot, quot_frac);
+}
+
+void dw8250_setup_port(struct uart_port *p)
+{
+ struct uart_8250_port *up = up_to_u8250p(p);
+ u32 reg;
+
+ /*
+ * If the Component Version Register returns zero, we know that
+ * ADDITIONAL_FEATURES are not enabled. No need to go any further.
+ */
+ reg = dw8250_readl_ext(p, DW_UART_UCV);
+ if (!reg)
+ return;
+
+ dev_dbg(p->dev, "Designware UART version %c.%c%c\n",
+ (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff);
+
+ dw8250_writel_ext(p, DW_UART_DLF, ~0U);
+ reg = dw8250_readl_ext(p, DW_UART_DLF);
+ dw8250_writel_ext(p, DW_UART_DLF, 0);
+
+ if (reg) {
+ struct dw8250_port_data *d = p->private_data;
+
+ d->dlf_size = fls(reg);
+ p->get_divisor = dw8250_get_divisor;
+ p->set_divisor = dw8250_set_divisor;
+ }
+
+ reg = dw8250_readl_ext(p, DW_UART_CPR);
+ if (!reg)
+ return;
+
+ /* Select the type based on FIFO */
+ if (reg & DW_UART_CPR_FIFO_MODE) {
+ p->type = PORT_16550A;
+ p->flags |= UPF_FIXED_TYPE;
+ p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+ up->capabilities = UART_CAP_FIFO;
+ }
+
+ if (reg & DW_UART_CPR_AFCE_MODE)
+ up->capabilities |= UART_CAP_AFE;
+
+ if (reg & DW_UART_CPR_SIR_MODE)
+ up->capabilities |= UART_CAP_IRDA;
+}
+EXPORT_SYMBOL_GPL(dw8250_setup_port);
diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h
new file mode 100644
index 0000000..87a4db2
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_dwlib.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Synopsys DesignWare 8250 library header file. */
+
+#include <linux/types.h>
+
+#include "8250.h"
+
+struct dw8250_port_data {
+ /* Port properties */
+ int line;
+
+ /* DMA operations */
+ struct uart_8250_dma dma;
+
+ /* Hardware configuration */
+ u8 dlf_size;
+};
+
+void dw8250_setup_port(struct uart_port *p);
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index 0089aa3..597eb9d 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -19,6 +19,7 @@
#include <linux/string.h>
#include <linux/tty.h>
#include <linux/8250_pci.h>
+#include <linux/delay.h>
#include <asm/byteorder.h>
@@ -36,6 +37,8 @@
#define UART_EXAR_INT0 0x80
#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */
+#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
+#define UART_EXAR_DVID 0x8d /* Device identification */
#define UART_EXAR_FCTR 0x08 /* Feature Control Register */
#define UART_FCTR_EXAR_IRDA 0x10 /* IrDa data encode select */
@@ -127,18 +130,95 @@
int line[0];
};
+static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
+{
+ /*
+ * Exar UARTs have a SLEEP register that enables or disables each UART
+ * to enter sleep mode separately. On the XR17V35x the register
+ * is accessible to each UART at the UART_EXAR_SLEEP offset, but
+ * the UART channel may only write to the corresponding bit.
+ */
+ serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0);
+}
+
+/*
+ * XR17V35x UARTs have an extra fractional divisor register (DLD)
+ * Calculate divisor with extra 4-bit fractional portion
+ */
+static unsigned int xr17v35x_get_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int *frac)
+{
+ unsigned int quot_16;
+
+ quot_16 = DIV_ROUND_CLOSEST(p->uartclk, baud);
+ *frac = quot_16 & 0x0f;
+
+ return quot_16 >> 4;
+}
+
+static void xr17v35x_set_divisor(struct uart_port *p, unsigned int baud,
+ unsigned int quot, unsigned int quot_frac)
+{
+ serial8250_do_set_divisor(p, baud, quot, quot_frac);
+
+ /* Preserve bits not related to baudrate; DLD[7:4]. */
+ quot_frac |= serial_port_in(p, 0x2) & 0xf0;
+ serial_port_out(p, 0x2, quot_frac);
+}
+
+static void exar_shutdown(struct uart_port *port)
+{
+ unsigned char lsr;
+ bool tx_complete = 0;
+ struct uart_8250_port *up = up_to_u8250p(port);
+ struct circ_buf *xmit = &port->state->xmit;
+ int i = 0;
+
+ do {
+ lsr = serial_in(up, UART_LSR);
+ if (lsr & (UART_LSR_TEMT | UART_LSR_THRE))
+ tx_complete = 1;
+ else
+ tx_complete = 0;
+ usleep_range(1000, 1100);
+ } while (!uart_circ_empty(xmit) && !tx_complete && i++ < 1000);
+
+ serial8250_do_shutdown(port);
+}
+
static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev,
int idx, unsigned int offset,
struct uart_8250_port *port)
{
const struct exar8250_board *board = priv->board;
unsigned int bar = 0;
+ unsigned char status;
port->port.iotype = UPIO_MEM;
port->port.mapbase = pci_resource_start(pcidev, bar) + offset;
port->port.membase = priv->virt + offset;
port->port.regshift = board->reg_shift;
+ /*
+ * XR17V35x UARTs have an extra divisor register, DLD that gets enabled
+ * with when DLAB is set which will cause the device to incorrectly match
+ * and assign port type to PORT_16650. The EFR for this UART is found
+ * at offset 0x09. Instead check the Deice ID (DVID) register
+ * for a 2, 4 or 8 port UART.
+ */
+ status = readb(port->port.membase + UART_EXAR_DVID);
+ if (status == 0x82 || status == 0x84 || status == 0x88) {
+ port->port.type = PORT_XR17V35X;
+
+ port->port.get_divisor = xr17v35x_get_divisor;
+ port->port.set_divisor = xr17v35x_set_divisor;
+ } else {
+ port->port.type = PORT_XR17D15X;
+ }
+
+ port->port.pm = exar_pm;
+ port->port.shutdown = exar_shutdown;
+
return 0;
}
@@ -361,12 +441,15 @@
.register_gpio = iot2040_register_gpio,
};
+/*
+ * For SIMATIC IOT2000, only IOT2040 and its variants have the Exar device,
+ * IOT2020 doesn't have. Therefore it is sufficient to match on the common
+ * board name after the device was found.
+ */
static const struct dmi_system_id exar_platforms[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
- DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
- "6ES7647-0AA00-1YA2"),
},
.driver_data = (void *)&iot2040_platform,
},
@@ -431,6 +514,16 @@
port->port.private_data = NULL;
}
+static inline void exar_misc_clear(struct exar8250 *priv)
+{
+ /* Clear all PCI interrupts by reading INT0. No effect on IIR */
+ readb(priv->virt + UART_EXAR_INT0);
+
+ /* Clear INT0 for Expansion Interface slave ports, too */
+ if (priv->board->num_ports > 8)
+ readb(priv->virt + 0x2000 + UART_EXAR_INT0);
+}
+
/*
* These Exar UARTs have an extra interrupt indicator that could fire for a
* few interrupts that are not presented/cleared through IIR. One of which is
@@ -442,14 +535,7 @@
*/
static irqreturn_t exar_misc_handler(int irq, void *data)
{
- struct exar8250 *priv = data;
-
- /* Clear all PCI interrupts by reading INT0. No effect on IIR */
- readb(priv->virt + UART_EXAR_INT0);
-
- /* Clear INT0 for Expansion Interface slave ports, too */
- if (priv->board->num_ports > 8)
- readb(priv->virt + 0x2000 + UART_EXAR_INT0);
+ exar_misc_clear(data);
return IRQ_HANDLED;
}
@@ -475,9 +561,7 @@
nr_ports = board->num_ports ? board->num_ports : pcidev->device & 0x0f;
- priv = devm_kzalloc(&pcidev->dev, sizeof(*priv) +
- sizeof(unsigned int) * nr_ports,
- GFP_KERNEL);
+ priv = devm_kzalloc(&pcidev->dev, struct_size(priv, line, nr_ports), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -493,8 +577,7 @@
return rc;
memset(&uart, 0, sizeof(uart));
- uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ
- | UPF_EXAR_EFR;
+ uart.port.flags = UPF_SHARE_IRQ | UPF_EXAR_EFR | UPF_FIXED_TYPE | UPF_FIXED_PORT;
uart.port.irq = pci_irq_vector(pcidev, 0);
uart.port.dev = &pcidev->dev;
@@ -503,6 +586,9 @@
if (rc)
return rc;
+ /* Clear interrupts */
+ exar_misc_clear(priv);
+
for (i = 0; i < nr_ports && i < maxnr; i++) {
rc = board->setup(priv, pcidev, &uart, i);
if (rc) {
@@ -558,10 +644,11 @@
static int __maybe_unused exar_resume(struct device *dev)
{
- struct pci_dev *pcidev = to_pci_dev(dev);
- struct exar8250 *priv = pci_get_drvdata(pcidev);
+ struct exar8250 *priv = dev_get_drvdata(dev);
unsigned int i;
+ exar_misc_clear(priv);
+
for (i = 0; i < priv->nr; i++)
if (priv->line[i] >= 0)
serial8250_resume_port(priv->line[i]);
diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c
index 79a4958..31c91c2 100644
--- a/drivers/tty/serial/8250/8250_fintek.c
+++ b/drivers/tty/serial/8250/8250_fintek.c
@@ -303,8 +303,9 @@
}
}
-void fintek_8250_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
+static void fintek_8250_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
{
struct fintek_8250 *pdata = port->private_data;
unsigned int baud = tty_termios_baud_rate(termios);
diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c
index 6640a4c..aa0e216 100644
--- a/drivers/tty/serial/8250/8250_fsl.c
+++ b/drivers/tty/serial/8250/8250_fsl.c
@@ -1,4 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/serial_reg.h>
#include <linux/serial_8250.h>
@@ -45,8 +49,29 @@
lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
- if (lsr & (UART_LSR_DR | UART_LSR_BI))
+ /* Process incoming characters first */
+ if ((lsr & (UART_LSR_DR | UART_LSR_BI)) &&
+ (up->ier & (UART_IER_RLSI | UART_IER_RDI))) {
lsr = serial8250_rx_chars(up, lsr);
+ }
+
+ /* Stop processing interrupts on input overrun */
+ if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
+ unsigned long delay;
+
+ up->ier = port->serial_in(port, UART_IER);
+ if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
+ port->ops->stop_rx(port);
+ } else {
+ /* Keep restarting the timer until
+ * the input overrun subsides.
+ */
+ cancel_delayed_work(&up->overrun_backoff);
+ }
+
+ delay = msecs_to_jiffies(up->overrun_backoff_time_ms);
+ schedule_delayed_work(&up->overrun_backoff, delay);
+ }
serial8250_modem_status(up);
@@ -54,7 +79,7 @@
serial8250_tx_chars(up);
up->lsr_saved_flags = orig_lsr;
- spin_unlock_irqrestore(&up->port.lock, flags);
+ uart_unlock_and_check_sysrq(&up->port, flags);
return 1;
}
EXPORT_SYMBOL_GPL(fsl8250_handle_irq);
diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c
index 15a8c8d..424c07c 100644
--- a/drivers/tty/serial/8250/8250_ingenic.c
+++ b/drivers/tty/serial/8250/8250_ingenic.c
@@ -129,22 +129,21 @@
return 0;
}
-EARLYCON_DECLARE(jz4740_uart, ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4740_uart, "ingenic,jz4740-uart",
ingenic_early_console_setup);
-EARLYCON_DECLARE(jz4770_uart, ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4770_uart, "ingenic,jz4770-uart",
ingenic_early_console_setup);
-EARLYCON_DECLARE(jz4775_uart, ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4775_uart, "ingenic,jz4775-uart",
ingenic_early_console_setup);
-EARLYCON_DECLARE(jz4780_uart, ingenic_early_console_setup);
OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart",
ingenic_early_console_setup);
+OF_EARLYCON_DECLARE(x1000_uart, "ingenic,x1000-uart",
+ ingenic_early_console_setup);
+
static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
{
int ier;
@@ -328,12 +327,18 @@
.fifosize = 64,
};
+static const struct ingenic_uart_config x1000_uart_config = {
+ .tx_loadsz = 32,
+ .fifosize = 64,
+};
+
static const struct of_device_id of_match[] = {
{ .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config },
{ .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4770-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config },
{ .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config },
+ { .compatible = "ingenic,x1000-uart", .data = &x1000_uart_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_match);
diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c
index eddf119..570e25d 100644
--- a/drivers/tty/serial/8250/8250_lpc18xx.c
+++ b/drivers/tty/serial/8250/8250_lpc18xx.c
@@ -106,10 +106,8 @@
int irq, ret;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "irq not found");
+ if (irq < 0)
return irq;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c
index 98dbc79..5f72ef3 100644
--- a/drivers/tty/serial/8250/8250_lpss.c
+++ b/drivers/tty/serial/8250/8250_lpss.c
@@ -14,7 +14,7 @@
#include <linux/dmaengine.h>
#include <linux/dma/dw.h>
-#include "8250.h"
+#include "8250_dwlib.h"
#define PCI_DEVICE_ID_INTEL_QRK_UARTx 0x0936
@@ -24,6 +24,13 @@
#define PCI_DEVICE_ID_INTEL_BSW_UART1 0x228a
#define PCI_DEVICE_ID_INTEL_BSW_UART2 0x228c
+#define PCI_DEVICE_ID_INTEL_EHL_UART0 0x4b96
+#define PCI_DEVICE_ID_INTEL_EHL_UART1 0x4b97
+#define PCI_DEVICE_ID_INTEL_EHL_UART2 0x4b98
+#define PCI_DEVICE_ID_INTEL_EHL_UART3 0x4b99
+#define PCI_DEVICE_ID_INTEL_EHL_UART4 0x4b9a
+#define PCI_DEVICE_ID_INTEL_EHL_UART5 0x4b9b
+
#define PCI_DEVICE_ID_INTEL_BDW_UART1 0x9ce3
#define PCI_DEVICE_ID_INTEL_BDW_UART2 0x9ce4
@@ -48,21 +55,25 @@
};
struct lpss8250 {
- int line;
+ struct dw8250_port_data data;
struct lpss8250_board *board;
/* DMA parameters */
- struct uart_8250_dma dma;
struct dw_dma_chip dma_chip;
struct dw_dma_slave dma_param;
u8 dma_maxburst;
};
+static inline struct lpss8250 *to_lpss8250(struct dw8250_port_data *data)
+{
+ return container_of(data, struct lpss8250, data);
+}
+
static void byt_set_termios(struct uart_port *p, struct ktermios *termios,
struct ktermios *old)
{
unsigned int baud = tty_termios_baud_rate(termios);
- struct lpss8250 *lpss = p->private_data;
+ struct lpss8250 *lpss = to_lpss8250(p->private_data);
unsigned long fref = lpss->board->freq, fuart = baud * 16;
unsigned long w = BIT(15) - 1;
unsigned long m, n;
@@ -109,7 +120,6 @@
static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port)
{
struct dw_dma_slave *param = &lpss->dma_param;
- struct uart_8250_port *up = up_to_u8250p(port);
struct pci_dev *pdev = to_pci_dev(port->dev);
unsigned int dma_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
struct pci_dev *dma_dev = pci_get_slot(pdev->bus, dma_devfn);
@@ -135,10 +145,6 @@
param->m_master = 0;
param->p_master = 1;
- /* TODO: Detect FIFO size automaticaly for DesignWare 8250 */
- port->fifosize = 64;
- up->tx_loadsz = 64;
-
lpss->dma_maxburst = 16;
port->set_termios = byt_set_termios;
@@ -153,7 +159,6 @@
#ifdef CONFIG_SERIAL_8250_DMA
static const struct dw_dma_platform_data qrk_serial_dma_pdata = {
.nr_channels = 2,
- .is_private = true,
.chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
.chan_priority = CHAN_PRIORITY_ASCENDING,
.block_size = 4095,
@@ -164,16 +169,19 @@
static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port)
{
- struct uart_8250_dma *dma = &lpss->dma;
+ struct uart_8250_dma *dma = &lpss->data.dma;
struct dw_dma_chip *chip = &lpss->dma_chip;
struct dw_dma_slave *param = &lpss->dma_param;
struct pci_dev *pdev = to_pci_dev(port->dev);
int ret;
+ chip->pdata = &qrk_serial_dma_pdata;
chip->dev = &pdev->dev;
+ chip->id = pdev->devfn;
chip->irq = pci_irq_vector(pdev, 0);
chip->regs = pci_ioremap_bar(pdev, 1);
- chip->pdata = &qrk_serial_dma_pdata;
+ if (!chip->regs)
+ return;
/* Falling back to PIO mode if DMA probing fails */
ret = dw_dma_probe(chip);
@@ -196,11 +204,15 @@
static void qrk_serial_exit_dma(struct lpss8250 *lpss)
{
+ struct dw_dma_chip *chip = &lpss->dma_chip;
struct dw_dma_slave *param = &lpss->dma_param;
if (!param->dma_dev)
return;
- dw_dma_remove(&lpss->dma_chip);
+
+ dw_dma_remove(chip);
+
+ pci_iounmap(to_pci_dev(chip->dev), chip->regs);
}
#else /* CONFIG_SERIAL_8250_DMA */
static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) {}
@@ -242,7 +254,7 @@
static int lpss8250_dma_setup(struct lpss8250 *lpss, struct uart_8250_port *port)
{
- struct uart_8250_dma *dma = &lpss->dma;
+ struct uart_8250_dma *dma = &lpss->data.dma;
struct dw_dma_slave *rx_param, *tx_param;
struct device *dev = port->port.dev;
@@ -291,7 +303,7 @@
uart.port.dev = &pdev->dev;
uart.port.irq = pdev->irq;
- uart.port.private_data = lpss;
+ uart.port.private_data = &lpss->data;
uart.port.type = PORT_16550A;
uart.port.iotype = UPIO_MEM;
uart.port.regshift = 2;
@@ -307,6 +319,8 @@
if (ret)
return ret;
+ dw8250_setup_port(&uart.port);
+
ret = lpss8250_dma_setup(lpss, &uart);
if (ret)
goto err_exit;
@@ -315,7 +329,7 @@
if (ret < 0)
goto err_exit;
- lpss->line = ret;
+ lpss->data.line = ret;
pci_set_drvdata(pdev, lpss);
return 0;
@@ -330,7 +344,7 @@
{
struct lpss8250 *lpss = pci_get_drvdata(pdev);
- serial8250_unregister_port(lpss->line);
+ serial8250_unregister_port(lpss->data.line);
if (lpss->board->exit)
lpss->board->exit(lpss);
@@ -342,6 +356,11 @@
.setup = byt_serial_setup,
};
+static const struct lpss8250_board ehl_board = {
+ .freq = 200000000,
+ .base_baud = 12500000,
+};
+
static const struct lpss8250_board qrk_board = {
.freq = 44236800,
.base_baud = 2764800,
@@ -349,17 +368,21 @@
.exit = qrk_serial_exit,
};
-#define LPSS_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board }
-
static const struct pci_device_id pci_ids[] = {
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_QRK_UARTx, qrk_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART1, byt_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BYT_UART2, byt_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART1, byt_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BSW_UART2, byt_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART1, byt_board),
- LPSS_DEVICE(PCI_DEVICE_ID_INTEL_BDW_UART2, byt_board),
- { },
+ { PCI_DEVICE_DATA(INTEL, QRK_UARTx, &qrk_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART0, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART1, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART2, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART3, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART4, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, EHL_UART5, &ehl_board) },
+ { PCI_DEVICE_DATA(INTEL, BYT_UART1, &byt_board) },
+ { PCI_DEVICE_DATA(INTEL, BYT_UART2, &byt_board) },
+ { PCI_DEVICE_DATA(INTEL, BSW_UART1, &byt_board) },
+ { PCI_DEVICE_DATA(INTEL, BSW_UART2, &byt_board) },
+ { PCI_DEVICE_DATA(INTEL, BDW_UART1, &byt_board) },
+ { PCI_DEVICE_DATA(INTEL, BDW_UART2, &byt_board) },
+ { }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
diff --git a/drivers/tty/serial/8250/8250_men_mcb.c b/drivers/tty/serial/8250/8250_men_mcb.c
index 127017c..8df89e9 100644
--- a/drivers/tty/serial/8250/8250_men_mcb.c
+++ b/drivers/tty/serial/8250/8250_men_mcb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -71,8 +72,8 @@
{
struct serial_8250_men_mcb_data *data;
struct resource *mem;
- unsigned int num_ports;
- unsigned int i;
+ int num_ports;
+ int i;
void __iomem *membase;
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
@@ -87,7 +88,7 @@
dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n",
mdev->id, num_ports);
- if (num_ports == 0 || num_ports > 4) {
+ if (num_ports <= 0 || num_ports > 4) {
dev_err(&mdev->dev, "unexpected number of ports: %u\n",
num_ports);
return -ENODEV;
@@ -132,7 +133,7 @@
static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
{
- unsigned int num_ports, i;
+ int num_ports, i;
struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
if (!data)
diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c
deleted file mode 100644
index 1ee4cd9..0000000
--- a/drivers/tty/serial/8250/8250_moxa.c
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * 8250_moxa.c - MOXA Smartio/Industio MUE multiport serial driver.
- *
- * Author: Mathieu OTHACEHE <m.othacehe@gmail.com>
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include "8250.h"
-
-#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
-#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
-#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
-#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144
-#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160
-#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161
-#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182
-#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183
-#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322
-#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342
-#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
-#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
-
-#define MOXA_BASE_BAUD 921600
-#define MOXA_UART_OFFSET 0x200
-#define MOXA_BASE_BAR 1
-
-struct moxa8250_board {
- unsigned int num_ports;
- int line[0];
-};
-
-enum {
- moxa8250_2p = 0,
- moxa8250_4p,
- moxa8250_8p
-};
-
-static struct moxa8250_board moxa8250_boards[] = {
- [moxa8250_2p] = { .num_ports = 2},
- [moxa8250_4p] = { .num_ports = 4},
- [moxa8250_8p] = { .num_ports = 8},
-};
-
-static int moxa8250_probe(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct uart_8250_port uart;
- struct moxa8250_board *brd;
- void __iomem *ioaddr;
- resource_size_t baseaddr;
- unsigned int i, nr_ports;
- unsigned int offset;
- int ret;
-
- brd = &moxa8250_boards[id->driver_data];
- nr_ports = brd->num_ports;
-
- ret = pcim_enable_device(pdev);
- if (ret)
- return ret;
-
- brd = devm_kzalloc(&pdev->dev, sizeof(struct moxa8250_board) +
- sizeof(unsigned int) * nr_ports, GFP_KERNEL);
- if (!brd)
- return -ENOMEM;
- brd->num_ports = nr_ports;
-
- memset(&uart, 0, sizeof(struct uart_8250_port));
-
- uart.port.dev = &pdev->dev;
- uart.port.irq = pdev->irq;
- uart.port.uartclk = MOXA_BASE_BAUD * 16;
- uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
-
- baseaddr = pci_resource_start(pdev, MOXA_BASE_BAR);
- ioaddr = pcim_iomap(pdev, MOXA_BASE_BAR, 0);
- if (!ioaddr)
- return -ENOMEM;
-
- for (i = 0; i < nr_ports; i++) {
-
- /*
- * MOXA Smartio MUE boards with 4 ports have
- * a different offset for port #3
- */
- if (nr_ports == 4 && i == 3)
- offset = 7 * MOXA_UART_OFFSET;
- else
- offset = i * MOXA_UART_OFFSET;
-
- uart.port.iotype = UPIO_MEM;
- uart.port.iobase = 0;
- uart.port.mapbase = baseaddr + offset;
- uart.port.membase = ioaddr + offset;
- uart.port.regshift = 0;
-
- dev_dbg(&pdev->dev, "Setup PCI port: port %lx, irq %d, type %d\n",
- uart.port.iobase, uart.port.irq, uart.port.iotype);
-
- brd->line[i] = serial8250_register_8250_port(&uart);
- if (brd->line[i] < 0) {
- dev_err(&pdev->dev,
- "Couldn't register serial port %lx, irq %d, type %d, error %d\n",
- uart.port.iobase, uart.port.irq,
- uart.port.iotype, brd->line[i]);
- break;
- }
- }
-
- pci_set_drvdata(pdev, brd);
- return 0;
-}
-
-static void moxa8250_remove(struct pci_dev *pdev)
-{
- struct moxa8250_board *brd = pci_get_drvdata(pdev);
- unsigned int i;
-
- for (i = 0; i < brd->num_ports; i++)
- serial8250_unregister_port(brd->line[i]);
-}
-
-#define MOXA_DEVICE(id, data) { PCI_VDEVICE(MOXA, id), (kernel_ulong_t)data }
-
-static const struct pci_device_id pci_ids[] = {
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102E, moxa8250_2p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP102EL, moxa8250_2p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP104EL_A, moxa8250_4p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP114EL, moxa8250_4p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_A, moxa8250_8p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP116E_A_B, moxa8250_8p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118EL_A, moxa8250_8p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP118E_A_I, moxa8250_8p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP132EL, moxa8250_2p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP134EL_A, moxa8250_4p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP138E_A, moxa8250_8p),
- MOXA_DEVICE(PCI_DEVICE_ID_MOXA_CP168EL_A, moxa8250_8p),
- {0}
-};
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
-static struct pci_driver moxa8250_pci_driver = {
- .name = "8250_moxa",
- .id_table = pci_ids,
- .probe = moxa8250_probe,
- .remove = moxa8250_remove,
-};
-
-module_pci_driver(moxa8250_pci_driver);
-
-MODULE_AUTHOR("Mathieu OTHACEHE");
-MODULE_DESCRIPTION("MOXA SmartIO MUE driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index c3f933d..b411ba4 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -10,36 +10,305 @@
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/serial_8250.h>
#include <linux/serial_reg.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include "8250.h"
-#define UART_MTK_HIGHS 0x09 /* Highspeed register */
-#define UART_MTK_SAMPLE_COUNT 0x0a /* Sample count register */
-#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */
+#define MTK_UART_HIGHS 0x09 /* Highspeed register */
+#define MTK_UART_SAMPLE_COUNT 0x0a /* Sample count register */
+#define MTK_UART_SAMPLE_POINT 0x0b /* Sample point register */
#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */
+#define MTK_UART_ESCAPE_DAT 0x10 /* Escape Character register */
+#define MTK_UART_ESCAPE_EN 0x11 /* Escape Enable register */
+#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */
+#define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */
+#define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */
+#define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */
+#define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */
+#define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */
+#define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */
+
+#define MTK_UART_EFR_EN 0x10 /* Enable enhancement feature */
+#define MTK_UART_EFR_RTS 0x40 /* Enable hardware rx flow control */
+#define MTK_UART_EFR_CTS 0x80 /* Enable hardware tx flow control */
+#define MTK_UART_EFR_NO_SW_FC 0x0 /* no sw flow control */
+#define MTK_UART_EFR_XON1_XOFF1 0xa /* XON1/XOFF1 as sw flow control */
+#define MTK_UART_EFR_XON2_XOFF2 0x5 /* XON2/XOFF2 as sw flow control */
+#define MTK_UART_EFR_SW_FC_MASK 0xf /* Enable CTS Modem status interrupt */
+#define MTK_UART_EFR_HW_FC (MTK_UART_EFR_RTS | MTK_UART_EFR_CTS)
+#define MTK_UART_DMA_EN_TX 0x2
+#define MTK_UART_DMA_EN_RX 0x5
+
+#define MTK_UART_ESCAPE_CHAR 0x77 /* Escape char added under sw fc */
+#define MTK_UART_RX_SIZE 0x8000
+#define MTK_UART_TX_TRIGGER 1
+#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE
+
+#ifdef CONFIG_SERIAL_8250_DMA
+enum dma_rx_status {
+ DMA_RX_START = 0,
+ DMA_RX_RUNNING = 1,
+ DMA_RX_SHUTDOWN = 2,
+};
+#endif
struct mtk8250_data {
int line;
+ unsigned int rx_pos;
+ unsigned int clk_count;
struct clk *uart_clk;
struct clk *bus_clk;
+ struct uart_8250_dma *dma;
+#ifdef CONFIG_SERIAL_8250_DMA
+ enum dma_rx_status rx_status;
+#endif
+ int rx_wakeup_irq;
};
+/* flow control mode */
+enum {
+ MTK_UART_FC_NONE,
+ MTK_UART_FC_SW,
+ MTK_UART_FC_HW,
+};
+
+#ifdef CONFIG_SERIAL_8250_DMA
+static void mtk8250_rx_dma(struct uart_8250_port *up);
+
+static void mtk8250_dma_rx_complete(void *param)
+{
+ struct uart_8250_port *up = param;
+ struct uart_8250_dma *dma = up->dma;
+ struct mtk8250_data *data = up->port.private_data;
+ struct tty_port *tty_port = &up->port.state->port;
+ struct dma_tx_state state;
+ int copied, total, cnt;
+ unsigned char *ptr;
+
+ if (data->rx_status == DMA_RX_SHUTDOWN)
+ return;
+
+ dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ total = dma->rx_size - state.residue;
+ cnt = total;
+
+ if ((data->rx_pos + cnt) > dma->rx_size)
+ cnt = dma->rx_size - data->rx_pos;
+
+ ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+ copied = tty_insert_flip_string(tty_port, ptr, cnt);
+ data->rx_pos += cnt;
+
+ if (total > cnt) {
+ ptr = (unsigned char *)(dma->rx_buf);
+ cnt = total - cnt;
+ copied += tty_insert_flip_string(tty_port, ptr, cnt);
+ data->rx_pos = cnt;
+ }
+
+ up->port.icount.rx += copied;
+
+ tty_flip_buffer_push(tty_port);
+
+ mtk8250_rx_dma(up);
+}
+
+static void mtk8250_rx_dma(struct uart_8250_port *up)
+{
+ struct uart_8250_dma *dma = up->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+ dma->rx_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ pr_err("failed to prepare rx slave single\n");
+ return;
+ }
+
+ desc->callback = mtk8250_dma_rx_complete;
+ desc->callback_param = up;
+
+ dma->rx_cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->rxchan);
+}
+
+static void mtk8250_dma_enable(struct uart_8250_port *up)
+{
+ struct uart_8250_dma *dma = up->dma;
+ struct mtk8250_data *data = up->port.private_data;
+ int lcr = serial_in(up, UART_LCR);
+
+ if (data->rx_status != DMA_RX_START)
+ return;
+
+ dma->rxconf.src_port_window_size = dma->rx_size;
+ dma->rxconf.src_addr = dma->rx_addr;
+
+ dma->txconf.dst_port_window_size = UART_XMIT_SIZE;
+ dma->txconf.dst_addr = dma->tx_addr;
+
+ serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+ serial_out(up, MTK_UART_DMA_EN,
+ MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
+
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, UART_EFR_ECB);
+ serial_out(up, UART_LCR, lcr);
+
+ if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0)
+ pr_err("failed to configure rx dma channel\n");
+ if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0)
+ pr_err("failed to configure tx dma channel\n");
+
+ data->rx_status = DMA_RX_RUNNING;
+ data->rx_pos = 0;
+ mtk8250_rx_dma(up);
+}
+#endif
+
+static int mtk8250_startup(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+ struct uart_8250_port *up = up_to_u8250p(port);
+ struct mtk8250_data *data = port->private_data;
+
+ /* disable DMA for console */
+ if (uart_console(port))
+ up->dma = NULL;
+
+ if (up->dma) {
+ data->rx_status = DMA_RX_START;
+ uart_circ_clear(&port->state->xmit);
+ }
+#endif
+ memset(&port->icount, 0, sizeof(port->icount));
+
+ return serial8250_do_startup(port);
+}
+
+static void mtk8250_shutdown(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_8250_DMA
+ struct uart_8250_port *up = up_to_u8250p(port);
+ struct mtk8250_data *data = port->private_data;
+
+ if (up->dma)
+ data->rx_status = DMA_RX_SHUTDOWN;
+#endif
+
+ return serial8250_do_shutdown(port);
+}
+
+static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
+{
+ serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
+}
+
+static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
+{
+ serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
+}
+
+static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
+{
+ struct uart_port *port = &up->port;
+ int lcr = serial_in(up, UART_LCR);
+
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, UART_EFR_ECB);
+ serial_out(up, UART_LCR, lcr);
+ lcr = serial_in(up, UART_LCR);
+
+ switch (mode) {
+ case MTK_UART_FC_NONE:
+ serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
+ serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, serial_in(up, UART_EFR) &
+ (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK)));
+ serial_out(up, UART_LCR, lcr);
+ mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI |
+ MTK_UART_IER_RTSI | MTK_UART_IER_CTSI);
+ break;
+
+ case MTK_UART_FC_HW:
+ serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
+ serial_out(up, MTK_UART_ESCAPE_EN, 0x00);
+ serial_out(up, UART_MCR, UART_MCR_RTS);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ /*enable hw flow control*/
+ serial_out(up, UART_EFR, MTK_UART_EFR_HW_FC |
+ (serial_in(up, UART_EFR) &
+ (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
+
+ serial_out(up, UART_LCR, lcr);
+ mtk8250_disable_intrs(up, MTK_UART_IER_XOFFI);
+ mtk8250_enable_intrs(up, MTK_UART_IER_CTSI | MTK_UART_IER_RTSI);
+ break;
+
+ case MTK_UART_FC_SW: /*MTK software flow control */
+ serial_out(up, MTK_UART_ESCAPE_DAT, MTK_UART_ESCAPE_CHAR);
+ serial_out(up, MTK_UART_ESCAPE_EN, 0x01);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ /*enable sw flow control */
+ serial_out(up, UART_EFR, MTK_UART_EFR_XON1_XOFF1 |
+ (serial_in(up, UART_EFR) &
+ (~(MTK_UART_EFR_HW_FC | MTK_UART_EFR_SW_FC_MASK))));
+
+ serial_out(up, UART_XON1, START_CHAR(port->state->port.tty));
+ serial_out(up, UART_XOFF1, STOP_CHAR(port->state->port.tty));
+ serial_out(up, UART_LCR, lcr);
+ mtk8250_disable_intrs(up, MTK_UART_IER_CTSI|MTK_UART_IER_RTSI);
+ mtk8250_enable_intrs(up, MTK_UART_IER_XOFFI);
+ break;
+ default:
+ break;
+ }
+}
+
static void
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
+ unsigned short fraction_L_mapping[] = {
+ 0, 1, 0x5, 0x15, 0x55, 0x57, 0x57, 0x77, 0x7F, 0xFF, 0xFF
+ };
+ unsigned short fraction_M_mapping[] = {
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3
+ };
struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned int baud, quot, fraction;
unsigned long flags;
- unsigned int baud, quot;
+ int mode;
+
+#ifdef CONFIG_SERIAL_8250_DMA
+ if (up->dma) {
+ if (uart_console(port)) {
+ devm_kfree(up->port.dev, up->dma);
+ up->dma = NULL;
+ } else {
+ mtk8250_dma_enable(up);
+ }
+ }
+#endif
serial8250_do_set_termios(port, termios, old);
/*
- * Mediatek UARTs use an extra highspeed register (UART_MTK_HIGHS)
+ * Mediatek UARTs use an extra highspeed register (MTK_UART_HIGHS)
*
* We need to recalcualte the quot register, as the claculation depends
* on the vaule in the highspeed register.
@@ -55,18 +324,11 @@
port->uartclk / 16 / UART_DIV_MAX,
port->uartclk);
- if (baud <= 115200) {
- serial_port_out(port, UART_MTK_HIGHS, 0x0);
+ if (baud < 115200) {
+ serial_port_out(port, MTK_UART_HIGHS, 0x0);
quot = uart_get_divisor(port, baud);
- } else if (baud <= 576000) {
- serial_port_out(port, UART_MTK_HIGHS, 0x2);
-
- /* Set to next lower baudrate supported */
- if ((baud == 500000) || (baud == 576000))
- baud = 460800;
- quot = DIV_ROUND_UP(port->uartclk, 4 * baud);
} else {
- serial_port_out(port, UART_MTK_HIGHS, 0x3);
+ serial_port_out(port, MTK_UART_HIGHS, 0x3);
quot = DIV_ROUND_UP(port->uartclk, 256 * baud);
}
@@ -83,18 +345,40 @@
/* reset DLAB */
serial_port_out(port, UART_LCR, up->lcr);
- if (baud > 460800) {
+ if (baud >= 115200) {
unsigned int tmp;
- tmp = DIV_ROUND_CLOSEST(port->uartclk, quot * baud);
- serial_port_out(port, UART_MTK_SAMPLE_COUNT, tmp - 1);
- serial_port_out(port, UART_MTK_SAMPLE_POINT,
- (tmp - 2) >> 1);
+ tmp = (port->uartclk / (baud * quot)) - 1;
+ serial_port_out(port, MTK_UART_SAMPLE_COUNT, tmp);
+ serial_port_out(port, MTK_UART_SAMPLE_POINT,
+ (tmp >> 1) - 1);
+
+ /*count fraction to set fractoin register */
+ fraction = ((port->uartclk * 100) / baud / quot) % 100;
+ fraction = DIV_ROUND_CLOSEST(fraction, 10);
+ serial_port_out(port, MTK_UART_FRACDIV_L,
+ fraction_L_mapping[fraction]);
+ serial_port_out(port, MTK_UART_FRACDIV_M,
+ fraction_M_mapping[fraction]);
} else {
- serial_port_out(port, UART_MTK_SAMPLE_COUNT, 0x00);
- serial_port_out(port, UART_MTK_SAMPLE_POINT, 0xff);
+ serial_port_out(port, MTK_UART_SAMPLE_COUNT, 0x00);
+ serial_port_out(port, MTK_UART_SAMPLE_POINT, 0xff);
+ serial_port_out(port, MTK_UART_FRACDIV_L, 0x00);
+ serial_port_out(port, MTK_UART_FRACDIV_M, 0x00);
}
+ if ((termios->c_cflag & CRTSCTS) && (!(termios->c_iflag & CRTSCTS)))
+ mode = MTK_UART_FC_HW;
+ else if (termios->c_iflag & CRTSCTS)
+ mode = MTK_UART_FC_SW;
+ else
+ mode = MTK_UART_FC_NONE;
+
+ mtk8250_set_flow_ctrl(up, mode);
+
+ if (uart_console(port))
+ up->port.cons->cflag = termios->c_cflag;
+
spin_unlock_irqrestore(&port->lock, flags);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
@@ -143,9 +427,20 @@
pm_runtime_put_sync_suspend(port->dev);
}
+#ifdef CONFIG_SERIAL_8250_DMA
+static bool mtk8250_dma_filter(struct dma_chan *chan, void *param)
+{
+ return false;
+}
+#endif
+
static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
struct mtk8250_data *data)
{
+#ifdef CONFIG_SERIAL_8250_DMA
+ int dmacnt;
+#endif
+
data->uart_clk = devm_clk_get(&pdev->dev, "baud");
if (IS_ERR(data->uart_clk)) {
/*
@@ -162,7 +457,26 @@
}
data->bus_clk = devm_clk_get(&pdev->dev, "bus");
- return PTR_ERR_OR_ZERO(data->bus_clk);
+ if (IS_ERR(data->bus_clk))
+ return PTR_ERR(data->bus_clk);
+
+ data->dma = NULL;
+#ifdef CONFIG_SERIAL_8250_DMA
+ dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
+ if (dmacnt == 2) {
+ data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
+ GFP_KERNEL);
+ if (!data->dma)
+ return -ENOMEM;
+
+ data->dma->fn = mtk8250_dma_filter;
+ data->dma->rx_size = MTK_UART_RX_SIZE;
+ data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
+ data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
+ }
+#endif
+
+ return 0;
}
static int mtk8250_probe(struct platform_device *pdev)
@@ -204,8 +518,14 @@
uart.port.iotype = UPIO_MEM32;
uart.port.regshift = 2;
uart.port.private_data = data;
+ uart.port.shutdown = mtk8250_shutdown;
+ uart.port.startup = mtk8250_startup;
uart.port.set_termios = mtk8250_set_termios;
uart.port.uartclk = clk_get_rate(data->uart_clk);
+#ifdef CONFIG_SERIAL_8250_DMA
+ if (data->dma)
+ uart.dma = data->dma;
+#endif
/* Disable Rate Fix function */
writel(0x0, uart.port.membase +
@@ -224,6 +544,8 @@
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ data->rx_wakeup_irq = platform_get_irq(pdev, 1);
+
return 0;
}
@@ -245,15 +567,35 @@
static int __maybe_unused mtk8250_suspend(struct device *dev)
{
struct mtk8250_data *data = dev_get_drvdata(dev);
+ int irq = data->rx_wakeup_irq;
+ int err;
serial8250_suspend_port(data->line);
+ pinctrl_pm_select_sleep_state(dev);
+ if (irq >= 0) {
+ err = enable_irq_wake(irq);
+ if (err) {
+ dev_err(dev,
+ "failed to enable irq wake on IRQ %d: %d\n",
+ irq, err);
+ pinctrl_pm_select_default_state(dev);
+ serial8250_resume_port(data->line);
+ return err;
+ }
+ }
+
return 0;
}
static int __maybe_unused mtk8250_resume(struct device *dev)
{
struct mtk8250_data *data = dev_get_drvdata(dev);
+ int irq = data->rx_wakeup_irq;
+
+ if (irq >= 0)
+ disable_irq_wake(irq);
+ pinctrl_pm_select_default_state(dev);
serial8250_resume_port(data->line);
diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c
index 877fd7f..0826cfd 100644
--- a/drivers/tty/serial/8250/8250_of.c
+++ b/drivers/tty/serial/8250/8250_of.c
@@ -70,9 +70,10 @@
/* Get clk rate through clk driver if present */
info->clk = devm_clk_get(&ofdev->dev, NULL);
if (IS_ERR(info->clk)) {
- dev_warn(&ofdev->dev,
- "clk or clock-frequency not defined\n");
ret = PTR_ERR(info->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_warn(&ofdev->dev,
+ "failed to get clock: %d\n", ret);
goto err_pmruntime;
}
@@ -130,6 +131,10 @@
port->flags |= UPF_IOREMAP;
}
+ /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */
+ if (of_device_is_compatible(np, "mrvl,mmp-uart"))
+ port->regshift = 2;
+
/* Check for registers offset within the devices address range */
if (of_property_read_u32(np, "reg-shift", &prop) == 0)
port->regshift = prop;
@@ -201,18 +206,16 @@
/*
* Try to register a serial port
*/
-static const struct of_device_id of_platform_serial_table[];
static int of_platform_serial_probe(struct platform_device *ofdev)
{
- const struct of_device_id *match;
struct of_serial_info *info;
struct uart_8250_port port8250;
+ unsigned int port_type;
u32 tx_threshold;
- int port_type;
int ret;
- match = of_match_device(of_platform_serial_table, &ofdev->dev);
- if (!match)
+ port_type = (unsigned long)of_device_get_match_data(&ofdev->dev);
+ if (port_type == PORT_UNKNOWN)
return -EINVAL;
if (of_property_read_bool(ofdev->dev.of_node, "used-by-rtas"))
@@ -222,7 +225,6 @@
if (info == NULL)
return -ENOMEM;
- port_type = (unsigned long)match->data;
memset(&port8250, 0, sizeof(port8250));
ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info);
if (ret)
@@ -240,6 +242,11 @@
if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control"))
port8250.capabilities |= UART_CAP_AFE;
+ if (of_property_read_u32(ofdev->dev.of_node,
+ "overrun-throttle-ms",
+ &port8250.overrun_backoff_time_ms) != 0)
+ port8250.overrun_backoff_time_ms = 0;
+
ret = serial8250_register_8250_port(&port8250);
if (ret < 0)
goto err_dispose;
@@ -322,6 +329,7 @@
{ .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, },
{ .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, },
{ .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, },
+ { .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, },
{ .compatible = "altr,16550-FIFO32",
.data = (void *)PORT_ALTR_16550_F32, },
{ .compatible = "altr,16550-FIFO64",
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index a019286..836e736 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -8,6 +8,11 @@
*
*/
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -136,18 +141,20 @@
serial8250_do_set_mctrl(port, mctrl);
- /*
- * Turn off autoRTS if RTS is lowered and restore autoRTS setting
- * if RTS is raised
- */
- lcr = serial_in(up, UART_LCR);
- serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
- priv->efr |= UART_EFR_RTS;
- else
- priv->efr &= ~UART_EFR_RTS;
- serial_out(up, UART_EFR, priv->efr);
- serial_out(up, UART_LCR, lcr);
+ if (!mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS)) {
+ /*
+ * Turn off autoRTS if RTS is lowered and restore autoRTS
+ * setting if RTS is raised
+ */
+ lcr = serial_in(up, UART_LCR);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS))
+ priv->efr |= UART_EFR_RTS;
+ else
+ priv->efr &= ~UART_EFR_RTS;
+ serial_out(up, UART_EFR, priv->efr);
+ serial_out(up, UART_LCR, lcr);
+ }
}
/*
@@ -448,7 +455,9 @@
priv->efr = 0;
up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
- if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+ if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW &&
+ !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) &&
+ !mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_CTS)) {
/* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */
up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
priv->efr |= UART_EFR_CTS;
@@ -918,15 +927,13 @@
ret = omap_8250_tx_dma(p);
if (ret)
en_thri = true;
-
} else if (p->capabilities & UART_CAP_RPM) {
en_thri = true;
}
if (en_thri) {
dma->tx_err = 1;
- p->ier |= UART_IER_THRI;
- serial_port_out(&p->port, UART_IER, p->ier);
+ serial8250_set_THRI(p);
}
spin_unlock_irqrestore(&p->port.lock, flags);
@@ -954,10 +961,7 @@
ret = -EBUSY;
goto err;
}
- if (p->ier & UART_IER_THRI) {
- p->ier &= ~UART_IER_THRI;
- serial_out(p, UART_IER, p->ier);
- }
+ serial8250_clear_THRI(p);
return 0;
}
@@ -1015,10 +1019,7 @@
if (dma->tx_err)
dma->tx_err = 0;
- if (p->ier & UART_IER_THRI) {
- p->ier &= ~UART_IER_THRI;
- serial_out(p, UART_IER, p->ier);
- }
+ serial8250_clear_THRI(p);
if (skip_byte)
serial_out(p, UART_TX, xmit->buf[xmit->tail]);
return 0;
@@ -1085,7 +1086,7 @@
}
}
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_unlock_and_check_sysrq(port, flags);
serial8250_rpm_put(up);
return 1;
}
@@ -1130,10 +1131,12 @@
{
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ struct device_node *np = pdev->dev.of_node;
struct omap8250_priv *priv;
struct uart_8250_port up;
int ret;
void __iomem *membase;
+ const struct of_device_id *id;
if (!regs || !irq) {
dev_err(&pdev->dev, "missing registers or irq\n");
@@ -1190,27 +1193,31 @@
up.port.unthrottle = omap_8250_unthrottle;
up.port.rs485_config = omap_8250_rs485_config;
- if (pdev->dev.of_node) {
- const struct of_device_id *id;
-
- ret = of_alias_get_id(pdev->dev.of_node, "serial");
-
- of_property_read_u32(pdev->dev.of_node, "clock-frequency",
- &up.port.uartclk);
- priv->wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
-
- id = of_match_device(of_match_ptr(omap8250_dt_ids), &pdev->dev);
- if (id && id->data)
- priv->habit |= *(u8 *)id->data;
- } else {
- ret = pdev->id;
- }
+ ret = of_alias_get_id(np, "serial");
if (ret < 0) {
- dev_err(&pdev->dev, "failed to get alias/pdev id\n");
+ dev_err(&pdev->dev, "failed to get alias\n");
return ret;
}
up.port.line = ret;
+ if (of_property_read_u32(np, "clock-frequency", &up.port.uartclk)) {
+ struct clk *clk;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ } else {
+ up.port.uartclk = clk_get_rate(clk);
+ }
+ }
+
+ priv->wakeirq = irq_of_parse_and_map(np, 1);
+
+ id = of_match_device(of_match_ptr(omap8250_dt_ids), &pdev->dev);
+ if (id && id->data)
+ priv->habit |= *(u8 *)id->data;
+
if (!up.port.uartclk) {
up.port.uartclk = DEFAULT_CLK_SPEED;
dev_warn(&pdev->dev,
@@ -1228,7 +1235,16 @@
device_init_wakeup(&pdev->dev, true);
pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
+
+ /*
+ * Disable runtime PM until autosuspend delay unless specifically
+ * enabled by the user via sysfs. This is the historic way to
+ * prevent an unsafe default policy with lossy characters on wake-up.
+ * For serdev devices this is not needed, the policy can be managed by
+ * the serdev driver.
+ */
+ if (!of_get_available_child_count(pdev->dev.of_node))
+ pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
pm_runtime_irq_safe(&pdev->dev);
pm_runtime_enable(&pdev->dev);
@@ -1238,25 +1254,23 @@
omap_serial_fill_features_erratas(&up, priv);
up.port.handle_irq = omap8250_no_handle_irq;
#ifdef CONFIG_SERIAL_8250_DMA
- if (pdev->dev.of_node) {
- /*
- * Oh DMA support. If there are no DMA properties in the DT then
- * we will fall back to a generic DMA channel which does not
- * really work here. To ensure that we do not get a generic DMA
- * channel assigned, we have the the_no_dma_filter_fn() here.
- * To avoid "failed to request DMA" messages we check for DMA
- * properties in DT.
- */
- ret = of_property_count_strings(pdev->dev.of_node, "dma-names");
- if (ret == 2) {
- up.dma = &priv->omap8250_dma;
- priv->omap8250_dma.fn = the_no_dma_filter_fn;
- priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
- priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
- priv->omap8250_dma.rx_size = RX_TRIGGER;
- priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
- priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
- }
+ /*
+ * Oh DMA support. If there are no DMA properties in the DT then
+ * we will fall back to a generic DMA channel which does not
+ * really work here. To ensure that we do not get a generic DMA
+ * channel assigned, we have the the_no_dma_filter_fn() here.
+ * To avoid "failed to request DMA" messages we check for DMA
+ * properties in DT.
+ */
+ ret = of_property_count_strings(np, "dma-names");
+ if (ret == 2) {
+ up.dma = &priv->omap8250_dma;
+ priv->omap8250_dma.fn = the_no_dma_filter_fn;
+ priv->omap8250_dma.tx_dma = omap_8250_tx_dma;
+ priv->omap8250_dma.rx_dma = omap_8250_rx_dma;
+ priv->omap8250_dma.rx_size = RX_TRIGGER;
+ priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
+ priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
}
#endif
ret = serial8250_register_8250_port(&up);
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index f80a300..6adbadd 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -43,6 +43,11 @@
void (*exit)(struct pci_dev *dev);
};
+struct f815xxa_data {
+ spinlock_t lock;
+ int idx;
+};
+
#define PCI_NUM_BAR_RESOURCES 6
struct serial_private {
@@ -53,6 +58,16 @@
int line[0];
};
+static const struct pci_device_id pci_use_msi[] = {
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+ 0xA000, 0x1000) },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
+ 0xA000, 0x1000) },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922,
+ 0xA000, 0x1000) },
+ { }
+};
+
static int pci_default_setup(struct serial_private*,
const struct pciserial_board*, struct uart_8250_port *, int);
@@ -730,8 +745,16 @@
}
/* UART Port Control Register */
-#define NI8430_PORTCON 0x0f
-#define NI8430_PORTCON_TXVR_ENABLE (1 << 3)
+#define NI16550_PCR_OFFSET 0x0f
+#define NI16550_PCR_RS422 0x00
+#define NI16550_PCR_ECHO_RS485 0x01
+#define NI16550_PCR_DTR_RS485 0x02
+#define NI16550_PCR_AUTO_RS485 0x03
+#define NI16550_PCR_WIRE_MODE_MASK 0x03
+#define NI16550_PCR_TXVR_ENABLE_BIT BIT(3)
+#define NI16550_PCR_RS485_TERMINATION_BIT BIT(6)
+#define NI16550_ACR_DTR_AUTO_DTR (0x2 << 3)
+#define NI16550_ACR_DTR_MANUAL_DTR (0x0 << 3)
static int
pci_ni8430_setup(struct serial_private *priv,
@@ -753,14 +776,117 @@
return -ENOMEM;
/* enable the transceiver */
- writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,
- p + offset + NI8430_PORTCON);
+ writeb(readb(p + offset + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
+ p + offset + NI16550_PCR_OFFSET);
iounmap(p);
return setup_port(priv, port, bar, offset, board->reg_shift);
}
+static int pci_ni8431_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485)
+{
+ u8 pcr, acr;
+ struct uart_8250_port *up;
+
+ up = container_of(port, struct uart_8250_port, port);
+ acr = up->acr;
+ pcr = port->serial_in(port, NI16550_PCR_OFFSET);
+ pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
+
+ if (rs485->flags & SER_RS485_ENABLED) {
+ /* RS-485 */
+ if ((rs485->flags & SER_RS485_RX_DURING_TX) &&
+ (rs485->flags & SER_RS485_RTS_ON_SEND)) {
+ dev_dbg(port->dev, "Invalid 2-wire mode\n");
+ return -EINVAL;
+ }
+
+ if (rs485->flags & SER_RS485_RX_DURING_TX) {
+ /* Echo */
+ dev_vdbg(port->dev, "2-wire DTR with echo\n");
+ pcr |= NI16550_PCR_ECHO_RS485;
+ acr |= NI16550_ACR_DTR_MANUAL_DTR;
+ } else {
+ /* Auto or DTR */
+ if (rs485->flags & SER_RS485_RTS_ON_SEND) {
+ /* Auto */
+ dev_vdbg(port->dev, "2-wire Auto\n");
+ pcr |= NI16550_PCR_AUTO_RS485;
+ acr |= NI16550_ACR_DTR_AUTO_DTR;
+ } else {
+ /* DTR-controlled */
+ /* No Echo */
+ dev_vdbg(port->dev, "2-wire DTR no echo\n");
+ pcr |= NI16550_PCR_DTR_RS485;
+ acr |= NI16550_ACR_DTR_MANUAL_DTR;
+ }
+ }
+ } else {
+ /* RS-422 */
+ dev_vdbg(port->dev, "4-wire\n");
+ pcr |= NI16550_PCR_RS422;
+ acr |= NI16550_ACR_DTR_MANUAL_DTR;
+ }
+
+ dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr);
+ port->serial_out(port, NI16550_PCR_OFFSET, pcr);
+
+ up->acr = acr;
+ port->serial_out(port, UART_SCR, UART_ACR);
+ port->serial_out(port, UART_ICR, up->acr);
+
+ /* Update the cache. */
+ port->rs485 = *rs485;
+
+ return 0;
+}
+
+static int pci_ni8431_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *uart, int idx)
+{
+ u8 pcr, acr;
+ struct pci_dev *dev = priv->dev;
+ void __iomem *addr;
+ unsigned int bar, offset = board->first_offset;
+
+ if (idx >= board->num_ports)
+ return 1;
+
+ bar = FL_GET_BASE(board->flags);
+ offset += idx * board->uart_offset;
+
+ addr = pci_ioremap_bar(dev, bar);
+ if (!addr)
+ return -ENOMEM;
+
+ /* enable the transceiver */
+ writeb(readb(addr + NI16550_PCR_OFFSET) | NI16550_PCR_TXVR_ENABLE_BIT,
+ addr + NI16550_PCR_OFFSET);
+
+ pcr = readb(addr + NI16550_PCR_OFFSET);
+ pcr &= ~NI16550_PCR_WIRE_MODE_MASK;
+
+ /* set wire mode to default RS-422 */
+ pcr |= NI16550_PCR_RS422;
+ acr = NI16550_ACR_DTR_MANUAL_DTR;
+
+ /* write port configuration to register */
+ writeb(pcr, addr + NI16550_PCR_OFFSET);
+
+ /* access and write to UART acr register */
+ writeb(UART_ACR, addr + UART_SCR);
+ writeb(acr, addr + UART_ICR);
+
+ uart->port.rs485_config = &pci_ni8431_config_rs485;
+
+ iounmap(addr);
+
+ return setup_port(priv, uart, bar, offset, board->reg_shift);
+}
+
static int pci_netmos_9900_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
@@ -1326,7 +1452,36 @@
return setup_port(priv, port, bar, offset, board->reg_shift);
}
+static void
+pericom_do_set_divisor(struct uart_port *port, unsigned int baud,
+ unsigned int quot, unsigned int quot_frac)
+{
+ int scr;
+ int lcr;
+ int actual_baud;
+ int tolerance;
+ for (scr = 5 ; scr <= 15 ; scr++) {
+ actual_baud = 921600 * 16 / scr;
+ tolerance = actual_baud / 50;
+
+ if ((baud < actual_baud + tolerance) &&
+ (baud > actual_baud - tolerance)) {
+
+ lcr = serial_port_in(port, UART_LCR);
+ serial_port_out(port, UART_LCR, lcr | 0x80);
+
+ serial_port_out(port, UART_DLL, 1);
+ serial_port_out(port, UART_DLM, 0);
+ serial_port_out(port, 2, 16 - scr);
+ serial_port_out(port, UART_LCR, lcr);
+ return;
+ } else if (baud > actual_baud) {
+ break;
+ }
+ }
+ serial8250_do_set_divisor(port, baud, quot, quot_frac);
+}
static int pci_pericom_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
@@ -1339,6 +1494,30 @@
else
offset += idx * board->uart_offset;
+
+ maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
+ (board->reg_shift + 3);
+
+ if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
+ return 1;
+
+ port->port.set_divisor = pericom_do_set_divisor;
+
+ return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+static int pci_pericom_setup_four_at_eight(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ unsigned int bar, offset = board->first_offset, maxnr;
+
+ bar = FL_GET_BASE(board->flags);
+ if (board->flags & FL_BASE_BARS)
+ bar += idx;
+ else
+ offset += idx * board->uart_offset;
+
if (idx==3)
offset = 0x38;
@@ -1348,6 +1527,8 @@
if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
return 1;
+ port->port.set_divisor = pericom_do_set_divisor;
+
return setup_port(priv, port, bar, offset, board->reg_shift);
}
@@ -1541,6 +1722,77 @@
return max_port;
}
+static void f815xxa_mem_serial_out(struct uart_port *p, int offset, int value)
+{
+ struct f815xxa_data *data = p->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ writeb(value, p->membase + offset);
+ readb(p->membase + UART_SCR); /* Dummy read for flush pcie tx queue */
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static int pci_fintek_f815xxa_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ struct pci_dev *pdev = priv->dev;
+ struct f815xxa_data *data;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->idx = idx;
+ spin_lock_init(&data->lock);
+
+ port->port.private_data = data;
+ port->port.iotype = UPIO_MEM;
+ port->port.flags |= UPF_IOREMAP;
+ port->port.mapbase = pci_resource_start(pdev, 0) + 8 * idx;
+ port->port.serial_out = f815xxa_mem_serial_out;
+
+ return 0;
+}
+
+static int pci_fintek_f815xxa_init(struct pci_dev *dev)
+{
+ u32 max_port, i;
+ int config_base;
+
+ if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM))
+ return -ENODEV;
+
+ switch (dev->device) {
+ case 0x1204: /* 4 ports */
+ case 0x1208: /* 8 ports */
+ max_port = dev->device & 0xff;
+ break;
+ case 0x1212: /* 12 ports */
+ max_port = 12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set to mmio decode */
+ pci_write_config_byte(dev, 0x209, 0x40);
+
+ for (i = 0; i < max_port; ++i) {
+ /* UART0 configuration offset start from 0x2A0 */
+ config_base = 0x2A0 + 0x08 * i;
+
+ /* Select 128-byte FIFO and 8x FIFO threshold */
+ pci_write_config_byte(dev, config_base + 0x01, 0x33);
+
+ /* Enable UART I/O port */
+ pci_write_config_byte(dev, config_base + 0, 0x01);
+ }
+
+ return max_port;
+}
+
static int skip_tx_en_setup(struct serial_private *priv,
const struct pciserial_board *board,
struct uart_8250_port *port, int idx)
@@ -1637,6 +1889,46 @@
return pci_default_setup(priv, board, port, idx);
}
+static int
+pci_sunix_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ int bar;
+ int offset;
+
+ port->port.flags |= UPF_FIXED_TYPE;
+ port->port.type = PORT_SUNIX;
+
+ if (idx < 4) {
+ bar = 0;
+ offset = idx * board->uart_offset;
+ } else {
+ bar = 1;
+ idx -= 4;
+ idx = div_s64_rem(idx, 4, &offset);
+ offset = idx * 64 + offset * board->uart_offset;
+ }
+
+ return setup_port(priv, port, bar, offset, 0);
+}
+
+static int
+pci_moxa_setup(struct serial_private *priv,
+ const struct pciserial_board *board,
+ struct uart_8250_port *port, int idx)
+{
+ unsigned int bar = FL_GET_BASE(board->flags);
+ int offset;
+
+ if (board->num_ports == 4 && idx == 3)
+ offset = 7 * board->uart_offset;
+ else
+ offset = idx * board->uart_offset;
+
+ return setup_port(priv, port, bar, offset, 0);
+}
+
#define PCI_VENDOR_ID_SBSMODULARIO 0x124B
#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B
#define PCI_DEVICE_ID_OCTPRO 0x0001
@@ -1731,7 +2023,28 @@
#define PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM 0x10E9
#define PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM 0x11D8
+#define PCIE_DEVICE_ID_NI_PXIE8430_2328 0x74C2
+#define PCIE_DEVICE_ID_NI_PXIE8430_23216 0x74C1
+#define PCI_DEVICE_ID_NI_PXI8431_4852 0x7081
+#define PCI_DEVICE_ID_NI_PXI8431_4854 0x70DE
+#define PCI_DEVICE_ID_NI_PXI8431_4858 0x70E3
+#define PCI_DEVICE_ID_NI_PXI8433_4852 0x70E9
+#define PCI_DEVICE_ID_NI_PXI8433_4854 0x70ED
+#define PCIE_DEVICE_ID_NI_PXIE8431_4858 0x74C4
+#define PCIE_DEVICE_ID_NI_PXIE8431_48516 0x74C3
+#define PCI_DEVICE_ID_MOXA_CP102E 0x1024
+#define PCI_DEVICE_ID_MOXA_CP102EL 0x1025
+#define PCI_DEVICE_ID_MOXA_CP104EL_A 0x1045
+#define PCI_DEVICE_ID_MOXA_CP114EL 0x1144
+#define PCI_DEVICE_ID_MOXA_CP116E_A_A 0x1160
+#define PCI_DEVICE_ID_MOXA_CP116E_A_B 0x1161
+#define PCI_DEVICE_ID_MOXA_CP118EL_A 0x1182
+#define PCI_DEVICE_ID_MOXA_CP118E_A_I 0x1183
+#define PCI_DEVICE_ID_MOXA_CP132EL 0x1322
+#define PCI_DEVICE_ID_MOXA_CP134EL_A 0x1342
+#define PCI_DEVICE_ID_MOXA_CP138E_A 0x1381
+#define PCI_DEVICE_ID_MOXA_CP168EL_A 0x1683
/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584
@@ -1956,6 +2269,87 @@
.setup = pci_ni8430_setup,
.exit = pci_ni8430_exit,
},
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCIE_DEVICE_ID_NI_PXIE8430_2328,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8430_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCIE_DEVICE_ID_NI_PXIE8430_23216,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8430_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI8431_4852,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI8431_4854,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI8431_4858,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI8433_4852,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI8433_4854,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCIE_DEVICE_ID_NI_PXIE8431_4858,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCIE_DEVICE_ID_NI_PXIE8431_48516,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .init = pci_ni8430_init,
+ .setup = pci_ni8431_setup,
+ .exit = pci_ni8430_exit,
+ },
/* Quatech */
{
.vendor = PCI_VENDOR_ID_QUATECH,
@@ -1995,7 +2389,7 @@
.device = PCI_DEVICE_ID_PERICOM_PI7C9X7954,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
- .setup = pci_pericom_setup,
+ .setup = pci_pericom_setup_four_at_eight,
},
/*
* PLX
@@ -2027,7 +2421,118 @@
.setup = pci_default_setup,
.exit = pci_plx9050_exit,
},
- /*
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4S,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_COM_4SM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup_four_at_eight,
+ },
+ {
+ .vendor = PCI_VENDOR_ID_ACCESIO,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_pericom_setup,
+ }, /*
* SBS Technologies, Inc., PMC-OCTALPRO 232
*/
{
@@ -2123,21 +2628,14 @@
.setup = pci_timedia_setup,
},
/*
- * SUNIX (Timedia) cards
- * Do not "probe" for these cards as there is at least one combination
- * card that should be handled by parport_pc that doesn't match the
- * rule in pci_timedia_probe.
- * It is part number is MIO5079A but its subdevice ID is 0x0102.
- * There are some boards with part number SER5037AL that report
- * subdevice ID 0x0002.
+ * Sunix PCI serial boards
*/
{
.vendor = PCI_VENDOR_ID_SUNIX,
.device = PCI_DEVICE_ID_SUNIX_1999,
.subvendor = PCI_VENDOR_ID_SUNIX,
.subdevice = PCI_ANY_ID,
- .init = pci_timedia_init,
- .setup = pci_timedia_setup,
+ .setup = pci_sunix_setup,
},
/*
* Xircom cards
@@ -2397,6 +2895,40 @@
.setup = pci_fintek_setup,
.init = pci_fintek_init,
},
+ /*
+ * MOXA
+ */
+ {
+ .vendor = PCI_VENDOR_ID_MOXA,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_moxa_setup,
+ },
+ {
+ .vendor = 0x1c29,
+ .device = 0x1204,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_f815xxa_setup,
+ .init = pci_fintek_f815xxa_init,
+ },
+ {
+ .vendor = 0x1c29,
+ .device = 0x1208,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_f815xxa_setup,
+ .init = pci_fintek_f815xxa_init,
+ },
+ {
+ .vendor = 0x1c29,
+ .device = 0x1212,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .setup = pci_fintek_f815xxa_setup,
+ .init = pci_fintek_f815xxa_init,
+ },
/*
* Default "match everything" terminator entry
@@ -2574,6 +3106,13 @@
pbn_ni8430_4,
pbn_ni8430_8,
pbn_ni8430_16,
+ pbn_ni8430_pxie_8,
+ pbn_ni8430_pxie_16,
+ pbn_ni8431_2,
+ pbn_ni8431_4,
+ pbn_ni8431_8,
+ pbn_ni8431_pxie_8,
+ pbn_ni8431_pxie_16,
pbn_ADDIDATA_PCIe_1_3906250,
pbn_ADDIDATA_PCIe_2_3906250,
pbn_ADDIDATA_PCIe_4_3906250,
@@ -2585,12 +3124,23 @@
pbn_fintek_4,
pbn_fintek_8,
pbn_fintek_12,
+ pbn_fintek_F81504A,
+ pbn_fintek_F81508A,
+ pbn_fintek_F81512A,
pbn_wch382_2,
pbn_wch384_4,
pbn_pericom_PI7C9X7951,
pbn_pericom_PI7C9X7952,
pbn_pericom_PI7C9X7954,
pbn_pericom_PI7C9X7958,
+ pbn_sunix_pci_1s,
+ pbn_sunix_pci_2s,
+ pbn_sunix_pci_4s,
+ pbn_sunix_pci_8s,
+ pbn_sunix_pci_16s,
+ pbn_moxa8250_2p,
+ pbn_moxa8250_4p,
+ pbn_moxa8250_8p,
};
/*
@@ -3215,6 +3765,55 @@
.uart_offset = 0x10,
.first_offset = 0x800,
},
+ [pbn_ni8430_pxie_16] = {
+ .flags = FL_BASE0,
+ .num_ports = 16,
+ .base_baud = 3125000,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8430_pxie_8] = {
+ .flags = FL_BASE0,
+ .num_ports = 8,
+ .base_baud = 3125000,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8431_8] = {
+ .flags = FL_BASE0,
+ .num_ports = 8,
+ .base_baud = 3686400,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8431_4] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 3686400,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8431_2] = {
+ .flags = FL_BASE0,
+ .num_ports = 2,
+ .base_baud = 3686400,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8431_pxie_16] = {
+ .flags = FL_BASE0,
+ .num_ports = 16,
+ .base_baud = 3125000,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
+ [pbn_ni8431_pxie_8] = {
+ .flags = FL_BASE0,
+ .num_ports = 8,
+ .base_baud = 3125000,
+ .uart_offset = 0x10,
+ .first_offset = 0x800,
+ },
/*
* ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com>
*/
@@ -3287,6 +3886,21 @@
.base_baud = 115200,
.first_offset = 0x40,
},
+ [pbn_fintek_F81504A] = {
+ .num_ports = 4,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ },
+ [pbn_fintek_F81508A] = {
+ .num_ports = 8,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ },
+ [pbn_fintek_F81512A] = {
+ .num_ports = 12,
+ .uart_offset = 8,
+ .base_baud = 115200,
+ },
[pbn_wch382_2] = {
.flags = FL_BASE0,
.num_ports = 2,
@@ -3328,6 +3942,49 @@
.base_baud = 921600,
.uart_offset = 0x8,
},
+ [pbn_sunix_pci_1s] = {
+ .num_ports = 1,
+ .base_baud = 921600,
+ .uart_offset = 0x8,
+ },
+ [pbn_sunix_pci_2s] = {
+ .num_ports = 2,
+ .base_baud = 921600,
+ .uart_offset = 0x8,
+ },
+ [pbn_sunix_pci_4s] = {
+ .num_ports = 4,
+ .base_baud = 921600,
+ .uart_offset = 0x8,
+ },
+ [pbn_sunix_pci_8s] = {
+ .num_ports = 8,
+ .base_baud = 921600,
+ .uart_offset = 0x8,
+ },
+ [pbn_sunix_pci_16s] = {
+ .num_ports = 16,
+ .base_baud = 921600,
+ .uart_offset = 0x8,
+ },
+ [pbn_moxa8250_2p] = {
+ .flags = FL_BASE1,
+ .num_ports = 2,
+ .base_baud = 921600,
+ .uart_offset = 0x200,
+ },
+ [pbn_moxa8250_4p] = {
+ .flags = FL_BASE1,
+ .num_ports = 4,
+ .base_baud = 921600,
+ .uart_offset = 0x200,
+ },
+ [pbn_moxa8250_8p] = {
+ .flags = FL_BASE1,
+ .num_ports = 8,
+ .base_baud = 921600,
+ .uart_offset = 0x200,
+ },
};
static const struct pci_device_id blacklist[] = {
@@ -3341,20 +3998,6 @@
{ PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */
{ PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */
- /* Moxa Smartio MUE boards handled by 8250_moxa */
- { PCI_VDEVICE(MOXA, 0x1024), },
- { PCI_VDEVICE(MOXA, 0x1025), },
- { PCI_VDEVICE(MOXA, 0x1045), },
- { PCI_VDEVICE(MOXA, 0x1144), },
- { PCI_VDEVICE(MOXA, 0x1160), },
- { PCI_VDEVICE(MOXA, 0x1161), },
- { PCI_VDEVICE(MOXA, 0x1182), },
- { PCI_VDEVICE(MOXA, 0x1183), },
- { PCI_VDEVICE(MOXA, 0x1322), },
- { PCI_VDEVICE(MOXA, 0x1342), },
- { PCI_VDEVICE(MOXA, 0x1381), },
- { PCI_VDEVICE(MOXA, 0x1683), },
-
/* Intel platforms with MID UART */
{ PCI_VDEVICE(INTEL, 0x081b), },
{ PCI_VDEVICE(INTEL, 0x081c), },
@@ -3375,6 +4018,9 @@
/* Exar devices */
{ PCI_VDEVICE(EXAR, PCI_ANY_ID), },
{ PCI_VDEVICE(COMMTECH, PCI_ANY_ID), },
+
+ /* End of the black list */
+ { }
};
static int serial_pci_is_class_communication(struct pci_dev *dev)
@@ -3392,25 +4038,6 @@
return 0;
}
-static int serial_pci_is_blacklisted(struct pci_dev *dev)
-{
- const struct pci_device_id *bldev;
-
- /*
- * Do not access blacklisted devices that are known not to
- * feature serial ports or are handled by other modules.
- */
- for (bldev = blacklist;
- bldev < blacklist + ARRAY_SIZE(blacklist);
- bldev++) {
- if (dev->vendor == bldev->vendor &&
- dev->device == bldev->device)
- return -ENODEV;
- }
-
- return 0;
-}
-
/*
* Given a complete unknown PCI device, try to use some heuristics to
* guess what the configuration might be, based on the pitiful PCI
@@ -3420,6 +4047,11 @@
serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
{
int num_iomem, num_port, first_port = -1, i;
+ int rc;
+
+ rc = serial_pci_is_class_communication(dev);
+ if (rc)
+ return rc;
/*
* Should we try to make guesses for multiport serial devices later?
@@ -3533,7 +4165,22 @@
memset(&uart, 0, sizeof(uart));
uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
uart.port.uartclk = board->base_baud * 16;
- uart.port.irq = get_pci_irq(dev, board);
+
+ if (pci_match_id(pci_use_msi, dev)) {
+ dev_dbg(&dev->dev, "Using MSI(-X) interrupts\n");
+ pci_set_master(dev);
+ rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_ALL_TYPES);
+ } else {
+ dev_dbg(&dev->dev, "Using legacy interrupts\n");
+ rc = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
+ }
+ if (rc < 0) {
+ kfree(priv);
+ priv = ERR_PTR(rc);
+ goto err_deinit;
+ }
+
+ uart.port.irq = pci_irq_vector(dev, 0);
uart.port.dev = &dev->dev;
for (i = 0; i < nr_ports; i++) {
@@ -3629,6 +4276,7 @@
struct pci_serial_quirk *quirk;
struct serial_private *priv;
const struct pciserial_board *board;
+ const struct pci_device_id *exclude;
struct pciserial_board tmp;
int rc;
@@ -3647,13 +4295,9 @@
board = &pci_boards[ent->driver_data];
- rc = serial_pci_is_class_communication(dev);
- if (rc)
- return rc;
-
- rc = serial_pci_is_blacklisted(dev);
- if (rc)
- return rc;
+ exclude = pci_match_id(blacklist, dev);
+ if (exclude)
+ return -ENODEV;
rc = pcim_enable_device(dev);
pci_save_state(dev);
@@ -3707,8 +4351,7 @@
#ifdef CONFIG_PM_SLEEP
static int pciserial_suspend_one(struct device *dev)
{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct serial_private *priv = pci_get_drvdata(pdev);
+ struct serial_private *priv = dev_get_drvdata(dev);
if (priv)
pciserial_suspend_ports(priv);
@@ -4380,17 +5023,29 @@
pbn_b0_bt_1_921600 },
/*
- * SUNIX (TIMEDIA)
+ * Sunix PCI serial boards
*/
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
- PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00,
- pbn_b0_bt_1_921600 },
-
+ PCI_VENDOR_ID_SUNIX, 0x0001, 0, 0,
+ pbn_sunix_pci_1s },
{ PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
- PCI_VENDOR_ID_SUNIX, PCI_ANY_ID,
- PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, 0xffff00,
- pbn_b0_bt_1_921600 },
+ PCI_VENDOR_ID_SUNIX, 0x0002, 0, 0,
+ pbn_sunix_pci_2s },
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, 0x0004, 0, 0,
+ pbn_sunix_pci_4s },
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, 0x0084, 0, 0,
+ pbn_sunix_pci_4s },
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, 0x0008, 0, 0,
+ pbn_sunix_pci_8s },
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, 0x0088, 0, 0,
+ pbn_sunix_pci_8s },
+ { PCI_VENDOR_ID_SUNIX, PCI_DEVICE_ID_SUNIX_1999,
+ PCI_VENDOR_ID_SUNIX, 0x0010, 0, 0,
+ pbn_sunix_pci_16s },
/*
* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
@@ -4574,10 +5229,10 @@
*/
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SDB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2S,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SDB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
@@ -4586,10 +5241,10 @@
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_2DB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM232_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4DB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
@@ -4598,10 +5253,10 @@
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_2SMDB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_COM_2SM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SMDB,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
@@ -4610,13 +5265,13 @@
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_1,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7951 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM485_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM422_4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
@@ -4625,16 +5280,16 @@
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2S,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4S,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_MPCIE_ICM232_2,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM232_4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7954 },
@@ -4643,13 +5298,13 @@
pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_2SM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7954 },
+ pbn_pericom_PI7C9X7952 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7958 },
+ pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM485_4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7958 },
+ pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM422_8,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7958 },
@@ -4658,19 +5313,19 @@
pbn_pericom_PI7C9X7958 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_4,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7958 },
+ pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM232_8,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7958 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_4SM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7958 },
+ pbn_pericom_PI7C9X7954 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_COM_8SM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_pericom_PI7C9X7958 },
{ PCI_VENDOR_ID_ACCESIO, PCI_DEVICE_ID_ACCESIO_PCIE_ICM_4SM,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- pbn_pericom_PI7C9X7958 },
+ pbn_pericom_PI7C9X7954 },
/*
* Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
*/
@@ -4912,6 +5567,73 @@
{ PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_ni8430_4 },
+ { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_2328,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8430_pxie_8 },
+ { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8430_23216,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8430_pxie_16 },
+ { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4852,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_2 },
+ { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4854,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_4 },
+ { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8431_4858,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_8 },
+ { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_4858,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_pxie_8 },
+ { PCI_VENDOR_ID_NI, PCIE_DEVICE_ID_NI_PXIE8431_48516,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_pxie_16 },
+ { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4852,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_2 },
+ { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8433_4854,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_ni8431_4 },
+
+ /*
+ * MOXA
+ */
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_2p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102EL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_2p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104EL_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_4p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114EL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_4p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP116E_A_B,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118EL_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118E_A_I,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132EL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_2p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134EL_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_4p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP138E_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
+ { PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168EL_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ pbn_moxa8250_8p },
/*
* ADDI-DATA GmbH communication cards <info@addi-data.com>
@@ -5140,6 +5862,9 @@
{ PCI_DEVICE(0x1c29, 0x1104), .driver_data = pbn_fintek_4 },
{ PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
{ PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+ { PCI_DEVICE(0x1c29, 0x1204), .driver_data = pbn_fintek_F81504A },
+ { PCI_DEVICE(0x1c29, 0x1208), .driver_data = pbn_fintek_F81508A },
+ { PCI_DEVICE(0x1c29, 0x1212), .driver_data = pbn_fintek_F81512A },
/* MKS Tenta SCOM-080x serial cards */
{ PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 },
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index 431e69a..de90d68 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -462,8 +462,8 @@
return -ENODEV;
dev_dbg(&dev->dev,
- "Setup PNP port: port %lx, mem %pa, irq %d, type %d\n",
- uart.port.iobase, &uart.port.mapbase,
+ "Setup PNP port: port %#lx, mem %#llx, irq %u, type %u\n",
+ uart.port.iobase, (unsigned long long)uart.port.mapbase,
uart.port.irq, uart.port.iotype);
if (flags & CIR_PORT) {
@@ -498,10 +498,9 @@
serial8250_unregister_port(line - 1);
}
-#ifdef CONFIG_PM
-static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
+static int __maybe_unused serial_pnp_suspend(struct device *dev)
{
- long line = (long)pnp_get_drvdata(dev);
+ long line = (long)dev_get_drvdata(dev);
if (!line)
return -ENODEV;
@@ -509,26 +508,25 @@
return 0;
}
-static int serial_pnp_resume(struct pnp_dev *dev)
+static int __maybe_unused serial_pnp_resume(struct device *dev)
{
- long line = (long)pnp_get_drvdata(dev);
+ long line = (long)dev_get_drvdata(dev);
if (!line)
return -ENODEV;
serial8250_resume_port(line - 1);
return 0;
}
-#else
-#define serial_pnp_suspend NULL
-#define serial_pnp_resume NULL
-#endif /* CONFIG_PM */
+
+static SIMPLE_DEV_PM_OPS(serial_pnp_pm_ops, serial_pnp_suspend, serial_pnp_resume);
static struct pnp_driver serial_pnp_driver = {
.name = "serial",
.probe = serial_pnp_probe,
.remove = serial_pnp_remove,
- .suspend = serial_pnp_suspend,
- .resume = serial_pnp_resume,
+ .driver = {
+ .pm = &serial_pnp_pm_ops,
+ },
.id_table = pnp_dev_table,
};
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 3f779d2..8407166 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -40,13 +40,6 @@
#include "8250.h"
-/*
- * These are definitions for the Exar XR17V35X and XR17(C|D)15X
- */
-#define UART_EXAR_INT0 0x80
-#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
-#define UART_EXAR_DVID 0x8d /* Device identification */
-
/* Nuvoton NPCM timeout register */
#define UART_NPCM_TOR 7
#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */
@@ -308,6 +301,14 @@
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
+ [PORT_SUNIX] = {
+ .name = "Sunix",
+ .fifo_size = 128,
+ .tx_loadsz = 128,
+ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+ .rxtrig_bytes = {1, 32, 64, 112},
+ .flags = UART_CAP_FIFO | UART_CAP_SLEEP,
+ },
};
/* Uart divisor latch read */
@@ -709,19 +710,8 @@
static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
{
unsigned char lcr = 0, efr = 0;
- /*
- * Exar UARTs have a SLEEP register that enables or disables
- * each UART to enter sleep mode separately. On the XR17V35x the
- * register is accessible to each UART at the UART_EXAR_SLEEP
- * offset but the UART channel may only write to the corresponding
- * bit.
- */
+
serial8250_rpm_get(p);
- if ((p->port.type == PORT_XR17V35X) ||
- (p->port.type == PORT_XR17D15X)) {
- serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
- goto out;
- }
if (p->capabilities & UART_CAP_SLEEP) {
if (p->capabilities & UART_CAP_EFR) {
@@ -738,7 +728,7 @@
serial_out(p, UART_LCR, lcr);
}
}
-out:
+
serial8250_rpm_put(p);
}
@@ -1012,27 +1002,6 @@
up->capabilities |= UART_CAP_FIFO;
/*
- * XR17V35x UARTs have an extra divisor register, DLD
- * that gets enabled with when DLAB is set which will
- * cause the device to incorrectly match and assign
- * port type to PORT_16650. The EFR for this UART is
- * found at offset 0x09. Instead check the Deice ID (DVID)
- * register for a 2, 4 or 8 port UART.
- */
- if (up->port.flags & UPF_EXAR_EFR) {
- status1 = serial_in(up, UART_EXAR_DVID);
- if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) {
- DEBUG_AUTOCONF("Exar XR17V35x ");
- up->port.type = PORT_XR17V35X;
- up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
- UART_CAP_SLEEP;
-
- return;
- }
-
- }
-
- /*
* Check for presence of the EFR when DLAB is set.
* Only ST16C650V1 UARTs pass this test.
*/
@@ -1171,18 +1140,6 @@
serial_out(up, UART_IER, iersave);
/*
- * Exar uarts have EFR in a weird location
- */
- if (up->port.flags & UPF_EXAR_EFR) {
- DEBUG_AUTOCONF("Exar XR17D15x ");
- up->port.type = PORT_XR17D15X;
- up->capabilities |= UART_CAP_AFE | UART_CAP_EFR |
- UART_CAP_SLEEP;
-
- return;
- }
-
- /*
* We distinguish between 16550A and U6 16550A by counting
* how many bytes are in the FIFO.
*/
@@ -1502,11 +1459,8 @@
static inline void __do_stop_tx(struct uart_8250_port *p)
{
- if (p->ier & UART_IER_THRI) {
- p->ier &= ~UART_IER_THRI;
- serial_out(p, UART_IER, p->ier);
+ if (serial8250_clear_THRI(p))
serial8250_rpm_put_tx(p);
- }
}
static inline void __stop_tx(struct uart_8250_port *p)
@@ -1555,10 +1509,7 @@
if (up->dma && !up->dma->tx_dma(up))
return;
- if (!(up->ier & UART_IER_THRI)) {
- up->ier |= UART_IER_THRI;
- serial_port_out(port, UART_IER, up->ier);
-
+ if (serial8250_set_THRI(up)) {
if (up->bugs & UART_BUG_TXEN) {
unsigned char lsr;
@@ -1662,6 +1613,8 @@
if (up->bugs & UART_BUG_NOMSR)
return;
+ mctrl_gpio_disable_ms(up->gpios);
+
up->ier &= ~UART_IER_MSI;
serial_port_out(port, UART_IER, up->ier);
}
@@ -1674,6 +1627,8 @@
if (up->bugs & UART_BUG_NOMSR)
return;
+ mctrl_gpio_enable_ms(up->gpios);
+
up->ier |= UART_IER_MSI;
serial8250_rpm_get(up);
@@ -1736,7 +1691,7 @@
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
- if (uart_handle_sysrq_char(port, ch))
+ if (uart_prepare_sysrq_char(port, ch))
return;
uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
@@ -1869,16 +1824,16 @@
status = serial_port_in(port, UART_LSR);
- if (status & (UART_LSR_DR | UART_LSR_BI) &&
- iir & UART_IIR_RDI) {
+ if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
serial8250_modem_status(up);
- if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE))
+ if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) &&
+ (up->ier & UART_IER_THRI))
serial8250_tx_chars(up);
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_unlock_and_check_sysrq(port, flags);
return 1;
}
EXPORT_SYMBOL_GPL(serial8250_handle_irq);
@@ -1944,22 +1899,17 @@
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned int status;
- unsigned int ret;
+ unsigned int val;
serial8250_rpm_get(up);
status = serial8250_modem_status(up);
serial8250_rpm_put(up);
- ret = 0;
- if (status & UART_MSR_DCD)
- ret |= TIOCM_CAR;
- if (status & UART_MSR_RI)
- ret |= TIOCM_RNG;
- if (status & UART_MSR_DSR)
- ret |= TIOCM_DSR;
- if (status & UART_MSR_CTS)
- ret |= TIOCM_CTS;
- return ret;
+ val = serial8250_MSR_to_TIOCM(status);
+ if (up->gpios)
+ return mctrl_gpio_get(up->gpios, &val);
+
+ return val;
}
EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl);
@@ -1973,18 +1923,9 @@
void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct uart_8250_port *up = up_to_u8250p(port);
- unsigned char mcr = 0;
+ unsigned char mcr;
- if (mctrl & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
- if (mctrl & TIOCM_DTR)
- mcr |= UART_MCR_DTR;
- if (mctrl & TIOCM_OUT1)
- mcr |= UART_MCR_OUT1;
- if (mctrl & TIOCM_OUT2)
- mcr |= UART_MCR_OUT2;
- if (mctrl & TIOCM_LOOP)
- mcr |= UART_MCR_LOOP;
+ mcr = serial8250_TIOCM_to_MCR(mctrl);
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
@@ -2200,8 +2141,6 @@
serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
- if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
- serial_port_in(port, UART_EXAR_INT0);
/*
* At this point, there's no way the LSR could still be 0xff;
@@ -2359,8 +2298,6 @@
serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
- if ((port->type == PORT_XR17V35X) || (port->type == PORT_XR17D15X))
- serial_port_in(port, UART_EXAR_INT0);
up->lsr_saved_flags = 0;
up->msr_saved_flags = 0;
@@ -2469,23 +2406,6 @@
serial8250_do_shutdown(port);
}
-/*
- * XR17V35x UARTs have an extra fractional divisor register (DLD)
- * Calculate divisor with extra 4-bit fractional portion
- */
-static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
- unsigned int baud,
- unsigned int *frac)
-{
- struct uart_port *port = &up->port;
- unsigned int quot_16;
-
- quot_16 = DIV_ROUND_CLOSEST(port->uartclk, baud);
- *frac = quot_16 & 0x0f;
-
- return quot_16 >> 4;
-}
-
/* Nuvoton NPCM UARTs have a custom divisor calculation */
static unsigned int npcm_get_divisor(struct uart_8250_port *up,
unsigned int baud)
@@ -2513,8 +2433,6 @@
else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
baud == (port->uartclk/8))
quot = 0x8002;
- else if (up->port.type == PORT_XR17V35X)
- quot = xr17v35x_get_divisor(up, baud, frac);
else if (up->port.type == PORT_NPCM)
quot = npcm_get_divisor(up, baud);
else
@@ -2601,13 +2519,6 @@
serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
serial_dl_write(up, quot);
-
- /* XR17V35x UARTs have an extra fractional divisor register (DLD) */
- if (up->port.type == PORT_XR17V35X) {
- /* Preserve bits not related to baudrate; DLD[7:4]. */
- quot_frac |= serial_port_in(port, 0x2) & 0xf0;
- serial_port_out(port, 0x2, quot_frac);
- }
}
EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
@@ -3239,9 +3150,7 @@
serial8250_rpm_get(up);
- if (port->sysrq)
- locked = 0;
- else if (oops_in_progress)
+ if (oops_in_progress)
locked = spin_trylock_irqsave(&port->lock, flags);
else
spin_lock_irqsave(&port->lock, flags);
diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c
index b9bcbe2..c471888 100644
--- a/drivers/tty/serial/8250/8250_pxa.c
+++ b/drivers/tty/serial/8250/8250_pxa.c
@@ -113,6 +113,10 @@
if (ret)
return ret;
+ ret = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (ret >= 0)
+ uart.port.line = ret;
+
uart.port.type = PORT_XSCALE;
uart.port.iotype = UPIO_MEM32;
uart.port.mapbase = mmres->start;
diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c
index 28d88cc..e0b73a5 100644
--- a/drivers/tty/serial/8250/8250_uniphier.c
+++ b/drivers/tty/serial/8250/8250_uniphier.c
@@ -12,9 +12,6 @@
#include "8250.h"
-/* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */
-#define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64
-
/*
* This hardware is similar to 8250, but its register map is a bit different:
* - MMIO32 (regshift = 2)
@@ -158,42 +155,6 @@
writel(value, up->port.membase + UNIPHIER_UART_DLR);
}
-static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port,
- struct uniphier8250_priv *priv)
-{
- int ret;
- u32 prop;
- struct device_node *np = dev->of_node;
-
- ret = of_alias_get_id(np, "serial");
- if (ret < 0) {
- dev_err(dev, "failed to get alias id\n");
- return ret;
- }
- port->line = ret;
-
- /* Get clk rate through clk driver */
- priv->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(priv->clk)) {
- dev_err(dev, "failed to get clock\n");
- return PTR_ERR(priv->clk);
- }
-
- ret = clk_prepare_enable(priv->clk);
- if (ret < 0)
- return ret;
-
- port->uartclk = clk_get_rate(priv->clk);
-
- /* Check for fifo size */
- if (of_property_read_u32(np, "fifo-size", &prop) == 0)
- port->fifosize = prop;
- else
- port->fifosize = UNIPHIER_UART_DEFAULT_FIFO_SIZE;
-
- return 0;
-}
-
static int uniphier_uart_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -215,10 +176,8 @@
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "failed to get IRQ number\n");
+ if (irq < 0)
return irq;
- }
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -226,9 +185,24 @@
memset(&up, 0, sizeof(up));
- ret = uniphier_of_serial_setup(dev, &up.port, priv);
- if (ret < 0)
+ ret = of_alias_get_id(dev->of_node, "serial");
+ if (ret < 0) {
+ dev_err(dev, "failed to get alias id\n");
return ret;
+ }
+ up.port.line = ret;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "failed to get clock\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ up.port.uartclk = clk_get_rate(priv->clk);
spin_lock_init(&priv->atomic_write_lock);
@@ -241,10 +215,14 @@
up.port.type = PORT_16550A;
up.port.iotype = UPIO_MEM32;
+ up.port.fifosize = 64;
up.port.regshift = UNIPHIER_UART_REGSHIFT;
up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE;
up.capabilities = UART_CAP_FIFO;
+ if (of_property_read_bool(dev->of_node, "auto-flow-control"))
+ up.capabilities |= UART_CAP_AFE;
+
up.port.serial_in = uniphier_serial_in;
up.port.serial_out = uniphier_serial_out;
up.dl_read = uniphier_serial_dl_read;
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index f005eaf..7ef60f8 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# The 8250/16550 serial drivers. You shouldn't be in this list unless
# you somehow have an implicit or explicit dependency on SERIAL_8250.
@@ -7,6 +8,7 @@
tristate "8250/16550 and compatible serial support"
depends on !S390
select SERIAL_CORE
+ select SERIAL_MCTRL_GPIO if GPIOLIB
---help---
This selects whether you want to include the driver for the standard
serial ports. The standard answer is Y. People who might say N
@@ -312,6 +314,9 @@
If you don't have such card, or if unsure, say N.
+config SERIAL_8250_DWLIB
+ bool
+
config SERIAL_8250_ACORN
tristate "Acorn expansion card serial port support"
depends on ARCH_ACORN && SERIAL_8250
@@ -352,6 +357,7 @@
config SERIAL_8250_DW
tristate "Support for Synopsys DesignWare 8250 quirks"
depends on SERIAL_8250
+ select SERIAL_8250_DWLIB
help
Selecting this option will enable handling of the extra features
present in the Synopsys DesignWare APB UART.
@@ -375,7 +381,7 @@
config SERIAL_8250_OMAP
tristate "Support for OMAP internal UART (8250 based driver)"
- depends on SERIAL_8250 && ARCH_OMAP2PLUS
+ depends on SERIAL_8250 && (ARCH_OMAP2PLUS || ARCH_K3)
help
If you have a machine based on an Texas Instruments OMAP CPU you
can enable its onboard serial ports by enabling this option.
@@ -438,6 +444,7 @@
default SERIAL_8250
depends on SERIAL_8250 && PCI
depends on X86 || COMPILE_TEST
+ select SERIAL_8250_DWLIB
select DW_DMAC_CORE if SERIAL_8250_DMA
select DW_DMAC_PCI if (SERIAL_8250_DMA && X86_INTEL_LPSS)
select RATIONAL
@@ -461,16 +468,6 @@
present on the UART found on Intel Medfield SOC and various other
Intel platforms.
-config SERIAL_8250_MOXA
- tristate "MOXA SmartIO MUE support"
- depends on SERIAL_8250 && PCI
- help
- Say Y here if you have a Moxa SmartIO MUE multiport serial card.
- If unsure, say N.
-
- This driver can also be built as a module. The module will be called
- 8250_moxa. If you want to do that, say M here.
-
config SERIAL_8250_PXA
tristate "PXA serial port support"
depends on SERIAL_8250
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 18751bc..08c1d81 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -8,6 +8,7 @@
8250-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
8250_base-y := 8250_port.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
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
@@ -34,7 +35,6 @@
obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o
obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o
obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o
-obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o
obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index df8bd0c..67a9eb3 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Serial device configuration
#
@@ -85,6 +86,18 @@
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"
+ depends on RISCV
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Support for early debug console using RISC-V SBI. This enables
+ the console before standard serial driver is probed. This is enabled
+ with "earlycon=sbi" on the kernel command line. The console is
+ enabled when early_param is processed.
+
config SERIAL_SB1250_DUART
tristate "BCM1xxx on-chip DUART serial support"
depends on SIBYTE_SB1xxx_SOC=y
@@ -118,6 +131,7 @@
depends on ARCH_AT91 || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
+ select MFD_AT91_USART
help
This enables the driver for the on-chip UARTs of the Atmel
AT91 processors.
@@ -183,23 +197,6 @@
If unsure, say N.
-config SERIAL_KS8695
- bool "Micrel KS8695 (Centaur) serial port support"
- depends on ARCH_KS8695
- select SERIAL_CORE
- help
- This selects the Micrel Centaur KS8695 UART. Say Y here.
-
-config SERIAL_KS8695_CONSOLE
- bool "Support for console on KS8695 (Centaur) serial port"
- depends on SERIAL_KS8695=y
- select SERIAL_CORE_CONSOLE
- help
- Say Y here if you wish to use a KS8695 (Centaur) UART as the
- system console (the system console is the device which
- receives all kernel messages and warnings and which allows
- logins in single user mode).
-
config SERIAL_MESON
tristate "Meson serial port support"
depends on ARCH_MESON
@@ -322,6 +319,28 @@
are enabled). This driver uses the APB DMA to achieve higher baudrate
and better performance.
+config SERIAL_TEGRA_TCU
+ tristate "NVIDIA Tegra Combined UART"
+ depends on ARCH_TEGRA && TEGRA_HSP_MBOX
+ select SERIAL_CORE
+ help
+ Support for the mailbox-based TCU (Tegra Combined UART) serial port.
+ TCU is a virtual serial port that allows multiplexing multiple data
+ streams into a single hardware serial port.
+
+config SERIAL_TEGRA_TCU_CONSOLE
+ bool "Support for console on a Tegra TCU serial port"
+ depends on SERIAL_TEGRA_TCU=y
+ select SERIAL_CORE_CONSOLE
+ default y
+ ---help---
+ If you say Y here, it will be possible to use a the Tegra TCU as the
+ system console (the system console is the device which receives all
+ kernel messages and warnings and which allows logins in single user
+ mode).
+
+ If unsure, say Y.
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
@@ -334,7 +353,6 @@
depends on SPI_MASTER
select SERIAL_CORE
select REGMAP_SPI if SPI_MASTER
- default n
help
This selects support for an advanced UART from Maxim (Dallas).
Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830.
@@ -422,20 +440,6 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
-config SERIAL_MPSC
- bool "Marvell MPSC serial port support"
- depends on MV64X60
- select SERIAL_CORE
- help
- Say Y here if you want to use the Marvell MPSC serial controller.
-
-config SERIAL_MPSC_CONSOLE
- bool "Support for console on Marvell MPSC serial port"
- depends on SERIAL_MPSC
- select SERIAL_CORE_CONSOLE
- help
- Say Y here if you want to support a serial console on a Marvell MPSC.
-
config SERIAL_PXA
bool "PXA serial port support (DEPRECATED)"
depends on ARCH_PXA || ARCH_MMP
@@ -476,10 +480,11 @@
bool "SA1100 serial port support"
depends on ARCH_SA1100
select SERIAL_CORE
+ select SERIAL_MCTRL_GPIO if GPIOLIB
help
If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you
can enable its onboard serial port by enabling this option.
- Please read <file:Documentation/arm/SA1100/serial_UART> for further
+ Please read <file:Documentation/arm/sa1100/serial_uart.rst> for further
info.
config SERIAL_SA1100_CONSOLE
@@ -617,7 +622,6 @@
config PDC_CONSOLE
bool "PDC software console support"
depends on PARISC && !SERIAL_MUX && VT
- default n
help
Saying Y here will enable the software based PDC console to be
used as the system console. This is useful for machines in
@@ -718,7 +722,8 @@
config SERIAL_HS_LPC32XX
tristate "LPC32XX high speed serial port support"
- depends on ARCH_LPC32XX && OF
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on OF
select SERIAL_CORE
help
Support for the LPC32XX high speed serial ports (up to 900kbps).
@@ -834,16 +839,6 @@
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
-config SERIAL_SGI_L1_CONSOLE
- bool "SGI Altix L1 serial console support"
- depends on IA64_GENERIC || IA64_SGI_SN2
- select SERIAL_CORE
- select SERIAL_CORE_CONSOLE
- help
- If you have an SGI Altix and you would like to use the system
- controller serial port as your console (you want this!),
- say Y. Otherwise, say N.
-
config SERIAL_PIC32
tristate "Microchip PIC32 serial support"
depends on MACH_PIC32
@@ -961,23 +956,6 @@
To compile this driver as a module, choose M here: the
module will be called jsm.
-config SERIAL_SGI_IOC4
- tristate "SGI IOC4 controller serial support"
- depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC4
- select SERIAL_CORE
- help
- If you have an SGI Altix with an IOC4 based Base IO card
- and wish to use the serial ports on this card, say Y.
- Otherwise, say N.
-
-config SERIAL_SGI_IOC3
- tristate "SGI Altix IOC3 serial support"
- depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3
- select SERIAL_CORE
- help
- If you have an SGI Altix with an IOC3 serial card,
- say Y or M. Otherwise, say N.
-
config SERIAL_MSM
tristate "MSM on-chip serial port support"
depends on ARCH_QCOM
@@ -1014,25 +992,6 @@
depends on SERIAL_VT8500=y
select SERIAL_CORE_CONSOLE
-config SERIAL_NETX
- tristate "NetX serial port support"
- depends on ARCH_NETX
- select SERIAL_CORE
- help
- If you have a machine based on a Hilscher NetX SoC you
- can enable its onboard serial port by enabling this option.
-
- To compile this driver as a module, choose M here: the
- module will be called netx-serial.
-
-config SERIAL_NETX_CONSOLE
- bool "Console on NetX serial port"
- depends on SERIAL_NETX=y
- select SERIAL_CORE_CONSOLE
- help
- If you have enabled the serial port on the Hilscher NetX SoC
- you can make it the console by answering Y to this option.
-
config SERIAL_OMAP
tristate "OMAP serial port support"
depends on ARCH_OMAP2PLUS
@@ -1060,6 +1019,31 @@
your boot loader about how to pass options to the kernel at
boot time.)
+config SERIAL_SIFIVE
+ tristate "SiFive UART support"
+ depends on OF
+ select SERIAL_CORE
+ help
+ Select this option if you are building a kernel for a device that
+ contains a SiFive UART IP block. This type of UART is present on
+ SiFive FU540 SoCs, among others.
+
+config SERIAL_SIFIVE_CONSOLE
+ bool "Console on SiFive UART"
+ depends on SERIAL_SIFIVE=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Select this option if you would like to use a SiFive UART as the
+ system console.
+
+ Even if you say Y here, the currently visible virtual console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttySIFx". (Try "man bootparam" or see the documentation of
+ your boot loader about how to pass options to the kernel at
+ boot time.)
+
config SERIAL_LANTIQ
bool "Lantiq serial driver"
depends on LANTIQ
@@ -1074,7 +1058,6 @@
depends on QUICC_ENGINE
select SERIAL_CORE
select FW_LOADER
- default n
help
This driver supports the QE serial ports on Freescale embedded
PowerPC that contain a QUICC Engine.
@@ -1408,6 +1391,22 @@
If you have enabled the lpuart serial port on the Freescale SoCs,
you can make it the console by answering Y to this option.
+config SERIAL_FSL_LINFLEXUART
+ tristate "Freescale linflexuart serial port support"
+ depends on PRINTK
+ select SERIAL_CORE
+ help
+ Support for the on-chip linflexuart on some Freescale SOCs.
+
+config SERIAL_FSL_LINFLEXUART_CONSOLE
+ bool "Console on Freescale linflexuart serial port"
+ depends on SERIAL_FSL_LINFLEXUART=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ If you have enabled the linflexuart serial port on the Freescale
+ SoCs, you can make it the console by answering Y to this option.
+
config SERIAL_CONEXANT_DIGICOLOR
tristate "Conexant Digicolor CX92xxx USART serial port support"
depends on OF
@@ -1528,6 +1527,51 @@
Say 'Y' here if you wish to use Actions Semiconductor S500/S900 UART
as the system console.
+config SERIAL_RDA
+ bool "RDA Micro serial port support"
+ depends on ARCH_RDA || COMPILE_TEST
+ select SERIAL_CORE
+ help
+ This driver is for RDA8810PL SoC's UART.
+ Say 'Y' here if you wish to use the on-board serial port.
+ Otherwise, say 'N'.
+
+config SERIAL_RDA_CONSOLE
+ bool "Console on RDA Micro serial port"
+ depends on SERIAL_RDA=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ default y
+ help
+ Say 'Y' here if you wish to use the RDA8810PL UART as the system
+ console. Only earlycon is implemented currently.
+
+config SERIAL_MILBEAUT_USIO
+ tristate "Milbeaut USIO/UART serial port support"
+ depends on ARCH_MILBEAUT || (COMPILE_TEST && OF)
+ default ARCH_MILBEAUT
+ select SERIAL_CORE
+ help
+ This selects the USIO/UART IP found in Socionext Milbeaut SoCs.
+
+config SERIAL_MILBEAUT_USIO_PORTS
+ int "Maximum number of CSIO/UART ports (1-8)"
+ range 1 8
+ depends on SERIAL_MILBEAUT_USIO
+ default "4"
+
+config SERIAL_MILBEAUT_USIO_CONSOLE
+ bool "Support for console on MILBEAUT USIO/UART serial port"
+ depends on SERIAL_MILBEAUT_USIO=y
+ default y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Say 'Y' here if you wish to use a USIO/UART of Socionext Milbeaut
+ SoCs as the system console (the system console is the device which
+ receives all kernel messages and warnings and which allows logins in
+ single user mode).
+
endmenu
config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index daac675..863f470 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
+obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
# These Sparc drivers have to appear before others such as 8250
# which share ttySx minor node space. Otherwise console device
@@ -40,12 +41,10 @@
obj-$(CONFIG_SERIAL_DZ) += dz.o
obj-$(CONFIG_SERIAL_ZS) += zs.o
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
-obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o
obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
obj-$(CONFIG_SERIAL_IMX) += imx.o
obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
obj-$(CONFIG_SERIAL_ICOM) += icom.o
-obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
obj-$(CONFIG_SERIAL_MESON) += meson_uart.o
obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
obj-$(CONFIG_SERIAL_SCCNXP) += sccnxp.o
@@ -53,14 +52,10 @@
obj-$(CONFIG_SERIAL_JSM) += jsm/
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
-obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
-obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
-obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
-obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o
@@ -76,11 +71,13 @@
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o
+obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o
obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o
obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
obj-$(CONFIG_SERIAL_ARC) += arc_uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
obj-$(CONFIG_SERIAL_FSL_LPUART) += fsl_lpuart.o
+obj-$(CONFIG_SERIAL_FSL_LINFLEXUART) += fsl_linflexuart.o
obj-$(CONFIG_SERIAL_CONEXANT_DIGICOLOR) += digicolor-usart.o
obj-$(CONFIG_SERIAL_MEN_Z135) += men_z135_uart.o
obj-$(CONFIG_SERIAL_SPRD) += sprd_serial.o
@@ -89,6 +86,9 @@
obj-$(CONFIG_SERIAL_PIC32) += pic32_uart.o
obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o
obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
+obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
+obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
+obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index ebd33c0..3a7d1a6 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1717,7 +1717,7 @@
{
pl011_write(uap->im, uap, REG_IMSC);
- return request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
+ return request_irq(uap->port.irq, pl011_int, IRQF_SHARED, "uart-pl011", uap);
}
/*
@@ -2718,11 +2718,8 @@
return -ENOMEM;
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(&pdev->dev, "cannot obtain irq\n");
+ if (ret < 0)
return ret;
- }
uap->port.irq = ret;
#ifdef CONFIG_ACPI_SPCR_TABLE
@@ -2780,6 +2777,7 @@
.name = "sbsa-uart",
.of_match_table = of_match_ptr(sbsa_uart_of_match),
.acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match),
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
},
};
@@ -2808,6 +2806,7 @@
.drv = {
.name = "uart-pl011",
.pm = &pl011_dev_pm_ops,
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
},
.id_table = pl011_ids,
.probe = pl011_probe,
diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c
index db5df3d..3bdd56a 100644
--- a/drivers/tty/serial/ar933x_uart.c
+++ b/drivers/tty/serial/ar933x_uart.c
@@ -49,11 +49,6 @@
struct clk *clk;
};
-static inline bool ar933x_uart_console_enabled(void)
-{
- return IS_ENABLED(CONFIG_SERIAL_AR933X_CONSOLE);
-}
-
static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
int offset)
{
@@ -508,6 +503,7 @@
.verify_port = ar933x_uart_verify_port,
};
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
static struct ar933x_uart_port *
ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
@@ -604,14 +600,7 @@
.index = -1,
.data = &ar933x_uart_driver,
};
-
-static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
-{
- if (!ar933x_uart_console_enabled())
- return;
-
- ar933x_console_ports[up->port.line] = up;
-}
+#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
static struct uart_driver ar933x_uart_driver = {
.owner = THIS_MODULE,
@@ -700,7 +689,9 @@
baud = ar933x_uart_get_baud(port->uartclk, 0, AR933X_UART_MAX_STEP);
up->max_baud = min_t(unsigned int, baud, AR933X_UART_MAX_BAUD);
- ar933x_uart_add_console_port(up);
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_console_ports[up->port.line] = up;
+#endif
ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
if (ret)
@@ -749,8 +740,9 @@
{
int ret;
- if (ar933x_uart_console_enabled())
- ar933x_uart_driver.cons = &ar933x_uart_console;
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+ ar933x_uart_driver.cons = &ar933x_uart_console;
+#endif
ret = uart_register_driver(&ar933x_uart_driver);
if (ret)
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 8e44287..a8dc8af 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -34,6 +34,7 @@
#include <linux/suspend.h>
#include <linux/mm.h>
+#include <asm/div64.h>
#include <asm/io.h>
#include <asm/ioctls.h>
@@ -147,6 +148,8 @@
struct circ_buf rx_ring;
struct mctrl_gpios *gpios;
+ u32 backup_mode; /* MR saved during iso7816 operations */
+ u32 backup_brgr; /* BRGR saved during iso7816 operations */
unsigned int tx_done_mask;
u32 fifo_size;
u32 rts_high;
@@ -163,6 +166,12 @@
unsigned int pending_status;
spinlock_t lock_suspended;
+ bool hd_start_rx; /* can start RX during half-duplex operation */
+
+ /* ISO7816 */
+ unsigned int fidi_min;
+ unsigned int fidi_max;
+
#ifdef CONFIG_PM
struct {
u32 cr;
@@ -193,8 +202,7 @@
#if defined(CONFIG_OF)
static const struct of_device_id atmel_serial_dt_ids[] = {
- { .compatible = "atmel,at91rm9200-usart" },
- { .compatible = "atmel,at91sam9260-usart" },
+ { .compatible = "atmel,at91rm9200-usart-serial" },
{ /* sentinel */ }
};
#endif
@@ -225,6 +233,13 @@
__raw_writeb(value, port->membase + ATMEL_US_THR);
}
+static inline int atmel_uart_is_half_duplex(struct uart_port *port)
+{
+ return ((port->rs485.flags & SER_RS485_ENABLED) &&
+ !(port->rs485.flags & SER_RS485_RX_DURING_TX)) ||
+ (port->iso7816.flags & SER_ISO7816_ENABLED);
+}
+
#ifdef CONFIG_SERIAL_ATMEL_PDC
static bool atmel_use_pdc_rx(struct uart_port *port)
{
@@ -279,50 +294,6 @@
tasklet_schedule(t);
}
-static unsigned int atmel_get_lines_status(struct uart_port *port)
-{
- struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- unsigned int status, ret = 0;
-
- status = atmel_uart_readl(port, ATMEL_US_CSR);
-
- mctrl_gpio_get(atmel_port->gpios, &ret);
-
- if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
- UART_GPIO_CTS))) {
- if (ret & TIOCM_CTS)
- status &= ~ATMEL_US_CTS;
- else
- status |= ATMEL_US_CTS;
- }
-
- if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
- UART_GPIO_DSR))) {
- if (ret & TIOCM_DSR)
- status &= ~ATMEL_US_DSR;
- else
- status |= ATMEL_US_DSR;
- }
-
- if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
- UART_GPIO_RI))) {
- if (ret & TIOCM_RI)
- status &= ~ATMEL_US_RI;
- else
- status |= ATMEL_US_RI;
- }
-
- if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
- UART_GPIO_DCD))) {
- if (ret & TIOCM_CD)
- status &= ~ATMEL_US_DCD;
- else
- status |= ATMEL_US_DCD;
- }
-
- return status;
-}
-
/* Enable or disable the rs485 support */
static int atmel_config_rs485(struct uart_port *port,
struct serial_rs485 *rs485conf)
@@ -362,6 +333,127 @@
return 0;
}
+static unsigned int atmel_calc_cd(struct uart_port *port,
+ struct serial_iso7816 *iso7816conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int cd;
+ u64 mck_rate;
+
+ mck_rate = (u64)clk_get_rate(atmel_port->clk);
+ do_div(mck_rate, iso7816conf->clk);
+ cd = mck_rate;
+ return cd;
+}
+
+static unsigned int atmel_calc_fidi(struct uart_port *port,
+ struct serial_iso7816 *iso7816conf)
+{
+ u64 fidi = 0;
+
+ if (iso7816conf->sc_fi && iso7816conf->sc_di) {
+ fidi = (u64)iso7816conf->sc_fi;
+ do_div(fidi, iso7816conf->sc_di);
+ }
+ return (u32)fidi;
+}
+
+/* Enable or disable the iso7816 support */
+/* Called with interrupts disabled */
+static int atmel_config_iso7816(struct uart_port *port,
+ struct serial_iso7816 *iso7816conf)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int mode;
+ unsigned int cd, fidi;
+ int ret = 0;
+
+ /* Disable interrupts */
+ atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
+
+ mode = atmel_uart_readl(port, ATMEL_US_MR);
+
+ if (iso7816conf->flags & SER_ISO7816_ENABLED) {
+ mode &= ~ATMEL_US_USMODE;
+
+ if (iso7816conf->tg > 255) {
+ dev_err(port->dev, "ISO7816: Timeguard exceeding 255\n");
+ memset(iso7816conf, 0, sizeof(struct serial_iso7816));
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if ((iso7816conf->flags & SER_ISO7816_T_PARAM)
+ == SER_ISO7816_T(0)) {
+ mode |= ATMEL_US_USMODE_ISO7816_T0 | ATMEL_US_DSNACK;
+ } else if ((iso7816conf->flags & SER_ISO7816_T_PARAM)
+ == SER_ISO7816_T(1)) {
+ mode |= ATMEL_US_USMODE_ISO7816_T1 | ATMEL_US_INACK;
+ } else {
+ dev_err(port->dev, "ISO7816: Type not supported\n");
+ memset(iso7816conf, 0, sizeof(struct serial_iso7816));
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ mode &= ~(ATMEL_US_USCLKS | ATMEL_US_NBSTOP | ATMEL_US_PAR);
+
+ /* select mck clock, and output */
+ mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO;
+ /* set parity for normal/inverse mode + max iterations */
+ mode |= ATMEL_US_PAR_EVEN | ATMEL_US_NBSTOP_1 | ATMEL_US_MAX_ITER(3);
+
+ cd = atmel_calc_cd(port, iso7816conf);
+ fidi = atmel_calc_fidi(port, iso7816conf);
+ if (fidi == 0) {
+ dev_warn(port->dev, "ISO7816 fidi = 0, Generator generates no signal\n");
+ } else if (fidi < atmel_port->fidi_min
+ || fidi > atmel_port->fidi_max) {
+ dev_err(port->dev, "ISO7816 fidi = %u, value not supported\n", fidi);
+ memset(iso7816conf, 0, sizeof(struct serial_iso7816));
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (!(port->iso7816.flags & SER_ISO7816_ENABLED)) {
+ /* port not yet in iso7816 mode: store configuration */
+ atmel_port->backup_mode = atmel_uart_readl(port, ATMEL_US_MR);
+ atmel_port->backup_brgr = atmel_uart_readl(port, ATMEL_US_BRGR);
+ }
+
+ atmel_uart_writel(port, ATMEL_US_TTGR, iso7816conf->tg);
+ atmel_uart_writel(port, ATMEL_US_BRGR, cd);
+ atmel_uart_writel(port, ATMEL_US_FIDI, fidi);
+
+ atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXEN);
+ atmel_port->tx_done_mask = ATMEL_US_TXEMPTY | ATMEL_US_NACK | ATMEL_US_ITERATION;
+ } else {
+ dev_dbg(port->dev, "Setting UART back to RS232\n");
+ /* back to last RS232 settings */
+ mode = atmel_port->backup_mode;
+ memset(iso7816conf, 0, sizeof(struct serial_iso7816));
+ atmel_uart_writel(port, ATMEL_US_TTGR, 0);
+ atmel_uart_writel(port, ATMEL_US_BRGR, atmel_port->backup_brgr);
+ atmel_uart_writel(port, ATMEL_US_FIDI, 0x174);
+
+ if (atmel_use_pdc_tx(port))
+ atmel_port->tx_done_mask = ATMEL_US_ENDTX |
+ ATMEL_US_TXBUFE;
+ else
+ atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+ }
+
+ port->iso7816 = *iso7816conf;
+
+ atmel_uart_writel(port, ATMEL_US_MR, mode);
+
+err_out:
+ /* Enable interrupts */
+ atmel_uart_writel(port, ATMEL_US_IER, atmel_port->tx_done_mask);
+
+ return ret;
+}
+
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
*/
@@ -481,9 +573,9 @@
/* Disable interrupts */
atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask);
- if ((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX))
+ if (atmel_uart_is_half_duplex(port))
atmel_start_rx(port);
+
}
/*
@@ -500,8 +592,7 @@
return;
if (atmel_use_pdc_tx(port) || atmel_use_dma_tx(port))
- if ((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX))
+ if (atmel_uart_is_half_duplex(port))
atmel_stop_rx(port);
if (atmel_use_pdc_tx(port))
@@ -799,10 +890,14 @@
*/
if (!uart_circ_empty(xmit))
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
- else if ((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
- /* DMA done, stop TX, start RX for RS485 */
- atmel_start_rx(port);
+ else if (atmel_uart_is_half_duplex(port)) {
+ /*
+ * DMA done, re-enable TXEMPTY and signal that we can stop
+ * TX and start RX for RS485
+ */
+ atmel_port->hd_start_rx = true;
+ atmel_uart_writel(port, ATMEL_US_IER,
+ atmel_port->tx_done_mask);
}
spin_unlock_irqrestore(&port->lock, flags);
@@ -915,6 +1010,7 @@
static int atmel_prepare_tx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
dma_cap_mask_t mask;
struct dma_slave_config config;
int ret, nent;
@@ -922,7 +1018,7 @@
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+ atmel_port->chan_tx = dma_request_slave_channel(mfd_dev, "tx");
if (atmel_port->chan_tx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for tx DMA transfers\n",
@@ -1093,6 +1189,7 @@
static int atmel_prepare_rx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
struct dma_async_tx_descriptor *desc;
dma_cap_mask_t mask;
struct dma_slave_config config;
@@ -1104,7 +1201,7 @@
dma_cap_zero(mask);
dma_cap_set(DMA_CYCLIC, mask);
- atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+ atmel_port->chan_rx = dma_request_slave_channel(mfd_dev, "rx");
if (atmel_port->chan_rx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for rx DMA transfers\n",
@@ -1156,6 +1253,10 @@
sg_dma_len(&atmel_port->sg_rx)/2,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(port->dev, "Preparing DMA cyclic failed\n");
+ goto chan_err;
+ }
desc->callback = atmel_complete_rx_dma;
desc->callback_param = port;
atmel_port->desc_rx = desc;
@@ -1244,9 +1345,19 @@
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
if (pending & atmel_port->tx_done_mask) {
- /* Either PDC or interrupt transmission */
atmel_uart_writel(port, ATMEL_US_IDR,
atmel_port->tx_done_mask);
+
+ /* Start RX if flag was set and FIFO is empty */
+ if (atmel_port->hd_start_rx) {
+ if (!(atmel_uart_readl(port, ATMEL_US_CSR)
+ & ATMEL_US_TXEMPTY))
+ dev_warn(port->dev, "Should start RX, but TX fifo is not empty\n");
+
+ atmel_port->hd_start_rx = false;
+ atmel_start_rx(port);
+ }
+
atmel_tasklet_schedule(atmel_port, &atmel_port->tasklet_tx);
}
}
@@ -1281,6 +1392,9 @@
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
}
+
+ if (pending & (ATMEL_US_NACK | ATMEL_US_ITERATION))
+ dev_dbg(port->dev, "ISO7816 ERROR (0x%08x)\n", pending);
}
/*
@@ -1295,7 +1409,7 @@
spin_lock(&atmel_port->lock_suspended);
do {
- status = atmel_get_lines_status(port);
+ status = atmel_uart_readl(port, ATMEL_US_CSR);
mask = atmel_uart_readl(port, ATMEL_US_IMR);
pending = status & mask;
if (!pending)
@@ -1373,8 +1487,7 @@
atmel_uart_writel(port, ATMEL_US_IER,
atmel_port->tx_done_mask);
} else {
- if ((port->rs485.flags & SER_RS485_ENABLED) &&
- !(port->rs485.flags & SER_RS485_RX_DURING_TX)) {
+ if (atmel_uart_is_half_duplex(port)) {
/* DMA done, stop TX, start RX for RS485 */
atmel_start_rx(port);
}
@@ -1726,6 +1839,22 @@
atmel_port->has_frac_baudrate = true;
atmel_port->has_hw_timer = true;
atmel_port->rtor = ATMEL_US_RTOR;
+ version = atmel_uart_readl(port, ATMEL_US_VERSION);
+ switch (version) {
+ case 0x814: /* sama5d2 */
+ /* fall through */
+ case 0x701: /* sama5d4 */
+ atmel_port->fidi_min = 3;
+ atmel_port->fidi_max = 65535;
+ break;
+ case 0x502: /* sam9x5, sama5d3 */
+ atmel_port->fidi_min = 3;
+ atmel_port->fidi_max = 2047;
+ break;
+ default:
+ atmel_port->fidi_min = 1;
+ atmel_port->fidi_max = 2047;
+ }
} else if (name == dbgu_uart) {
dev_dbg(port->dev, "Dbgu or uart without hw timer\n");
} else {
@@ -1829,7 +1958,7 @@
}
/* Save current CSR for comparison in atmel_tasklet_func() */
- atmel_port->irq_status_prev = atmel_get_lines_status(port);
+ atmel_port->irq_status_prev = atmel_uart_readl(port, ATMEL_US_CSR);
/*
* Finally, enable the serial port
@@ -2099,6 +2228,17 @@
atmel_uart_writel(port, ATMEL_US_TTGR,
port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
+ } else if (port->iso7816.flags & SER_ISO7816_ENABLED) {
+ atmel_uart_writel(port, ATMEL_US_TTGR, port->iso7816.tg);
+ /* select mck clock, and output */
+ mode |= ATMEL_US_USCLKS_MCK | ATMEL_US_CLKO;
+ /* set max iterations */
+ mode |= ATMEL_US_MAX_ITER(3);
+ if ((port->iso7816.flags & SER_ISO7816_T_PARAM)
+ == SER_ISO7816_T(0))
+ mode |= ATMEL_US_USMODE_ISO7816_T0;
+ else
+ mode |= ATMEL_US_USMODE_ISO7816_T1;
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
if (atmel_use_fifo(port) &&
@@ -2175,7 +2315,8 @@
}
quot = cd | fp << ATMEL_US_FP_OFFSET;
- atmel_uart_writel(port, ATMEL_US_BRGR, quot);
+ if (!(port->iso7816.flags & SER_ISO7816_ENABLED))
+ atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
atmel_port->tx_stopped = false;
@@ -2222,8 +2363,8 @@
*/
static void atmel_release_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
release_mem_region(port->mapbase, size);
@@ -2238,8 +2379,8 @@
*/
static int atmel_request_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
if (!request_mem_region(port->mapbase, size, "atmel_serial"))
return -EBUSY;
@@ -2341,27 +2482,29 @@
{
int ret;
struct uart_port *port = &atmel_port->uart;
+ struct platform_device *mpdev = to_platform_device(pdev->dev.parent);
atmel_init_property(atmel_port, pdev);
atmel_set_ops(port);
- uart_get_rs485_mode(&pdev->dev, &port->rs485);
+ uart_get_rs485_mode(&mpdev->dev, &port->rs485);
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
port->ops = &atmel_pops;
port->fifosize = 1;
port->dev = &pdev->dev;
- port->mapbase = pdev->resource[0].start;
- port->irq = pdev->resource[1].start;
+ port->mapbase = mpdev->resource[0].start;
+ port->irq = mpdev->resource[1].start;
port->rs485_config = atmel_config_rs485;
- port->membase = NULL;
+ port->iso7816_config = atmel_config_iso7816;
+ port->membase = NULL;
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
- atmel_port->clk = clk_get(&pdev->dev, "usart");
+ atmel_port->clk = clk_get(&mpdev->dev, "usart");
if (IS_ERR(atmel_port->clk)) {
ret = PTR_ERR(atmel_port->clk);
atmel_port->clk = NULL;
@@ -2378,8 +2521,12 @@
/* only enable clock when USART is in use */
}
- /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
- if (port->rs485.flags & SER_RS485_ENABLED)
+ /*
+ * Use TXEMPTY for interrupt when rs485 or ISO7816 else TXRDY or
+ * ENDTX|TXBUFE
+ */
+ if (port->rs485.flags & SER_RS485_ENABLED ||
+ port->iso7816.flags & SER_ISO7816_ENABLED)
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
else if (atmel_use_pdc_tx(port)) {
port->fifosize = PDC_BUFFER_SIZE;
@@ -2694,13 +2841,22 @@
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *atmel_port;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = pdev->dev.parent->of_node;
void *data;
- int ret = -ENODEV;
+ int ret;
bool rs485_enabled;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
+ /*
+ * In device tree there is no node with "atmel,at91rm9200-usart-serial"
+ * as compatible string. This driver is probed by at91-usart mfd driver
+ * which is just a wrapper over the atmel_serial driver and
+ * spi-at91-usart driver. All attributes needed by this driver are
+ * found in of_node of parent.
+ */
+ pdev->dev.of_node = np;
+
ret = of_alias_get_id(np, "serial");
if (ret < 0)
/* port id not found in platform data nor device-tree aliases:
@@ -2836,6 +2992,7 @@
clk_put(atmel_port->clk);
atmel_port->clk = NULL;
+ pdev->dev.of_node = NULL;
return ret;
}
@@ -2846,7 +3003,7 @@
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.driver = {
- .name = "atmel_usart",
+ .name = "atmel_usart_serial",
.of_match_table = of_match_ptr(atmel_serial_dt_ids),
},
};
diff --git a/drivers/tty/serial/atmel_serial.h b/drivers/tty/serial/atmel_serial.h
index ba3a243..d811d4f 100644
--- a/drivers/tty/serial/atmel_serial.h
+++ b/drivers/tty/serial/atmel_serial.h
@@ -78,7 +78,8 @@
#define ATMEL_US_OVER BIT(19) /* Oversampling Mode */
#define ATMEL_US_INACK BIT(20) /* Inhibit Non Acknowledge */
#define ATMEL_US_DSNACK BIT(21) /* Disable Successive NACK */
-#define ATMEL_US_MAX_ITER GENMASK(26, 24) /* Max Iterations */
+#define ATMEL_US_MAX_ITER_MASK GENMASK(26, 24) /* Max Iterations */
+#define ATMEL_US_MAX_ITER(n) (((n) << 24) & ATMEL_US_MAX_ITER_MASK)
#define ATMEL_US_FILTER BIT(28) /* Infrared Receive Line Filter */
#define ATMEL_US_IER 0x08 /* Interrupt Enable Register */
diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c
index 98f193a..0615907 100644
--- a/drivers/tty/serial/clps711x.c
+++ b/drivers/tty/serial/clps711x.c
@@ -442,14 +442,10 @@
static int uart_clps711x_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
- int ret, index = np ? of_alias_get_id(np, "serial") : pdev->id;
struct clps711x_port *s;
struct resource *res;
struct clk *uart_clk;
- int irq;
-
- if (index < 0 || index >= UART_CLPS711X_NR)
- return -EINVAL;
+ int irq, ret;
s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s)
@@ -473,20 +469,11 @@
if (s->rx_irq < 0)
return s->rx_irq;
- if (!np) {
- char syscon_name[9];
+ s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(s->syscon))
+ return PTR_ERR(s->syscon);
- sprintf(syscon_name, "syscon.%i", index + 1);
- s->syscon = syscon_regmap_lookup_by_pdevname(syscon_name);
- if (IS_ERR(s->syscon))
- return PTR_ERR(s->syscon);
- } else {
- s->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
- if (IS_ERR(s->syscon))
- return PTR_ERR(s->syscon);
- }
-
- s->port.line = index;
+ s->port.line = of_alias_get_id(np, "serial");
s->port.dev = &pdev->dev;
s->port.iotype = UPIO_MEM32;
s->port.mapbase = res->start;
diff --git a/drivers/tty/serial/cpm_uart/Makefile b/drivers/tty/serial/cpm_uart/Makefile
index 896a5d5..3f3a6ed 100644
--- a/drivers/tty/serial/cpm_uart/Makefile
+++ b/drivers/tty/serial/cpm_uart/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Motorola 8xx FEC ethernet controller
#
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index e538959..de6d02f 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -24,7 +24,7 @@
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/dma-mapping.h>
#include <linux/fs_uart_pd.h>
#include <linux/of_address.h>
@@ -407,7 +407,16 @@
clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
}
cpm_uart_initbd(pinfo);
- cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
+ if (IS_SMC(pinfo)) {
+ out_be32(&pinfo->smcup->smc_rstate, 0);
+ out_be32(&pinfo->smcup->smc_tstate, 0);
+ out_be16(&pinfo->smcup->smc_rbptr,
+ in_be16(&pinfo->smcup->smc_rbase));
+ out_be16(&pinfo->smcup->smc_tbptr,
+ in_be16(&pinfo->smcup->smc_tbase));
+ } else {
+ cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
+ }
}
/* Install interrupt handler. */
retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port);
@@ -567,8 +576,6 @@
/*
* Set up parity check flag
*/
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);
if (termios->c_iflag & INPCK)
port->read_status_mask |= BD_SC_FR | BD_SC_PR;
@@ -861,16 +868,14 @@
(u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE);
/*
- * In case SMC1 is being relocated...
+ * In case SMC is being relocated...
*/
-#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH)
out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase));
out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase));
out_be32(&up->smc_rstate, 0);
out_be32(&up->smc_tstate, 0);
out_be16(&up->smc_brkcr, 1); /* number of break chars */
out_be16(&up->smc_brkec, 0);
-#endif
/* Set up the uart parameters in the
* parameter ram.
@@ -884,8 +889,6 @@
out_be16(&up->smc_brkec, 0);
out_be16(&up->smc_brkcr, 1);
- cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
-
/* Set UART mode, 8 bit, no parity, one stop.
* Enable receive and transmit.
*/
@@ -1155,8 +1158,8 @@
if (!pinfo->clk) {
data = of_get_property(np, "fsl,cpm-brg", &len);
if (!data || len != 4) {
- printk(KERN_ERR "CPM UART %s has no/invalid "
- "fsl,cpm-brg property.\n", np->name);
+ printk(KERN_ERR "CPM UART %pOFn has no/invalid "
+ "fsl,cpm-brg property.\n", np);
return -EINVAL;
}
pinfo->brg = *data;
@@ -1164,8 +1167,8 @@
data = of_get_property(np, "fsl,cpm-command", &len);
if (!data || len != 4) {
- printk(KERN_ERR "CPM UART %s has no/invalid "
- "fsl,cpm-command property.\n", np->name);
+ printk(KERN_ERR "CPM UART %pOFn has no/invalid "
+ "fsl,cpm-command property.\n", np);
return -EINVAL;
}
pinfo->command = *data;
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
index 4eba17f..56fc527 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
@@ -19,7 +19,7 @@
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
index e3bff06..6a1cd03 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
@@ -19,7 +19,7 @@
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/dma-mapping.h>
#include <asm/io.h>
diff --git a/drivers/tty/serial/digicolor-usart.c b/drivers/tty/serial/digicolor-usart.c
index f460cca..13ac36e 100644
--- a/drivers/tty/serial/digicolor-usart.c
+++ b/drivers/tty/serial/digicolor-usart.c
@@ -541,7 +541,11 @@
if (ret)
return ret;
- return platform_driver_register(&digicolor_uart_platform);
+ ret = platform_driver_register(&digicolor_uart_platform);
+ if (ret)
+ uart_unregister_driver(&digicolor_uart);
+
+ return ret;
}
module_init(digicolor_uart_init);
diff --git a/drivers/tty/serial/earlycon-riscv-sbi.c b/drivers/tty/serial/earlycon-riscv-sbi.c
new file mode 100644
index 0000000..ce81523
--- /dev/null
+++ b/drivers/tty/serial/earlycon-riscv-sbi.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RISC-V SBI based earlycon
+ *
+ * Copyright (C) 2018 Anup Patel <anup@brainfault.org>
+ */
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <asm/sbi.h>
+
+static void sbi_putc(struct uart_port *port, int c)
+{
+ sbi_console_putchar(c);
+}
+
+static void sbi_console_write(struct console *con,
+ const char *s, unsigned n)
+{
+ struct earlycon_device *dev = con->data;
+ uart_console_write(&dev->port, s, n, sbi_putc);
+}
+
+static int __init early_sbi_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ device->con->write = sbi_console_write;
+ return 0;
+}
+EARLYCON_DECLARE(sbi, early_sbi_setup);
diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c
new file mode 100644
index 0000000..a32f0d2
--- /dev/null
+++ b/drivers/tty/serial/fsl_linflexuart.c
@@ -0,0 +1,944 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale linflexuart serial port driver
+ *
+ * Copyright 2012-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2019 NXP
+ */
+
+#if defined(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE) && \
+ defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+
+/* All registers are 32-bit width */
+
+#define LINCR1 0x0000 /* LIN control register */
+#define LINIER 0x0004 /* LIN interrupt enable register */
+#define LINSR 0x0008 /* LIN status register */
+#define LINESR 0x000C /* LIN error status register */
+#define UARTCR 0x0010 /* UART mode control register */
+#define UARTSR 0x0014 /* UART mode status register */
+#define LINTCSR 0x0018 /* LIN timeout control status register */
+#define LINOCR 0x001C /* LIN output compare register */
+#define LINTOCR 0x0020 /* LIN timeout control register */
+#define LINFBRR 0x0024 /* LIN fractional baud rate register */
+#define LINIBRR 0x0028 /* LIN integer baud rate register */
+#define LINCFR 0x002C /* LIN checksum field register */
+#define LINCR2 0x0030 /* LIN control register 2 */
+#define BIDR 0x0034 /* Buffer identifier register */
+#define BDRL 0x0038 /* Buffer data register least significant */
+#define BDRM 0x003C /* Buffer data register most significant */
+#define IFER 0x0040 /* Identifier filter enable register */
+#define IFMI 0x0044 /* Identifier filter match index */
+#define IFMR 0x0048 /* Identifier filter mode register */
+#define GCR 0x004C /* Global control register */
+#define UARTPTO 0x0050 /* UART preset timeout register */
+#define UARTCTO 0x0054 /* UART current timeout register */
+
+/*
+ * Register field definitions
+ */
+
+#define LINFLEXD_LINCR1_INIT BIT(0)
+#define LINFLEXD_LINCR1_MME BIT(4)
+#define LINFLEXD_LINCR1_BF BIT(7)
+
+#define LINFLEXD_LINSR_LINS_INITMODE BIT(12)
+#define LINFLEXD_LINSR_LINS_MASK (0xF << 12)
+
+#define LINFLEXD_LINIER_SZIE BIT(15)
+#define LINFLEXD_LINIER_OCIE BIT(14)
+#define LINFLEXD_LINIER_BEIE BIT(13)
+#define LINFLEXD_LINIER_CEIE BIT(12)
+#define LINFLEXD_LINIER_HEIE BIT(11)
+#define LINFLEXD_LINIER_FEIE BIT(8)
+#define LINFLEXD_LINIER_BOIE BIT(7)
+#define LINFLEXD_LINIER_LSIE BIT(6)
+#define LINFLEXD_LINIER_WUIE BIT(5)
+#define LINFLEXD_LINIER_DBFIE BIT(4)
+#define LINFLEXD_LINIER_DBEIETOIE BIT(3)
+#define LINFLEXD_LINIER_DRIE BIT(2)
+#define LINFLEXD_LINIER_DTIE BIT(1)
+#define LINFLEXD_LINIER_HRIE BIT(0)
+
+#define LINFLEXD_UARTCR_OSR_MASK (0xF << 24)
+#define LINFLEXD_UARTCR_OSR(uartcr) (((uartcr) \
+ & LINFLEXD_UARTCR_OSR_MASK) >> 24)
+
+#define LINFLEXD_UARTCR_ROSE BIT(23)
+
+#define LINFLEXD_UARTCR_RFBM BIT(9)
+#define LINFLEXD_UARTCR_TFBM BIT(8)
+#define LINFLEXD_UARTCR_WL1 BIT(7)
+#define LINFLEXD_UARTCR_PC1 BIT(6)
+
+#define LINFLEXD_UARTCR_RXEN BIT(5)
+#define LINFLEXD_UARTCR_TXEN BIT(4)
+#define LINFLEXD_UARTCR_PC0 BIT(3)
+
+#define LINFLEXD_UARTCR_PCE BIT(2)
+#define LINFLEXD_UARTCR_WL0 BIT(1)
+#define LINFLEXD_UARTCR_UART BIT(0)
+
+#define LINFLEXD_UARTSR_SZF BIT(15)
+#define LINFLEXD_UARTSR_OCF BIT(14)
+#define LINFLEXD_UARTSR_PE3 BIT(13)
+#define LINFLEXD_UARTSR_PE2 BIT(12)
+#define LINFLEXD_UARTSR_PE1 BIT(11)
+#define LINFLEXD_UARTSR_PE0 BIT(10)
+#define LINFLEXD_UARTSR_RMB BIT(9)
+#define LINFLEXD_UARTSR_FEF BIT(8)
+#define LINFLEXD_UARTSR_BOF BIT(7)
+#define LINFLEXD_UARTSR_RPS BIT(6)
+#define LINFLEXD_UARTSR_WUF BIT(5)
+#define LINFLEXD_UARTSR_4 BIT(4)
+
+#define LINFLEXD_UARTSR_TO BIT(3)
+
+#define LINFLEXD_UARTSR_DRFRFE BIT(2)
+#define LINFLEXD_UARTSR_DTFTFF BIT(1)
+#define LINFLEXD_UARTSR_NF BIT(0)
+#define LINFLEXD_UARTSR_PE (LINFLEXD_UARTSR_PE0 |\
+ LINFLEXD_UARTSR_PE1 |\
+ LINFLEXD_UARTSR_PE2 |\
+ LINFLEXD_UARTSR_PE3)
+
+#define LINFLEX_LDIV_MULTIPLIER (16)
+
+#define DRIVER_NAME "fsl-linflexuart"
+#define DEV_NAME "ttyLF"
+#define UART_NR 4
+
+#define EARLYCON_BUFFER_INITIAL_CAP 8
+
+#define PREINIT_DELAY 2000 /* us */
+
+static const struct of_device_id linflex_dt_ids[] = {
+ {
+ .compatible = "fsl,s32v234-linflexuart",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, linflex_dt_ids);
+
+#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
+static struct uart_port *earlycon_port;
+static bool linflex_earlycon_same_instance;
+static DEFINE_SPINLOCK(init_lock);
+static bool during_init;
+
+static struct {
+ char *content;
+ unsigned int len, cap;
+} earlycon_buf;
+#endif
+
+static void linflex_stop_tx(struct uart_port *port)
+{
+ unsigned long ier;
+
+ ier = readl(port->membase + LINIER);
+ ier &= ~(LINFLEXD_LINIER_DTIE);
+ writel(ier, port->membase + LINIER);
+}
+
+static void linflex_stop_rx(struct uart_port *port)
+{
+ unsigned long ier;
+
+ ier = readl(port->membase + LINIER);
+ writel(ier & ~LINFLEXD_LINIER_DRIE, port->membase + LINIER);
+}
+
+static inline void linflex_transmit_buffer(struct uart_port *sport)
+{
+ struct circ_buf *xmit = &sport->state->xmit;
+ unsigned char c;
+ unsigned long status;
+
+ while (!uart_circ_empty(xmit)) {
+ c = xmit->buf[xmit->tail];
+ writeb(c, sport->membase + BDRL);
+
+ /* Waiting for data transmission completed. */
+ while (((status = readl(sport->membase + UARTSR)) &
+ LINFLEXD_UARTSR_DTFTFF) !=
+ LINFLEXD_UARTSR_DTFTFF)
+ ;
+
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ sport->icount.tx++;
+
+ writel(status | LINFLEXD_UARTSR_DTFTFF,
+ sport->membase + UARTSR);
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(sport);
+
+ if (uart_circ_empty(xmit))
+ linflex_stop_tx(sport);
+}
+
+static void linflex_start_tx(struct uart_port *port)
+{
+ unsigned long ier;
+
+ linflex_transmit_buffer(port);
+ ier = readl(port->membase + LINIER);
+ writel(ier | LINFLEXD_LINIER_DTIE, port->membase + LINIER);
+}
+
+static irqreturn_t linflex_txint(int irq, void *dev_id)
+{
+ struct uart_port *sport = dev_id;
+ struct circ_buf *xmit = &sport->state->xmit;
+ unsigned long flags;
+ unsigned long status;
+
+ spin_lock_irqsave(&sport->lock, flags);
+
+ if (sport->x_char) {
+ writeb(sport->x_char, sport->membase + BDRL);
+
+ /* waiting for data transmission completed */
+ while (((status = readl(sport->membase + UARTSR)) &
+ LINFLEXD_UARTSR_DTFTFF) != LINFLEXD_UARTSR_DTFTFF)
+ ;
+
+ writel(status | LINFLEXD_UARTSR_DTFTFF,
+ sport->membase + UARTSR);
+
+ goto out;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(sport)) {
+ linflex_stop_tx(sport);
+ goto out;
+ }
+
+ linflex_transmit_buffer(sport);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(sport);
+
+out:
+ spin_unlock_irqrestore(&sport->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t linflex_rxint(int irq, void *dev_id)
+{
+ struct uart_port *sport = dev_id;
+ unsigned int flg;
+ struct tty_port *port = &sport->state->port;
+ unsigned long flags, status;
+ unsigned char rx;
+ bool brk;
+
+ spin_lock_irqsave(&sport->lock, flags);
+
+ status = readl(sport->membase + UARTSR);
+ while (status & LINFLEXD_UARTSR_RMB) {
+ rx = readb(sport->membase + BDRM);
+ brk = false;
+ flg = TTY_NORMAL;
+ sport->icount.rx++;
+
+ if (status & (LINFLEXD_UARTSR_BOF | LINFLEXD_UARTSR_SZF |
+ LINFLEXD_UARTSR_FEF | LINFLEXD_UARTSR_PE)) {
+ if (status & LINFLEXD_UARTSR_SZF)
+ status |= LINFLEXD_UARTSR_SZF;
+ if (status & LINFLEXD_UARTSR_BOF)
+ status |= LINFLEXD_UARTSR_BOF;
+ if (status & LINFLEXD_UARTSR_FEF) {
+ if (!rx)
+ brk = true;
+ status |= LINFLEXD_UARTSR_FEF;
+ }
+ if (status & LINFLEXD_UARTSR_PE)
+ status |= LINFLEXD_UARTSR_PE;
+ }
+
+ writel(status | LINFLEXD_UARTSR_RMB | LINFLEXD_UARTSR_DRFRFE,
+ sport->membase + UARTSR);
+ status = readl(sport->membase + UARTSR);
+
+ if (brk) {
+ uart_handle_break(sport);
+ } else {
+#ifdef SUPPORT_SYSRQ
+ if (uart_handle_sysrq_char(sport, (unsigned char)rx))
+ continue;
+#endif
+ tty_insert_flip_char(port, rx, flg);
+ }
+ }
+
+ spin_unlock_irqrestore(&sport->lock, flags);
+
+ tty_flip_buffer_push(port);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t linflex_int(int irq, void *dev_id)
+{
+ struct uart_port *sport = dev_id;
+ unsigned long status;
+
+ status = readl(sport->membase + UARTSR);
+
+ if (status & LINFLEXD_UARTSR_DRFRFE)
+ linflex_rxint(irq, dev_id);
+ if (status & LINFLEXD_UARTSR_DTFTFF)
+ linflex_txint(irq, dev_id);
+
+ return IRQ_HANDLED;
+}
+
+/* return TIOCSER_TEMT when transmitter is not busy */
+static unsigned int linflex_tx_empty(struct uart_port *port)
+{
+ unsigned long status;
+
+ status = readl(port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF;
+
+ return status ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int linflex_get_mctrl(struct uart_port *port)
+{
+ return 0;
+}
+
+static void linflex_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void linflex_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void linflex_setup_watermark(struct uart_port *sport)
+{
+ unsigned long cr, ier, cr1;
+
+ /* Disable transmission/reception */
+ ier = readl(sport->membase + LINIER);
+ ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE);
+ writel(ier, sport->membase + LINIER);
+
+ cr = readl(sport->membase + UARTCR);
+ cr &= ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN);
+ writel(cr, sport->membase + UARTCR);
+
+ /* Enter initialization mode by setting INIT bit */
+
+ /* set the Linflex in master mode and activate by-pass filter */
+ cr1 = LINFLEXD_LINCR1_BF | LINFLEXD_LINCR1_MME
+ | LINFLEXD_LINCR1_INIT;
+ writel(cr1, sport->membase + LINCR1);
+
+ /* wait for init mode entry */
+ while ((readl(sport->membase + LINSR)
+ & LINFLEXD_LINSR_LINS_MASK)
+ != LINFLEXD_LINSR_LINS_INITMODE)
+ ;
+
+ /*
+ * UART = 0x1; - Linflex working in UART mode
+ * TXEN = 0x1; - Enable transmission of data now
+ * RXEn = 0x1; - Receiver enabled
+ * WL0 = 0x1; - 8 bit data
+ * PCE = 0x0; - No parity
+ */
+
+ /* set UART bit to allow writing other bits */
+ writel(LINFLEXD_UARTCR_UART, sport->membase + UARTCR);
+
+ cr = (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN |
+ LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART);
+
+ writel(cr, sport->membase + UARTCR);
+
+ cr1 &= ~(LINFLEXD_LINCR1_INIT);
+
+ writel(cr1, sport->membase + LINCR1);
+
+ ier = readl(sport->membase + LINIER);
+ ier |= LINFLEXD_LINIER_DRIE;
+ ier |= LINFLEXD_LINIER_DTIE;
+
+ writel(ier, sport->membase + LINIER);
+}
+
+static int linflex_startup(struct uart_port *port)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ linflex_setup_watermark(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ ret = devm_request_irq(port->dev, port->irq, linflex_int, 0,
+ DRIVER_NAME, port);
+
+ return ret;
+}
+
+static void linflex_shutdown(struct uart_port *port)
+{
+ unsigned long ier;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* disable interrupts */
+ ier = readl(port->membase + LINIER);
+ ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE);
+ writel(ier, port->membase + LINIER);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ devm_free_irq(port->dev, port->irq, port);
+}
+
+static void
+linflex_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned long flags;
+ unsigned long cr, old_cr, cr1;
+ unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+ cr = readl(port->membase + UARTCR);
+ old_cr = cr;
+
+ /* Enter initialization mode by setting INIT bit */
+ cr1 = readl(port->membase + LINCR1);
+ cr1 |= LINFLEXD_LINCR1_INIT;
+ writel(cr1, port->membase + LINCR1);
+
+ /* wait for init mode entry */
+ while ((readl(port->membase + LINSR)
+ & LINFLEXD_LINSR_LINS_MASK)
+ != LINFLEXD_LINSR_LINS_INITMODE)
+ ;
+
+ /*
+ * only support CS8 and CS7, and for CS7 must enable PE.
+ * supported mode:
+ * - (7,e/o,1)
+ * - (8,n,1)
+ * - (8,e/o,1)
+ */
+ /* enter the UART into configuration mode */
+
+ while ((termios->c_cflag & CSIZE) != CS8 &&
+ (termios->c_cflag & CSIZE) != CS7) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= old_csize;
+ old_csize = CS8;
+ }
+
+ if ((termios->c_cflag & CSIZE) == CS7) {
+ /* Word length: WL1WL0:00 */
+ cr = old_cr & ~LINFLEXD_UARTCR_WL1 & ~LINFLEXD_UARTCR_WL0;
+ }
+
+ if ((termios->c_cflag & CSIZE) == CS8) {
+ /* Word length: WL1WL0:01 */
+ cr = (old_cr | LINFLEXD_UARTCR_WL0) & ~LINFLEXD_UARTCR_WL1;
+ }
+
+ if (termios->c_cflag & CMSPAR) {
+ if ((termios->c_cflag & CSIZE) != CS8) {
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= CS8;
+ }
+ /* has a space/sticky bit */
+ cr |= LINFLEXD_UARTCR_WL0;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ termios->c_cflag &= ~CSTOPB;
+
+ /* parity must be enabled when CS7 to match 8-bits format */
+ if ((termios->c_cflag & CSIZE) == CS7)
+ termios->c_cflag |= PARENB;
+
+ if ((termios->c_cflag & PARENB)) {
+ cr |= LINFLEXD_UARTCR_PCE;
+ if (termios->c_cflag & PARODD)
+ cr = (cr | LINFLEXD_UARTCR_PC0) &
+ (~LINFLEXD_UARTCR_PC1);
+ else
+ cr = cr & (~LINFLEXD_UARTCR_PC1 &
+ ~LINFLEXD_UARTCR_PC0);
+ } else {
+ cr &= ~LINFLEXD_UARTCR_PCE;
+ }
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ port->read_status_mask = 0;
+
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= (LINFLEXD_UARTSR_FEF |
+ LINFLEXD_UARTSR_PE0 |
+ LINFLEXD_UARTSR_PE1 |
+ LINFLEXD_UARTSR_PE2 |
+ LINFLEXD_UARTSR_PE3);
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= LINFLEXD_UARTSR_FEF;
+
+ /* characters to ignore */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= LINFLEXD_UARTSR_PE;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= LINFLEXD_UARTSR_PE;
+ /*
+ * if we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= LINFLEXD_UARTSR_BOF;
+ }
+
+ writel(cr, port->membase + UARTCR);
+
+ cr1 &= ~(LINFLEXD_LINCR1_INIT);
+
+ writel(cr1, port->membase + LINCR1);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *linflex_type(struct uart_port *port)
+{
+ return "FSL_LINFLEX";
+}
+
+static void linflex_release_port(struct uart_port *port)
+{
+ /* nothing to do */
+}
+
+static int linflex_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+/* configure/auto-configure the port */
+static void linflex_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_LINFLEXUART;
+}
+
+static const struct uart_ops linflex_pops = {
+ .tx_empty = linflex_tx_empty,
+ .set_mctrl = linflex_set_mctrl,
+ .get_mctrl = linflex_get_mctrl,
+ .stop_tx = linflex_stop_tx,
+ .start_tx = linflex_start_tx,
+ .stop_rx = linflex_stop_rx,
+ .break_ctl = linflex_break_ctl,
+ .startup = linflex_startup,
+ .shutdown = linflex_shutdown,
+ .set_termios = linflex_set_termios,
+ .type = linflex_type,
+ .request_port = linflex_request_port,
+ .release_port = linflex_release_port,
+ .config_port = linflex_config_port,
+};
+
+static struct uart_port *linflex_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
+static void linflex_console_putchar(struct uart_port *port, int ch)
+{
+ unsigned long cr;
+
+ cr = readl(port->membase + UARTCR);
+
+ writeb(ch, port->membase + BDRL);
+
+ if (!(cr & LINFLEXD_UARTCR_TFBM))
+ while ((readl(port->membase + UARTSR) &
+ LINFLEXD_UARTSR_DTFTFF)
+ != LINFLEXD_UARTSR_DTFTFF)
+ ;
+ else
+ while (readl(port->membase + UARTSR) &
+ LINFLEXD_UARTSR_DTFTFF)
+ ;
+
+ if (!(cr & LINFLEXD_UARTCR_TFBM)) {
+ writel((readl(port->membase + UARTSR) |
+ LINFLEXD_UARTSR_DTFTFF),
+ port->membase + UARTSR);
+ }
+}
+
+static void linflex_earlycon_putchar(struct uart_port *port, int ch)
+{
+ unsigned long flags;
+ char *ret;
+
+ if (!linflex_earlycon_same_instance) {
+ linflex_console_putchar(port, ch);
+ return;
+ }
+
+ spin_lock_irqsave(&init_lock, flags);
+ if (!during_init)
+ goto outside_init;
+
+ if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT)
+ goto init_release;
+
+ if (!earlycon_buf.cap) {
+ earlycon_buf.content = kmalloc(EARLYCON_BUFFER_INITIAL_CAP,
+ GFP_ATOMIC);
+ earlycon_buf.cap = earlycon_buf.content ?
+ EARLYCON_BUFFER_INITIAL_CAP : 0;
+ } else if (earlycon_buf.len == earlycon_buf.cap) {
+ ret = krealloc(earlycon_buf.content, earlycon_buf.cap << 1,
+ GFP_ATOMIC);
+ if (ret) {
+ earlycon_buf.content = ret;
+ earlycon_buf.cap <<= 1;
+ }
+ }
+
+ if (earlycon_buf.len < earlycon_buf.cap)
+ earlycon_buf.content[earlycon_buf.len++] = ch;
+
+ goto init_release;
+
+outside_init:
+ linflex_console_putchar(port, ch);
+init_release:
+ spin_unlock_irqrestore(&init_lock, flags);
+}
+
+static void linflex_string_write(struct uart_port *sport, const char *s,
+ unsigned int count)
+{
+ unsigned long cr, ier = 0;
+
+ ier = readl(sport->membase + LINIER);
+ linflex_stop_tx(sport);
+
+ cr = readl(sport->membase + UARTCR);
+ cr |= (LINFLEXD_UARTCR_TXEN);
+ writel(cr, sport->membase + UARTCR);
+
+ uart_console_write(sport, s, count, linflex_console_putchar);
+
+ writel(ier, sport->membase + LINIER);
+}
+
+static void
+linflex_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_port *sport = linflex_ports[co->index];
+ unsigned long flags;
+ int locked = 1;
+
+ if (sport->sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock_irqsave(&sport->lock, flags);
+ else
+ spin_lock_irqsave(&sport->lock, flags);
+
+ linflex_string_write(sport, s, count);
+
+ if (locked)
+ spin_unlock_irqrestore(&sport->lock, flags);
+}
+
+/*
+ * if the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+linflex_console_get_options(struct uart_port *sport, int *parity, int *bits)
+{
+ unsigned long cr;
+
+ cr = readl(sport->membase + UARTCR);
+ cr &= LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN;
+
+ if (!cr)
+ return;
+
+ /* ok, the port was enabled */
+
+ *parity = 'n';
+ if (cr & LINFLEXD_UARTCR_PCE) {
+ if (cr & LINFLEXD_UARTCR_PC0)
+ *parity = 'o';
+ else
+ *parity = 'e';
+ }
+
+ if ((cr & LINFLEXD_UARTCR_WL0) && ((cr & LINFLEXD_UARTCR_WL1) == 0)) {
+ if (cr & LINFLEXD_UARTCR_PCE)
+ *bits = 9;
+ else
+ *bits = 8;
+ }
+}
+
+static int __init linflex_console_setup(struct console *co, char *options)
+{
+ struct uart_port *sport;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+ int i;
+ unsigned long flags;
+ /*
+ * check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index == -1 || co->index >= ARRAY_SIZE(linflex_ports))
+ co->index = 0;
+
+ sport = linflex_ports[co->index];
+ if (!sport)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ linflex_console_get_options(sport, &parity, &bits);
+
+ if (earlycon_port && sport->mapbase == earlycon_port->mapbase) {
+ linflex_earlycon_same_instance = true;
+
+ spin_lock_irqsave(&init_lock, flags);
+ during_init = true;
+ spin_unlock_irqrestore(&init_lock, flags);
+
+ /* Workaround for character loss or output of many invalid
+ * characters, when INIT mode is entered shortly after a
+ * character has just been printed.
+ */
+ udelay(PREINIT_DELAY);
+ }
+
+ linflex_setup_watermark(sport);
+
+ ret = uart_set_options(sport, co, baud, parity, bits, flow);
+
+ if (!linflex_earlycon_same_instance)
+ goto done;
+
+ spin_lock_irqsave(&init_lock, flags);
+
+ /* Emptying buffer */
+ if (earlycon_buf.len) {
+ for (i = 0; i < earlycon_buf.len; i++)
+ linflex_console_putchar(earlycon_port,
+ earlycon_buf.content[i]);
+
+ kfree(earlycon_buf.content);
+ earlycon_buf.len = 0;
+ }
+
+ during_init = false;
+ spin_unlock_irqrestore(&init_lock, flags);
+
+done:
+ return ret;
+}
+
+static struct uart_driver linflex_reg;
+static struct console linflex_console = {
+ .name = DEV_NAME,
+ .write = linflex_console_write,
+ .device = uart_console_device,
+ .setup = linflex_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &linflex_reg,
+};
+
+static void linflex_earlycon_write(struct console *con, const char *s,
+ unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+
+ uart_console_write(&dev->port, s, n, linflex_earlycon_putchar);
+}
+
+static int __init linflex_early_console_setup(struct earlycon_device *device,
+ const char *options)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = linflex_earlycon_write;
+ earlycon_port = &device->port;
+
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(linflex, "fsl,s32v234-linflexuart",
+ linflex_early_console_setup);
+
+#define LINFLEX_CONSOLE (&linflex_console)
+#else
+#define LINFLEX_CONSOLE NULL
+#endif
+
+static struct uart_driver linflex_reg = {
+ .owner = THIS_MODULE,
+ .driver_name = DRIVER_NAME,
+ .dev_name = DEV_NAME,
+ .nr = ARRAY_SIZE(linflex_ports),
+ .cons = LINFLEX_CONSOLE,
+};
+
+static int linflex_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct uart_port *sport;
+ struct resource *res;
+ int ret;
+
+ sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+ if (!sport)
+ return -ENOMEM;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ if (ret >= UART_NR) {
+ dev_err(&pdev->dev, "driver limited to %d serial ports\n",
+ UART_NR);
+ return -ENOMEM;
+ }
+
+ sport->line = ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ sport->mapbase = res->start;
+ sport->membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sport->membase))
+ return PTR_ERR(sport->membase);
+
+ sport->dev = &pdev->dev;
+ sport->type = PORT_LINFLEXUART;
+ sport->iotype = UPIO_MEM;
+ sport->irq = platform_get_irq(pdev, 0);
+ sport->ops = &linflex_pops;
+ sport->flags = UPF_BOOT_AUTOCONF;
+
+ linflex_ports[sport->line] = sport;
+
+ platform_set_drvdata(pdev, sport);
+
+ ret = uart_add_one_port(&linflex_reg, sport);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int linflex_remove(struct platform_device *pdev)
+{
+ struct uart_port *sport = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&linflex_reg, sport);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int linflex_suspend(struct device *dev)
+{
+ struct uart_port *sport = dev_get_drvdata(dev);
+
+ uart_suspend_port(&linflex_reg, sport);
+
+ return 0;
+}
+
+static int linflex_resume(struct device *dev)
+{
+ struct uart_port *sport = dev_get_drvdata(dev);
+
+ uart_resume_port(&linflex_reg, sport);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume);
+
+static struct platform_driver linflex_driver = {
+ .probe = linflex_probe,
+ .remove = linflex_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = linflex_dt_ids,
+ .pm = &linflex_pm_ops,
+ },
+};
+
+static int __init linflex_serial_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&linflex_reg);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&linflex_driver);
+ if (ret)
+ uart_unregister_driver(&linflex_reg);
+
+ return ret;
+}
+
+static void __exit linflex_serial_exit(void)
+{
+ platform_driver_unregister(&linflex_driver);
+ uart_unregister_driver(&linflex_reg);
+}
+
+module_init(linflex_serial_init);
+module_exit(linflex_serial_exit);
+
+MODULE_DESCRIPTION("Freescale linflex serial port driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 3f8d127..537896c 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -214,6 +214,7 @@
#define UARTFIFO_TXSIZE_OFF 4
#define UARTFIFO_RXFE 0x00000008
#define UARTFIFO_RXSIZE_OFF 0
+#define UARTFIFO_DEPTH(x) (0x1 << ((x) ? ((x) + 1) : 0))
#define UARTWATER_COUNT_MASK 0xff
#define UARTWATER_TXCNT_OFF 8
@@ -232,9 +233,20 @@
/* IMX lpuart has four extra unused regs located at the beginning */
#define IMX_REG_OFF 0x10
+static DEFINE_IDA(fsl_lpuart_ida);
+
+enum lpuart_type {
+ VF610_LPUART,
+ LS1021A_LPUART,
+ IMX7ULP_LPUART,
+ IMX8QXP_LPUART,
+};
+
struct lpuart_port {
struct uart_port port;
- struct clk *clk;
+ enum lpuart_type devtype;
+ struct clk *ipg_clk;
+ struct clk *baud_clk;
unsigned int txfifo_size;
unsigned int rxfifo_size;
@@ -259,19 +271,29 @@
};
struct lpuart_soc_data {
- char iotype;
- u8 reg_off;
+ enum lpuart_type devtype;
+ char iotype;
+ u8 reg_off;
};
static const struct lpuart_soc_data vf_data = {
+ .devtype = VF610_LPUART,
.iotype = UPIO_MEM,
};
static const struct lpuart_soc_data ls_data = {
+ .devtype = LS1021A_LPUART,
.iotype = UPIO_MEM32BE,
};
-static struct lpuart_soc_data imx_data = {
+static struct lpuart_soc_data imx7ulp_data = {
+ .devtype = IMX7ULP_LPUART,
+ .iotype = UPIO_MEM32,
+ .reg_off = IMX_REG_OFF,
+};
+
+static struct lpuart_soc_data imx8qxp_data = {
+ .devtype = IMX8QXP_LPUART,
.iotype = UPIO_MEM32,
.reg_off = IMX_REG_OFF,
};
@@ -279,7 +301,8 @@
static const struct of_device_id lpuart_dt_ids[] = {
{ .compatible = "fsl,vf610-lpuart", .data = &vf_data, },
{ .compatible = "fsl,ls1021a-lpuart", .data = &ls_data, },
- { .compatible = "fsl,imx7ulp-lpuart", .data = &imx_data, },
+ { .compatible = "fsl,imx7ulp-lpuart", .data = &imx7ulp_data, },
+ { .compatible = "fsl,imx8qxp-lpuart", .data = &imx8qxp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
@@ -287,6 +310,11 @@
/* Forward declare this for the dma callbacks*/
static void lpuart_dma_tx_complete(void *arg);
+static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport)
+{
+ return sport->devtype == IMX8QXP_LPUART;
+}
+
static inline u32 lpuart32_read(struct uart_port *port, u32 off)
{
switch (port->iotype) {
@@ -312,6 +340,39 @@
}
}
+static int __lpuart_enable_clks(struct lpuart_port *sport, bool is_en)
+{
+ int ret = 0;
+
+ if (is_en) {
+ ret = clk_prepare_enable(sport->ipg_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(sport->baud_clk);
+ if (ret) {
+ clk_disable_unprepare(sport->ipg_clk);
+ return ret;
+ }
+ } else {
+ clk_disable_unprepare(sport->baud_clk);
+ clk_disable_unprepare(sport->ipg_clk);
+ }
+
+ return 0;
+}
+
+static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport)
+{
+ if (is_imx8qxp_lpuart(sport))
+ return clk_get_rate(sport->baud_clk);
+
+ return clk_get_rate(sport->ipg_clk);
+}
+
+#define lpuart_enable_clks(x) __lpuart_enable_clks(x, true)
+#define lpuart_disable_clks(x) __lpuart_enable_clks(x, false)
+
static void lpuart_stop_tx(struct uart_port *port)
{
unsigned char temp;
@@ -391,6 +452,11 @@
dma_async_issue_pending(sport->dma_tx_chan);
}
+static bool lpuart_stopped_or_empty(struct uart_port *port)
+{
+ return uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port);
+}
+
static void lpuart_dma_tx_complete(void *arg)
{
struct lpuart_port *sport = arg;
@@ -418,12 +484,23 @@
spin_lock_irqsave(&sport->port.lock, flags);
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+ if (!lpuart_stopped_or_empty(&sport->port))
lpuart_dma_tx(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
+static dma_addr_t lpuart_dma_datareg_addr(struct lpuart_port *sport)
+{
+ switch (sport->port.iotype) {
+ case UPIO_MEM32:
+ return sport->port.mapbase + UARTDATA;
+ case UPIO_MEM32BE:
+ return sport->port.mapbase + UARTDATA + sizeof(u32) - 1;
+ }
+ return sport->port.mapbase + UARTDR;
+}
+
static int lpuart_dma_tx_request(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port,
@@ -431,7 +508,7 @@
struct dma_slave_config dma_tx_sconfig = {};
int ret;
- dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+ dma_tx_sconfig.dst_addr = lpuart_dma_datareg_addr(sport);
dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_tx_sconfig.dst_maxburst = 1;
dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
@@ -446,9 +523,16 @@
return 0;
}
+static bool lpuart_is_32(struct lpuart_port *sport)
+{
+ return sport->port.iotype == UPIO_MEM32 ||
+ sport->port.iotype == UPIO_MEM32BE;
+}
+
static void lpuart_flush_buffer(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ u32 val;
if (sport->lpuart_dma_tx_use) {
if (sport->dma_tx_in_progress) {
@@ -458,6 +542,30 @@
}
dmaengine_terminate_all(sport->dma_tx_chan);
}
+
+ if (lpuart_is_32(sport)) {
+ val = lpuart32_read(&sport->port, UARTFIFO);
+ val |= UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH;
+ lpuart32_write(&sport->port, val, UARTFIFO);
+ } else {
+ val = readb(sport->port.membase + UARTCFIFO);
+ val |= UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH;
+ writeb(val, sport->port.membase + UARTCFIFO);
+ }
+}
+
+static void lpuart_wait_bit_set(struct uart_port *port, unsigned int offset,
+ u8 bit)
+{
+ while (!(readb(port->membase + offset) & bit))
+ cpu_relax();
+}
+
+static void lpuart32_wait_bit_set(struct uart_port *port, unsigned int offset,
+ u32 bit)
+{
+ while (!(lpuart32_read(port, offset) & bit))
+ cpu_relax();
}
#if defined(CONFIG_CONSOLE_POLL)
@@ -503,9 +611,7 @@
static void lpuart_poll_put_char(struct uart_port *port, unsigned char c)
{
/* drain */
- while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
- barrier();
-
+ lpuart_wait_bit_set(port, UARTSR1, UARTSR1_TDRE);
writeb(c, port->membase + UARTDR);
}
@@ -528,26 +634,26 @@
spin_lock_irqsave(&sport->port.lock, flags);
/* Disable Rx & Tx */
- writel(0, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, UARTCTRL, 0);
- temp = readl(sport->port.membase + UARTFIFO);
+ temp = lpuart32_read(&sport->port, UARTFIFO);
/* Enable Rx and Tx FIFO */
- writel(temp | UARTFIFO_RXFE | UARTFIFO_TXFE,
- sport->port.membase + UARTFIFO);
+ lpuart32_write(&sport->port, UARTFIFO,
+ temp | UARTFIFO_RXFE | UARTFIFO_TXFE);
/* flush Tx and Rx FIFO */
- writel(UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH,
- sport->port.membase + UARTFIFO);
+ lpuart32_write(&sport->port, UARTFIFO,
+ UARTFIFO_TXFLUSH | UARTFIFO_RXFLUSH);
/* explicitly clear RDRF */
- if (readl(sport->port.membase + UARTSTAT) & UARTSTAT_RDRF) {
- readl(sport->port.membase + UARTDATA);
- writel(UARTFIFO_RXUF, sport->port.membase + UARTFIFO);
+ if (lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_RDRF) {
+ lpuart32_read(&sport->port, UARTDATA);
+ lpuart32_write(&sport->port, UARTFIFO, UARTFIFO_RXUF);
}
/* Enable Rx and Tx */
- writel(UARTCTRL_RE | UARTCTRL_TE, sport->port.membase + UARTCTRL);
+ lpuart32_write(&sport->port, UARTCTRL, UARTCTRL_RE | UARTCTRL_TE);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
@@ -555,18 +661,16 @@
static void lpuart32_poll_put_char(struct uart_port *port, unsigned char c)
{
- while (!(readl(port->membase + UARTSTAT) & UARTSTAT_TDRE))
- barrier();
-
- writel(c, port->membase + UARTDATA);
+ lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TDRE);
+ lpuart32_write(port, UARTDATA, c);
}
static int lpuart32_poll_get_char(struct uart_port *port)
{
- if (!(readl(port->membase + UARTSTAT) & UARTSTAT_RDRF))
+ if (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_RDRF))
return NO_POLL_CHAR;
- return readl(port->membase + UARTDATA);
+ return lpuart32_read(port, UARTDATA);
}
#endif
@@ -574,6 +678,18 @@
{
struct circ_buf *xmit = &sport->port.state->xmit;
+ if (sport->port.x_char) {
+ writeb(sport->port.x_char, sport->port.membase + UARTDR);
+ sport->port.icount.tx++;
+ sport->port.x_char = 0;
+ return;
+ }
+
+ if (lpuart_stopped_or_empty(&sport->port)) {
+ lpuart_stop_tx(&sport->port);
+ return;
+ }
+
while (!uart_circ_empty(xmit) &&
(readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size)) {
writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
@@ -593,6 +709,18 @@
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long txcnt;
+ if (sport->port.x_char) {
+ lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
+ sport->port.icount.tx++;
+ sport->port.x_char = 0;
+ return;
+ }
+
+ if (lpuart_stopped_or_empty(&sport->port)) {
+ lpuart32_stop_tx(&sport->port);
+ return;
+ }
+
txcnt = lpuart32_read(&sport->port, UARTWATER);
txcnt = txcnt >> UARTWATER_TXCNT_OFF;
txcnt &= UARTWATER_COUNT_MASK;
@@ -616,14 +744,13 @@
{
struct lpuart_port *sport = container_of(port,
struct lpuart_port, port);
- struct circ_buf *xmit = &sport->port.state->xmit;
unsigned char temp;
temp = readb(port->membase + UARTCR2);
writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
if (sport->lpuart_dma_tx_use) {
- if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
+ if (!lpuart_stopped_or_empty(port))
lpuart_dma_tx(sport);
} else {
if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
@@ -636,11 +763,16 @@
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
unsigned long temp;
- temp = lpuart32_read(port, UARTCTRL);
- lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL);
+ if (sport->lpuart_dma_tx_use) {
+ if (!lpuart_stopped_or_empty(port))
+ lpuart_dma_tx(sport);
+ } else {
+ temp = lpuart32_read(port, UARTCTRL);
+ lpuart32_write(port, temp | UARTCTRL_TIE, UARTCTRL);
- if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)
- lpuart32_transmit_buffer(sport);
+ if (lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE)
+ lpuart32_transmit_buffer(sport);
+ }
}
/* return TIOCSER_TEMT when transmitter is not busy */
@@ -662,56 +794,32 @@
static unsigned int lpuart32_tx_empty(struct uart_port *port)
{
- return (lpuart32_read(port, UARTSTAT) & UARTSTAT_TC) ?
- TIOCSER_TEMT : 0;
+ struct lpuart_port *sport = container_of(port,
+ struct lpuart_port, port);
+ unsigned long stat = lpuart32_read(port, UARTSTAT);
+ unsigned long sfifo = lpuart32_read(port, UARTFIFO);
+
+ if (sport->dma_tx_in_progress)
+ return 0;
+
+ if (stat & UARTSTAT_TC && sfifo & UARTFIFO_TXEMPT)
+ return TIOCSER_TEMT;
+
+ return 0;
}
-static bool lpuart_is_32(struct lpuart_port *sport)
+static void lpuart_txint(struct lpuart_port *sport)
{
- return sport->port.iotype == UPIO_MEM32 ||
- sport->port.iotype == UPIO_MEM32BE;
-}
-
-static irqreturn_t lpuart_txint(int irq, void *dev_id)
-{
- struct lpuart_port *sport = dev_id;
- struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
- if (sport->port.x_char) {
- if (lpuart_is_32(sport))
- lpuart32_write(&sport->port, sport->port.x_char, UARTDATA);
- else
- writeb(sport->port.x_char, sport->port.membase + UARTDR);
- goto out;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
- if (lpuart_is_32(sport))
- lpuart32_stop_tx(&sport->port);
- else
- lpuart_stop_tx(&sport->port);
- goto out;
- }
-
- if (lpuart_is_32(sport))
- lpuart32_transmit_buffer(sport);
- else
- lpuart_transmit_buffer(sport);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&sport->port);
-
-out:
+ lpuart_transmit_buffer(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
- return IRQ_HANDLED;
}
-static irqreturn_t lpuart_rxint(int irq, void *dev_id)
+static void lpuart_rxint(struct lpuart_port *sport)
{
- struct lpuart_port *sport = dev_id;
- unsigned int flg, ignored = 0;
+ unsigned int flg, ignored = 0, overrun = 0;
struct tty_port *port = &sport->port.state->port;
unsigned long flags;
unsigned char rx, sr;
@@ -738,7 +846,7 @@
sport->port.icount.frame++;
if (sr & UARTSR1_OR)
- sport->port.icount.overrun++;
+ overrun++;
if (sr & sport->port.ignore_status_mask) {
if (++ignored > 100)
@@ -765,15 +873,33 @@
}
out:
+ if (overrun) {
+ sport->port.icount.overrun += overrun;
+
+ /*
+ * Overruns cause FIFO pointers to become missaligned.
+ * Flushing the receive FIFO reinitializes the pointers.
+ */
+ writeb(UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO);
+ writeb(UARTSFIFO_RXOF, sport->port.membase + UARTSFIFO);
+ }
+
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port);
- return IRQ_HANDLED;
}
-static irqreturn_t lpuart32_rxint(int irq, void *dev_id)
+static void lpuart32_txint(struct lpuart_port *sport)
{
- struct lpuart_port *sport = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ lpuart32_transmit_buffer(sport);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart32_rxint(struct lpuart_port *sport)
+{
unsigned int flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
unsigned long flags;
@@ -832,7 +958,6 @@
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port);
- return IRQ_HANDLED;
}
static irqreturn_t lpuart_int(int irq, void *dev_id)
@@ -842,11 +967,11 @@
sts = readb(sport->port.membase + UARTSR1);
- if (sts & UARTSR1_RDRF)
- lpuart_rxint(irq, dev_id);
+ if (sts & UARTSR1_RDRF && !sport->lpuart_dma_rx_use)
+ lpuart_rxint(sport);
- if (sts & UARTSR1_TDRE)
- lpuart_txint(irq, dev_id);
+ if (sts & UARTSR1_TDRE && !sport->lpuart_dma_tx_use)
+ lpuart_txint(sport);
return IRQ_HANDLED;
}
@@ -860,12 +985,11 @@
rxcount = lpuart32_read(&sport->port, UARTWATER);
rxcount = rxcount >> UARTWATER_RXCNT_OFF;
- if (sts & UARTSTAT_RDRF || rxcount > 0)
- lpuart32_rxint(irq, dev_id);
+ if ((sts & UARTSTAT_RDRF || rxcount > 0) && !sport->lpuart_dma_rx_use)
+ lpuart32_rxint(sport);
- if ((sts & UARTSTAT_TDRE) &&
- !(lpuart32_read(&sport->port, UARTBAUD) & UARTBAUD_TDMAE))
- lpuart_txint(irq, dev_id);
+ if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use)
+ lpuart32_txint(sport);
lpuart32_write(&sport->port, sts, UARTSTAT);
return IRQ_HANDLED;
@@ -879,18 +1003,57 @@
struct circ_buf *ring = &sport->rx_ring;
unsigned long flags;
int count = 0;
- unsigned char sr;
- sr = readb(sport->port.membase + UARTSR1);
+ if (lpuart_is_32(sport)) {
+ unsigned long sr = lpuart32_read(&sport->port, UARTSTAT);
- if (sr & (UARTSR1_PE | UARTSR1_FE)) {
- /* Read DR to clear the error flags */
- readb(sport->port.membase + UARTDR);
+ if (sr & (UARTSTAT_PE | UARTSTAT_FE)) {
+ /* Read DR to clear the error flags */
+ lpuart32_read(&sport->port, UARTDATA);
- if (sr & UARTSR1_PE)
- sport->port.icount.parity++;
- else if (sr & UARTSR1_FE)
- sport->port.icount.frame++;
+ if (sr & UARTSTAT_PE)
+ sport->port.icount.parity++;
+ else if (sr & UARTSTAT_FE)
+ sport->port.icount.frame++;
+ }
+ } else {
+ unsigned char sr = readb(sport->port.membase + UARTSR1);
+
+ if (sr & (UARTSR1_PE | UARTSR1_FE)) {
+ unsigned char cr2;
+
+ /* Disable receiver during this operation... */
+ cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 &= ~UARTCR2_RE;
+ writeb(cr2, sport->port.membase + UARTCR2);
+
+ /* Read DR to clear the error flags */
+ readb(sport->port.membase + UARTDR);
+
+ if (sr & UARTSR1_PE)
+ sport->port.icount.parity++;
+ else if (sr & UARTSR1_FE)
+ sport->port.icount.frame++;
+ /*
+ * At this point parity/framing error is
+ * cleared However, since the DMA already read
+ * the data register and we had to read it
+ * again after reading the status register to
+ * properly clear the flags, the FIFO actually
+ * underflowed... This requires a clearing of
+ * the FIFO...
+ */
+ if (readb(sport->port.membase + UARTSFIFO) &
+ UARTSFIFO_RXUF) {
+ writeb(UARTSFIFO_RXUF,
+ sport->port.membase + UARTSFIFO);
+ writeb(UARTCFIFO_RXFLUSH,
+ sport->port.membase + UARTCFIFO);
+ }
+
+ cr2 |= UARTCR2_RE;
+ writeb(cr2, sport->port.membase + UARTCR2);
+ }
}
async_tx_ack(sport->dma_rx_desc);
@@ -998,14 +1161,11 @@
if (sport->rx_dma_rng_buf_len < 16)
sport->rx_dma_rng_buf_len = 16;
- ring->buf = kmalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
- if (!ring->buf) {
- dev_err(sport->port.dev, "Ring buf alloc failed\n");
+ ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
+ if (!ring->buf)
return -ENOMEM;
- }
sg_init_one(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
- sg_set_buf(&sport->rx_sgl, ring->buf, sport->rx_dma_rng_buf_len);
nent = dma_map_sg(sport->port.dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
if (!nent) {
@@ -1013,7 +1173,7 @@
return -EINVAL;
}
- dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+ dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_rx_sconfig.src_maxburst = 1;
dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
@@ -1041,8 +1201,14 @@
sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
dma_async_issue_pending(sport->dma_rx_chan);
- writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
- sport->port.membase + UARTCR5);
+ if (lpuart_is_32(sport)) {
+ unsigned long temp = lpuart32_read(&sport->port, UARTBAUD);
+
+ lpuart32_write(&sport->port, temp | UARTBAUD_RDMAE, UARTBAUD);
+ } else {
+ writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS,
+ sport->port.membase + UARTCR5);
+ }
return 0;
}
@@ -1237,6 +1403,17 @@
writeb(cr2_saved, sport->port.membase + UARTCR2);
}
+static void lpuart_setup_watermark_enable(struct lpuart_port *sport)
+{
+ unsigned char cr2;
+
+ lpuart_setup_watermark(sport);
+
+ cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 |= UARTCR2_RIE | UARTCR2_RE | UARTCR2_TE;
+ writeb(cr2, sport->port.membase + UARTCR2);
+}
+
static void lpuart32_setup_watermark(struct lpuart_port *sport)
{
unsigned long val, ctrl;
@@ -1262,11 +1439,57 @@
lpuart32_write(&sport->port, ctrl_saved, UARTCTRL);
}
+static void lpuart32_setup_watermark_enable(struct lpuart_port *sport)
+{
+ u32 temp;
+
+ lpuart32_setup_watermark(sport);
+
+ temp = lpuart32_read(&sport->port, UARTCTRL);
+ temp |= UARTCTRL_RE | UARTCTRL_TE | UARTCTRL_ILIE;
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+}
+
static void rx_dma_timer_init(struct lpuart_port *sport)
{
- timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0);
- sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
- add_timer(&sport->lpuart_timer);
+ timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0);
+ sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+ add_timer(&sport->lpuart_timer);
+}
+
+static void lpuart_tx_dma_startup(struct lpuart_port *sport)
+{
+ u32 uartbaud;
+
+ if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
+ init_waitqueue_head(&sport->dma_wait);
+ sport->lpuart_dma_tx_use = true;
+ if (lpuart_is_32(sport)) {
+ uartbaud = lpuart32_read(&sport->port, UARTBAUD);
+ lpuart32_write(&sport->port,
+ uartbaud | UARTBAUD_TDMAE, UARTBAUD);
+ } else {
+ writeb(readb(sport->port.membase + UARTCR5) |
+ UARTCR5_TDMAS, sport->port.membase + UARTCR5);
+ }
+ } else {
+ sport->lpuart_dma_tx_use = false;
+ }
+}
+
+static void lpuart_rx_dma_startup(struct lpuart_port *sport)
+{
+ if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
+ /* set Rx DMA timeout */
+ sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+ if (!sport->dma_rx_timeout)
+ sport->dma_rx_timeout = 1;
+
+ sport->lpuart_dma_rx_use = true;
+ rx_dma_timer_init(sport);
+ } else {
+ sport->lpuart_dma_rx_use = false;
+ }
}
static int lpuart_startup(struct uart_port *port)
@@ -1278,48 +1501,43 @@
/* determine FIFO size and enable FIFO mode */
temp = readb(sport->port.membase + UARTPFIFO);
- sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) &
- UARTPFIFO_FIFOSIZE_MASK) + 1);
-
+ sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_TXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK);
sport->port.fifosize = sport->txfifo_size;
- sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
- UARTPFIFO_FIFOSIZE_MASK) + 1);
+ sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) &
+ UARTPFIFO_FIFOSIZE_MASK);
spin_lock_irqsave(&sport->port.lock, flags);
- lpuart_setup_watermark(sport);
+ lpuart_setup_watermark_enable(sport);
- temp = readb(sport->port.membase + UARTCR2);
- temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
- writeb(temp, sport->port.membase + UARTCR2);
-
- if (sport->dma_rx_chan && !lpuart_start_rx_dma(sport)) {
- /* set Rx DMA timeout */
- sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
- if (!sport->dma_rx_timeout)
- sport->dma_rx_timeout = 1;
-
- sport->lpuart_dma_rx_use = true;
- rx_dma_timer_init(sport);
- } else {
- sport->lpuart_dma_rx_use = false;
- }
-
- if (sport->dma_tx_chan && !lpuart_dma_tx_request(port)) {
- init_waitqueue_head(&sport->dma_wait);
- sport->lpuart_dma_tx_use = true;
- temp = readb(port->membase + UARTCR5);
- writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
- } else {
- sport->lpuart_dma_tx_use = false;
- }
+ lpuart_rx_dma_startup(sport);
+ lpuart_tx_dma_startup(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
+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;
+ if (!sport->lpuart_dma_tx_use)
+ temp |= UARTCTRL_TIE;
+ lpuart32_write(&sport->port, temp, UARTCTRL);
+}
+
static int lpuart32_startup(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
@@ -1329,25 +1547,43 @@
/* determine FIFO size */
temp = lpuart32_read(&sport->port, UARTFIFO);
- sport->txfifo_size = 0x1 << (((temp >> UARTFIFO_TXSIZE_OFF) &
- UARTFIFO_FIFOSIZE_MASK) - 1);
+ sport->txfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_TXSIZE_OFF) &
+ UARTFIFO_FIFOSIZE_MASK);
+ sport->port.fifosize = sport->txfifo_size;
- sport->rxfifo_size = 0x1 << (((temp >> UARTFIFO_RXSIZE_OFF) &
- UARTFIFO_FIFOSIZE_MASK) - 1);
+ sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTFIFO_RXSIZE_OFF) &
+ UARTFIFO_FIFOSIZE_MASK);
spin_lock_irqsave(&sport->port.lock, flags);
- lpuart32_setup_watermark(sport);
+ lpuart32_setup_watermark_enable(sport);
- temp = lpuart32_read(&sport->port, UARTCTRL);
- temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | UARTCTRL_TE);
- temp |= UARTCTRL_ILIE;
- lpuart32_write(&sport->port, temp, UARTCTRL);
+
+ lpuart_rx_dma_startup(sport);
+ lpuart_tx_dma_startup(sport);
+
+ lpuart32_configure(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
+static void lpuart_dma_shutdown(struct lpuart_port *sport)
+{
+ if (sport->lpuart_dma_rx_use) {
+ del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
+ }
+
+ if (sport->lpuart_dma_tx_use) {
+ if (wait_event_interruptible(sport->dma_wait,
+ !sport->dma_tx_in_progress) != false) {
+ sport->dma_tx_in_progress = false;
+ dmaengine_terminate_all(sport->dma_tx_chan);
+ }
+ }
+}
+
static void lpuart_shutdown(struct uart_port *port)
{
struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
@@ -1364,24 +1600,13 @@
spin_unlock_irqrestore(&port->lock, flags);
- if (sport->lpuart_dma_rx_use) {
- del_timer_sync(&sport->lpuart_timer);
- lpuart_dma_rx_free(&sport->port);
- }
-
- if (sport->lpuart_dma_tx_use) {
- if (wait_event_interruptible(sport->dma_wait,
- !sport->dma_tx_in_progress) != false) {
- sport->dma_tx_in_progress = false;
- dmaengine_terminate_all(sport->dma_tx_chan);
- }
-
- lpuart_stop_tx(port);
- }
+ lpuart_dma_shutdown(sport);
}
static void lpuart32_shutdown(struct uart_port *port)
{
+ struct lpuart_port *sport =
+ container_of(port, struct lpuart_port, port);
unsigned long temp;
unsigned long flags;
@@ -1394,6 +1619,8 @@
lpuart32_write(port, temp, UARTCTRL);
spin_unlock_irqrestore(&port->lock, flags);
+
+ lpuart_dma_shutdown(sport);
}
static void
@@ -1447,21 +1674,18 @@
if (sport->port.rs485.flags & SER_RS485_ENABLED)
termios->c_cflag &= ~CRTSCTS;
- if (termios->c_cflag & CRTSCTS) {
- modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
- } else {
- termios->c_cflag &= ~CRTSCTS;
+ if (termios->c_cflag & CRTSCTS)
+ modem |= UARTMODEM_RXRTSE | UARTMODEM_TXCTSE;
+ else
modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
- }
- if (termios->c_cflag & CSTOPB)
- termios->c_cflag &= ~CSTOPB;
+ termios->c_cflag &= ~CSTOPB;
/* parity must be enabled when CS7 to match 8-bits format */
if ((termios->c_cflag & CSIZE) == CS7)
termios->c_cflag |= PARENB;
- if ((termios->c_cflag & PARENB)) {
+ if (termios->c_cflag & PARENB) {
if (termios->c_cflag & CMSPAR) {
cr1 &= ~UARTCR1_PE;
if (termios->c_cflag & PARODD)
@@ -1477,6 +1701,8 @@
else
cr1 &= ~UARTCR1_PT;
}
+ } else {
+ cr1 &= ~UARTCR1_PE;
}
/* ask the core to calculate the divisor */
@@ -1498,7 +1724,7 @@
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
- sport->port.read_status_mask |= (UARTSR1_FE | UARTSR1_PE);
+ sport->port.read_status_mask |= UARTSR1_FE | UARTSR1_PE;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
sport->port.read_status_mask |= UARTSR1_FE;
@@ -1520,8 +1746,7 @@
uart_update_timeout(port, termios->c_cflag, baud);
/* wait transmit engin complete */
- while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
- barrier();
+ lpuart_wait_bit_set(&sport->port, UARTSR1, UARTSR1_TC);
/* disable transmit and receive */
writeb(old_cr2 & ~(UARTCR2_TE | UARTCR2_RE),
@@ -1612,12 +1837,15 @@
tmp |= UARTBAUD_BOTHEDGE;
tmp &= ~(UARTBAUD_OSR_MASK << UARTBAUD_OSR_SHIFT);
- tmp |= (((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT);
+ tmp |= ((osr-1) & UARTBAUD_OSR_MASK) << UARTBAUD_OSR_SHIFT;
tmp &= ~UARTBAUD_SBR_MASK;
tmp |= sbr & UARTBAUD_SBR_MASK;
- tmp &= ~(UARTBAUD_TDMAE | UARTBAUD_RDMAE);
+ if (!sport->lpuart_dma_rx_use)
+ tmp &= ~UARTBAUD_RDMAE;
+ if (!sport->lpuart_dma_tx_use)
+ tmp &= ~UARTBAUD_TDMAE;
lpuart32_write(&sport->port, tmp, UARTBAUD);
}
@@ -1662,7 +1890,7 @@
}
if (termios->c_cflag & CRTSCTS) {
- modem |= (UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
+ modem |= UARTMODEM_RXRTSE | UARTMODEM_TXCTSE;
} else {
termios->c_cflag &= ~CRTSCTS;
modem &= ~(UARTMODEM_RXRTSE | UARTMODEM_TXCTSE);
@@ -1680,7 +1908,7 @@
ctrl &= ~UARTCTRL_PE;
ctrl |= UARTCTRL_M;
} else {
- ctrl |= UARTCR1_PE;
+ ctrl |= UARTCTRL_PE;
if ((termios->c_cflag & CSIZE) == CS8)
ctrl |= UARTCTRL_M;
if (termios->c_cflag & PARODD)
@@ -1688,16 +1916,30 @@
else
ctrl &= ~UARTCTRL_PT;
}
+ } else {
+ ctrl &= ~UARTCTRL_PE;
}
/* ask the core to calculate the divisor */
- baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+ baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 4);
+
+ /*
+ * Need to update the Ring buffer length according to the selected
+ * baud rate and restart Rx DMA path.
+ *
+ * Since timer function acqures sport->port.lock, need to stop before
+ * acquring same lock because otherwise del_timer_sync() can deadlock.
+ */
+ if (old && sport->lpuart_dma_rx_use) {
+ del_timer_sync(&sport->lpuart_timer);
+ lpuart_dma_rx_free(&sport->port);
+ }
spin_lock_irqsave(&sport->port.lock, flags);
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
- sport->port.read_status_mask |= (UARTSTAT_FE | UARTSTAT_PE);
+ sport->port.read_status_mask |= UARTSTAT_FE | UARTSTAT_PE;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
sport->port.read_status_mask |= UARTSTAT_FE;
@@ -1719,8 +1961,7 @@
uart_update_timeout(port, termios->c_cflag, baud);
/* wait transmit engin complete */
- while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
- barrier();
+ lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC);
/* disable transmit and receive */
lpuart32_write(&sport->port, old_ctrl & ~(UARTCTRL_TE | UARTCTRL_RE),
@@ -1731,6 +1972,13 @@
lpuart32_write(&sport->port, ctrl, UARTCTRL);
/* restore control register */
+ if (old && sport->lpuart_dma_rx_use) {
+ if (!lpuart_start_rx_dma(sport))
+ rx_dma_timer_init(sport);
+ else
+ sport->lpuart_dma_rx_use = false;
+ }
+
spin_unlock_irqrestore(&sport->port.lock, flags);
}
@@ -1828,17 +2076,13 @@
#ifdef CONFIG_SERIAL_FSL_LPUART_CONSOLE
static void lpuart_console_putchar(struct uart_port *port, int ch)
{
- while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE))
- barrier();
-
+ lpuart_wait_bit_set(port, UARTSR1, UARTSR1_TDRE);
writeb(ch, port->membase + UARTDR);
}
static void lpuart32_console_putchar(struct uart_port *port, int ch)
{
- while (!(lpuart32_read(port, UARTSTAT) & UARTSTAT_TDRE))
- barrier();
-
+ lpuart32_wait_bit_set(port, UARTSTAT, UARTSTAT_TDRE);
lpuart32_write(port, ch, UARTDATA);
}
@@ -1857,15 +2101,14 @@
/* first save CR2 and then disable interrupts */
cr2 = old_cr2 = readb(sport->port.membase + UARTCR2);
- cr2 |= (UARTCR2_TE | UARTCR2_RE);
+ cr2 |= UARTCR2_TE | UARTCR2_RE;
cr2 &= ~(UARTCR2_TIE | UARTCR2_TCIE | UARTCR2_RIE);
writeb(cr2, sport->port.membase + UARTCR2);
uart_console_write(&sport->port, s, count, lpuart_console_putchar);
/* wait for transmitter finish complete and restore CR2 */
- while (!(readb(sport->port.membase + UARTSR1) & UARTSR1_TC))
- barrier();
+ lpuart_wait_bit_set(&sport->port, UARTSR1, UARTSR1_TC);
writeb(old_cr2, sport->port.membase + UARTCR2);
@@ -1888,15 +2131,14 @@
/* first save CR2 and then disable interrupts */
cr = old_cr = lpuart32_read(&sport->port, UARTCTRL);
- cr |= (UARTCTRL_TE | UARTCTRL_RE);
+ cr |= UARTCTRL_TE | UARTCTRL_RE;
cr &= ~(UARTCTRL_TIE | UARTCTRL_TCIE | UARTCTRL_RIE);
lpuart32_write(&sport->port, cr, UARTCTRL);
uart_console_write(&sport->port, s, count, lpuart32_console_putchar);
/* wait for transmitter finish complete and restore CR2 */
- while (!(lpuart32_read(&sport->port, UARTSTAT) & UARTSTAT_TC))
- barrier();
+ lpuart32_wait_bit_set(&sport->port, UARTSTAT, UARTSTAT_TC);
lpuart32_write(&sport->port, old_cr, UARTCTRL);
@@ -1946,14 +2188,14 @@
brfa = readb(sport->port.membase + UARTCR4);
brfa &= UARTCR4_BRFA_MASK;
- uartclk = clk_get_rate(sport->clk);
+ uartclk = lpuart_get_baud_clk_rate(sport);
/*
* baud = mod_clk/(16*(sbr[13]+(brfa)/32)
*/
baud_raw = uartclk / (16 * (sbr + brfa / 32));
if (*baud != baud_raw)
- printk(KERN_INFO "Serial: Console lpuart rounded baud rate"
+ dev_info(sport->port.dev, "Serial: Console lpuart rounded baud rate"
"from %d to %d\n", baud_raw, *baud);
}
@@ -1989,14 +2231,14 @@
bd = lpuart32_read(&sport->port, UARTBAUD);
bd &= UARTBAUD_SBR_MASK;
sbr = bd;
- uartclk = clk_get_rate(sport->clk);
+ uartclk = lpuart_get_baud_clk_rate(sport);
/*
* baud = mod_clk/(16*(sbr[13]+(brfa)/32)
*/
baud_raw = uartclk / (16 * sbr);
if (*baud != baud_raw)
- printk(KERN_INFO "Serial: Console lpuart rounded baud rate"
+ dev_info(sport->port.dev, "Serial: Console lpuart rounded baud rate"
"from %d to %d\n", baud_raw, *baud);
}
@@ -2107,6 +2349,7 @@
OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
+OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);
EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup);
@@ -2139,12 +2382,13 @@
if (!sport)
return -ENOMEM;
- pdev->dev.coherent_dma_mask = 0;
-
ret = of_alias_get_id(np, "serial");
if (ret < 0) {
- dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
- return ret;
+ ret = ida_simple_get(&fsl_lpuart_ida, 0, UART_NR, GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "port line is full, add device failed\n");
+ return ret;
+ }
}
if (ret >= ARRAY_SIZE(lpuart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", ret);
@@ -2160,11 +2404,10 @@
sport->port.mapbase = res->start;
sport->port.dev = &pdev->dev;
sport->port.type = PORT_LPUART;
+ sport->devtype = sdata->devtype;
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot obtain irq\n");
+ if (ret < 0)
return ret;
- }
sport->port.irq = ret;
sport->port.iotype = sdata->iotype;
if (lpuart_is_32(sport))
@@ -2175,20 +2418,27 @@
sport->port.rs485_config = lpuart_config_rs485;
- sport->clk = devm_clk_get(&pdev->dev, "ipg");
- if (IS_ERR(sport->clk)) {
- ret = PTR_ERR(sport->clk);
- dev_err(&pdev->dev, "failed to get uart clk: %d\n", ret);
+ sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(sport->ipg_clk)) {
+ ret = PTR_ERR(sport->ipg_clk);
+ dev_err(&pdev->dev, "failed to get uart ipg clk: %d\n", ret);
return ret;
}
- ret = clk_prepare_enable(sport->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable uart clk: %d\n", ret);
- return ret;
+ sport->baud_clk = NULL;
+ if (is_imx8qxp_lpuart(sport)) {
+ sport->baud_clk = devm_clk_get(&pdev->dev, "baud");
+ if (IS_ERR(sport->baud_clk)) {
+ ret = PTR_ERR(sport->baud_clk);
+ dev_err(&pdev->dev, "failed to get uart baud clk: %d\n", ret);
+ return ret;
+ }
}
- sport->port.uartclk = clk_get_rate(sport->clk);
+ ret = lpuart_enable_clks(sport);
+ if (ret)
+ return ret;
+ sport->port.uartclk = lpuart_get_baud_clk_rate(sport);
lpuart_ports[sport->port.line] = sport;
@@ -2236,7 +2486,7 @@
failed_attach_port:
failed_irq_request:
- clk_disable_unprepare(sport->clk);
+ lpuart_disable_clks(sport);
return ret;
}
@@ -2246,7 +2496,9 @@
uart_remove_one_port(&lpuart_reg, &sport->port);
- clk_disable_unprepare(sport->clk);
+ ida_simple_remove(&fsl_lpuart_ida, sport->port.line);
+
+ lpuart_disable_clks(sport);
if (sport->dma_tx_chan)
dma_release_channel(sport->dma_tx_chan);
@@ -2295,8 +2547,14 @@
}
/* Disable Rx DMA to use UART port as wakeup source */
- writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_RDMAS,
- sport->port.membase + UARTCR5);
+ if (lpuart_is_32(sport)) {
+ temp = lpuart32_read(&sport->port, UARTBAUD);
+ lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
+ UARTBAUD);
+ } else {
+ writeb(readb(sport->port.membase + UARTCR5) &
+ ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+ }
}
if (sport->lpuart_dma_tx_use) {
@@ -2305,7 +2563,7 @@
}
if (sport->port.suspended && !irq_wake)
- clk_disable_unprepare(sport->clk);
+ lpuart_disable_clks(sport);
return 0;
}
@@ -2314,23 +2572,14 @@
{
struct lpuart_port *sport = dev_get_drvdata(dev);
bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
- unsigned long temp;
if (sport->port.suspended && !irq_wake)
- clk_prepare_enable(sport->clk);
+ lpuart_enable_clks(sport);
- if (lpuart_is_32(sport)) {
- lpuart32_setup_watermark(sport);
- temp = lpuart32_read(&sport->port, UARTCTRL);
- temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE |
- UARTCTRL_TE | UARTCTRL_ILIE);
- lpuart32_write(&sport->port, temp, UARTCTRL);
- } else {
- lpuart_setup_watermark(sport);
- temp = readb(sport->port.membase + UARTCR2);
- temp |= (UARTCR2_RIE | UARTCR2_TIE | UARTCR2_RE | UARTCR2_TE);
- writeb(temp, sport->port.membase + UARTCR2);
- }
+ if (lpuart_is_32(sport))
+ lpuart32_setup_watermark_enable(sport);
+ else
+ lpuart_setup_watermark_enable(sport);
if (sport->lpuart_dma_rx_use) {
if (irq_wake) {
@@ -2341,14 +2590,10 @@
}
}
- if (sport->dma_tx_chan && !lpuart_dma_tx_request(&sport->port)) {
- init_waitqueue_head(&sport->dma_wait);
- sport->lpuart_dma_tx_use = true;
- writeb(readb(sport->port.membase + UARTCR5) |
- UARTCR5_TDMAS, sport->port.membase + UARTCR5);
- } else {
- sport->lpuart_dma_tx_use = false;
- }
+ lpuart_tx_dma_startup(sport);
+
+ if (lpuart_is_32(sport))
+ lpuart32_configure(sport);
uart_resume_port(&lpuart_reg, &sport->port);
@@ -2384,6 +2629,7 @@
static void __exit lpuart_serial_exit(void)
{
+ ida_destroy(&fsl_lpuart_ida);
platform_driver_unregister(&lpuart_driver);
uart_unregister_driver(&lpuart_reg);
}
diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c
index ad374f7..624f3d5 100644
--- a/drivers/tty/serial/icom.c
+++ b/drivers/tty/serial/icom.c
@@ -207,8 +207,6 @@
return -ENOMEM;
}
- memset(icom_port->statStg, 0, 4096);
-
/* FODs: Frame Out Descriptor Queue, this is a FIFO queue that
indicates that frames are to be transmitted
*/
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 0f67197..5e08f26 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -24,6 +24,7 @@
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/rational.h>
#include <linux/slab.h>
#include <linux/of.h>
@@ -382,6 +383,7 @@
}
#endif
+/* called with port.lock taken and irqs caller dependent */
static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
@@ -390,6 +392,7 @@
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
+/* called with port.lock taken and irqs caller dependent */
static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~UCR2_CTSC;
@@ -399,11 +402,6 @@
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
-static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
-{
- *ucr2 |= UCR2_CTSC;
-}
-
/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
@@ -441,7 +439,7 @@
return;
ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1);
+ imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1);
/* in rs485 mode disable transmitter if shifter is empty */
if (port->rs485.flags & SER_RS485_ENABLED &&
@@ -519,7 +517,7 @@
* and the TX IRQ is disabled.
**/
ucr1 = imx_uart_readl(sport, UCR1);
- ucr1 &= ~UCR1_TXMPTYEN;
+ ucr1 &= ~UCR1_TRDYEN;
if (sport->dma_is_txing) {
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
@@ -681,7 +679,7 @@
if (!sport->dma_is_enabled) {
ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
+ imx_uart_writel(sport, ucr1 | UCR1_TRDYEN, UCR1);
}
if (sport->dma_is_enabled) {
@@ -690,7 +688,7 @@
* disable TX DMA to let TX interrupt to send X-char */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
- ucr1 |= UCR1_TXMPTYEN;
+ ucr1 |= UCR1_TRDYEN;
imx_uart_writel(sport, ucr1, UCR1);
return;
}
@@ -706,27 +704,25 @@
{
struct imx_port *sport = dev_id;
u32 usr1;
- unsigned long flags;
- spin_lock_irqsave(&sport->port.lock, flags);
+ spin_lock(&sport->port.lock);
imx_uart_writel(sport, USR1_RTSD, USR1);
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ spin_unlock(&sport->port.lock);
return IRQ_HANDLED;
}
static irqreturn_t imx_uart_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
- unsigned long flags;
- spin_lock_irqsave(&sport->port.lock, flags);
+ spin_lock(&sport->port.lock);
imx_uart_transmit_buffer(sport);
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ spin_unlock(&sport->port.lock);
return IRQ_HANDLED;
}
@@ -735,9 +731,8 @@
struct imx_port *sport = dev_id;
unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
- unsigned long flags;
- spin_lock_irqsave(&sport->port.lock, flags);
+ spin_lock(&sport->port.lock);
while (imx_uart_readl(sport, USR2) & USR2_RDR) {
u32 usr2;
@@ -797,7 +792,7 @@
}
out:
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ spin_unlock(&sport->port.lock);
tty_flip_buffer_push(port);
return IRQ_HANDLED;
}
@@ -879,7 +874,7 @@
usr1 &= ~USR1_RRDY;
if ((ucr2 & UCR2_ATEN) == 0)
usr1 &= ~USR1_AGTIM;
- if ((ucr1 & UCR1_TXMPTYEN) == 0)
+ if ((ucr1 & UCR1_TRDYEN) == 0)
usr1 &= ~USR1_TRDY;
if ((ucr4 & UCR4_TCEN) == 0)
usr2 &= ~USR2_TXDC;
@@ -903,13 +898,11 @@
}
if (usr1 & USR1_DTRD) {
- unsigned long flags;
-
imx_uart_writel(sport, USR1_DTRD, USR1);
- spin_lock_irqsave(&sport->port.lock, flags);
+ spin_lock(&sport->port.lock);
imx_uart_mctrl_check(sport);
- spin_unlock_irqrestore(&sport->port.lock, flags);
+ spin_unlock(&sport->port.lock);
ret = IRQ_HANDLED;
}
@@ -970,10 +963,22 @@
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
u32 ucr2;
+ /*
+ * Turn off autoRTS if RTS is lowered and restore autoRTS
+ * setting if RTS is raised.
+ */
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
- if (mctrl & TIOCM_RTS)
- ucr2 |= UCR2_CTS | UCR2_CTSC;
+ if (mctrl & TIOCM_RTS) {
+ ucr2 |= UCR2_CTS;
+ /*
+ * UCR2_IRTS is unset if and only if the port is
+ * configured for CRTSCTS, so we use inverted UCR2_IRTS
+ * to get the state to restore to.
+ */
+ if (!(ucr2 & UCR2_IRTS))
+ ucr2 |= UCR2_CTSC;
+ }
imx_uart_writel(sport, ucr2, UCR2);
}
@@ -1169,7 +1174,6 @@
sport->port.icount.buf_overrun++;
tty_flip_buffer_push(port);
} else {
- dev_err(sport->port.dev, "DMA transaction error.\n");
if (usr1 & USR1_FRAMERR) {
sport->port.icount.frame++;
imx_uart_writel(sport, USR1_FRAMERR, USR1);
@@ -1470,7 +1474,7 @@
spin_lock_irqsave(&sport->port.lock, flags);
ucr1 = imx_uart_readl(sport, UCR1);
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1537,11 +1541,11 @@
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- u32 ucr2, old_ucr1, old_ucr2, ufcr;
+ u32 ucr2, old_ucr2, ufcr;
unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned long div;
- unsigned long num, denom;
+ unsigned long num, denom, old_ubir, old_ubmr;
uint64_t tdiv64;
/*
@@ -1554,49 +1558,6 @@
old_csize = CS8;
}
- if ((termios->c_cflag & CSIZE) == CS8)
- ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
- else
- ucr2 = UCR2_SRST | UCR2_IRTS;
-
- if (termios->c_cflag & CRTSCTS) {
- if (sport->have_rtscts) {
- ucr2 &= ~UCR2_IRTS;
-
- if (port->rs485.flags & SER_RS485_ENABLED) {
- /*
- * RTS is mandatory for rs485 operation, so keep
- * it under manual control and keep transmitter
- * disabled.
- */
- if (port->rs485.flags &
- SER_RS485_RTS_AFTER_SEND)
- imx_uart_rts_active(sport, &ucr2);
- else
- imx_uart_rts_inactive(sport, &ucr2);
- } else {
- imx_uart_rts_auto(sport, &ucr2);
- }
- } else {
- termios->c_cflag &= ~CRTSCTS;
- }
- } else if (port->rs485.flags & SER_RS485_ENABLED) {
- /* disable transmitter */
- if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- imx_uart_rts_active(sport, &ucr2);
- else
- imx_uart_rts_inactive(sport, &ucr2);
- }
-
-
- if (termios->c_cflag & CSTOPB)
- ucr2 |= UCR2_STPB;
- if (termios->c_cflag & PARENB) {
- ucr2 |= UCR2_PREN;
- if (termios->c_cflag & PARODD)
- ucr2 |= UCR2_PROE;
- }
-
del_timer_sync(&sport->timer);
/*
@@ -1607,6 +1568,51 @@
spin_lock_irqsave(&sport->port.lock, flags);
+ /*
+ * Read current UCR2 and save it for future use, then clear all the bits
+ * except those we will or may need to preserve.
+ */
+ old_ucr2 = imx_uart_readl(sport, UCR2);
+ ucr2 = old_ucr2 & (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN | UCR2_CTS);
+
+ ucr2 |= UCR2_SRST | UCR2_IRTS;
+ if ((termios->c_cflag & CSIZE) == CS8)
+ ucr2 |= UCR2_WS;
+
+ if (!sport->have_rtscts)
+ termios->c_cflag &= ~CRTSCTS;
+
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ /*
+ * RTS is mandatory for rs485 operation, so keep
+ * it under manual control and keep transmitter
+ * disabled.
+ */
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ imx_uart_rts_active(sport, &ucr2);
+ else
+ imx_uart_rts_inactive(sport, &ucr2);
+
+ } else if (termios->c_cflag & CRTSCTS) {
+ /*
+ * Only let receiver control RTS output if we were not requested
+ * to have RTS inactive (which then should take precedence).
+ */
+ if (ucr2 & UCR2_CTS)
+ ucr2 |= UCR2_CTSC;
+ }
+
+ if (termios->c_cflag & CRTSCTS)
+ ucr2 &= ~UCR2_IRTS;
+
+ if (termios->c_cflag & CSTOPB)
+ ucr2 |= UCR2_STPB;
+ if (termios->c_cflag & PARENB) {
+ ucr2 |= UCR2_PREN;
+ if (termios->c_cflag & PARODD)
+ ucr2 |= UCR2_PROE;
+ }
+
sport->port.read_status_mask = 0;
if (termios->c_iflag & INPCK)
sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
@@ -1637,23 +1643,6 @@
*/
uart_update_timeout(port, termios->c_cflag, baud);
- /*
- * disable interrupts and drain transmitter
- */
- old_ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport,
- old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- UCR1);
- old_ucr2 = imx_uart_readl(sport, UCR2);
- imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);
-
- while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))
- barrier();
-
- /* then, disable everything */
- imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);
- old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN);
-
/* custom-baudrate handling */
div = sport->port.uartclk / (baud * 16);
if (baud == 38400 && quot != div)
@@ -1681,17 +1670,27 @@
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
imx_uart_writel(sport, ufcr, UFCR);
- imx_uart_writel(sport, num, UBIR);
- imx_uart_writel(sport, denom, UBMR);
+ /*
+ * Two registers below should always be written both and in this
+ * particular order. One consequence is that we need to check if any of
+ * them changes and then update both. We do need the check for change
+ * as even writing the same values seem to "restart"
+ * transmission/receiving logic in the hardware, that leads to data
+ * breakage even when rate doesn't in fact change. E.g., user switches
+ * RTS/CTS handshake and suddenly gets broken bytes.
+ */
+ old_ubir = imx_uart_readl(sport, UBIR);
+ old_ubmr = imx_uart_readl(sport, UBMR);
+ if (old_ubir != num || old_ubmr != denom) {
+ imx_uart_writel(sport, num, UBIR);
+ imx_uart_writel(sport, denom, UBMR);
+ }
if (!imx_uart_is_imx1(sport))
imx_uart_writel(sport, sport->port.uartclk / div / 1000,
IMX21_ONEMS);
- imx_uart_writel(sport, old_ucr1, UCR1);
-
- /* set the parity, stop bits and data size */
- imx_uart_writel(sport, ucr2 | old_ucr2, UCR2);
+ imx_uart_writel(sport, ucr2, UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_uart_enable_ms(&sport->port);
@@ -1779,7 +1778,7 @@
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RTSDEN | UCR1_RRDYEN);
ucr2 |= UCR2_RXEN;
ucr2 &= ~UCR2_ATEN;
@@ -1939,7 +1938,7 @@
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
@@ -2020,7 +2019,7 @@
}
if (*baud != baud_raw)
- pr_info("Console IMX rounded baud rate from %d to %d\n",
+ dev_info(sport->port.dev, "Console IMX rounded baud rate from %d to %d\n",
baud_raw, *baud);
}
}
@@ -2068,7 +2067,7 @@
retval = clk_prepare(sport->clk_per);
if (retval)
- clk_disable_unprepare(sport->clk_ipg);
+ clk_unprepare(sport->clk_ipg);
error_console:
return retval;
@@ -2223,8 +2222,8 @@
return PTR_ERR(base);
rxirq = platform_get_irq(pdev, 0);
- txirq = platform_get_irq(pdev, 1);
- rtsirq = platform_get_irq(pdev, 2);
+ txirq = platform_get_irq_optional(pdev, 1);
+ rtsirq = platform_get_irq_optional(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
@@ -2295,7 +2294,7 @@
/* Disable interrupts before requesting them */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
- UCR1_TXMPTYEN | UCR1_RTSDEN);
+ UCR1_TRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
@@ -2384,8 +2383,13 @@
static void imx_uart_restore_context(struct imx_port *sport)
{
- if (!sport->context_saved)
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ if (!sport->context_saved) {
+ spin_unlock_irqrestore(&sport->port.lock, flags);
return;
+ }
imx_uart_writel(sport, sport->saved_reg[4], UFCR);
imx_uart_writel(sport, sport->saved_reg[5], UESC);
@@ -2398,11 +2402,15 @@
imx_uart_writel(sport, sport->saved_reg[2], UCR3);
imx_uart_writel(sport, sport->saved_reg[3], UCR4);
sport->context_saved = false;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
static void imx_uart_save_context(struct imx_port *sport)
{
+ unsigned long flags;
+
/* Save necessary regs */
+ spin_lock_irqsave(&sport->port.lock, flags);
sport->saved_reg[0] = imx_uart_readl(sport, UCR1);
sport->saved_reg[1] = imx_uart_readl(sport, UCR2);
sport->saved_reg[2] = imx_uart_readl(sport, UCR3);
@@ -2414,6 +2422,7 @@
sport->saved_reg[8] = imx_uart_readl(sport, UBMR);
sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS);
sport->context_saved = true;
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
@@ -2447,6 +2456,8 @@
clk_disable(sport->clk_ipg);
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
@@ -2455,6 +2466,8 @@
struct imx_port *sport = dev_get_drvdata(dev);
int ret;
+ pinctrl_pm_select_default_state(dev);
+
ret = clk_enable(sport->clk_ipg);
if (ret)
return ret;
diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c
deleted file mode 100644
index d8a1cdd..0000000
--- a/drivers/tty/serial/ioc3_serial.c
+++ /dev/null
@@ -1,2195 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-/*
- * This file contains a module version of the ioc3 serial driver. This
- * includes all the support functions needed (support functions, etc.)
- * and the serial driver itself.
- */
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/circ_buf.h>
-#include <linux/serial_reg.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/serial_core.h>
-#include <linux/ioc3.h>
-#include <linux/slab.h>
-
-/*
- * Interesting things about the ioc3
- */
-
-#define LOGICAL_PORTS 2 /* rs232(0) and rs422(1) */
-#define PORTS_PER_CARD 2
-#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS)
-#define MAX_CARDS 8
-#define MAX_LOGICAL_PORTS (LOGICAL_PORTS_PER_CARD * MAX_CARDS)
-
-/* determine given the sio_ir what port it applies to */
-#define GET_PORT_FROM_SIO_IR(_x) (_x & SIO_IR_SA) ? 0 : 1
-
-
-/*
- * we have 2 logical ports (rs232, rs422) for each physical port
- * evens are rs232, odds are rs422
- */
-#define GET_PHYSICAL_PORT(_x) ((_x) >> 1)
-#define GET_LOGICAL_PORT(_x) ((_x) & 1)
-#define IS_PHYSICAL_PORT(_x) !((_x) & 1)
-#define IS_RS232(_x) !((_x) & 1)
-
-static unsigned int Num_of_ioc3_cards;
-static unsigned int Submodule_slot;
-
-/* defining this will get you LOTS of great debug info */
-//#define DEBUG_INTERRUPTS
-#define DPRINT_CONFIG(_x...) ;
-//#define DPRINT_CONFIG(_x...) printk _x
-#define NOT_PROGRESS() ;
-//#define NOT_PROGRESS() printk("%s : fails %d\n", __func__, __LINE__)
-
-/* number of characters we want to transmit to the lower level at a time */
-#define MAX_CHARS 256
-#define FIFO_SIZE (MAX_CHARS-1) /* it's a uchar */
-
-/* Device name we're using */
-#define DEVICE_NAME "ttySIOC"
-#define DEVICE_MAJOR 204
-#define DEVICE_MINOR 116
-
-/* flags for next_char_state */
-#define NCS_BREAK 0x1
-#define NCS_PARITY 0x2
-#define NCS_FRAMING 0x4
-#define NCS_OVERRUN 0x8
-
-/* cause we need SOME parameters ... */
-#define MIN_BAUD_SUPPORTED 1200
-#define MAX_BAUD_SUPPORTED 115200
-
-/* protocol types supported */
-#define PROTO_RS232 0
-#define PROTO_RS422 1
-
-/* Notification types */
-#define N_DATA_READY 0x01
-#define N_OUTPUT_LOWAT 0x02
-#define N_BREAK 0x04
-#define N_PARITY_ERROR 0x08
-#define N_FRAMING_ERROR 0x10
-#define N_OVERRUN_ERROR 0x20
-#define N_DDCD 0x40
-#define N_DCTS 0x80
-
-#define N_ALL_INPUT (N_DATA_READY | N_BREAK \
- | N_PARITY_ERROR | N_FRAMING_ERROR \
- | N_OVERRUN_ERROR | N_DDCD | N_DCTS)
-
-#define N_ALL_OUTPUT N_OUTPUT_LOWAT
-
-#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR \
- | N_OVERRUN_ERROR)
-
-#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK \
- | N_PARITY_ERROR | N_FRAMING_ERROR \
- | N_OVERRUN_ERROR | N_DDCD | N_DCTS)
-
-#define SER_CLK_SPEED(prediv) ((22000000 << 1) / prediv)
-#define SER_DIVISOR(x, clk) (((clk) + (x) * 8) / ((x) * 16))
-#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div))
-
-/* Some masks */
-#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \
- | UART_LCR_WLEN7 | UART_LCR_WLEN8)
-#define LCR_MASK_STOP_BITS (UART_LCR_STOP)
-
-#define PENDING(_a, _p) (readl(&(_p)->vma->sio_ir) & (_a)->ic_enable)
-
-#define RING_BUF_SIZE 4096
-#define BUF_SIZE_BIT SBBR_L_SIZE
-#define PROD_CONS_MASK PROD_CONS_PTR_4K
-
-#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)
-
-/* driver specific - one per card */
-struct ioc3_card {
- struct {
- /* uart ports are allocated here */
- struct uart_port icp_uart_port[LOGICAL_PORTS];
- /* the ioc3_port used for this port */
- struct ioc3_port *icp_port;
- } ic_port[PORTS_PER_CARD];
- /* currently enabled interrupts */
- uint32_t ic_enable;
-};
-
-/* Local port info for each IOC3 serial port */
-struct ioc3_port {
- /* handy reference material */
- struct uart_port *ip_port;
- struct ioc3_card *ip_card;
- struct ioc3_driver_data *ip_idd;
- struct ioc3_submodule *ip_is;
-
- /* pci mem addresses for this port */
- struct ioc3_serialregs __iomem *ip_serial_regs;
- struct ioc3_uartregs __iomem *ip_uart_regs;
-
- /* Ring buffer page for this port */
- dma_addr_t ip_dma_ringbuf;
- /* vaddr of ring buffer */
- struct ring_buffer *ip_cpu_ringbuf;
-
- /* Rings for this port */
- struct ring *ip_inring;
- struct ring *ip_outring;
-
- /* Hook to port specific values */
- struct port_hooks *ip_hooks;
-
- spinlock_t ip_lock;
-
- /* Various rx/tx parameters */
- int ip_baud;
- int ip_tx_lowat;
- int ip_rx_timeout;
-
- /* Copy of notification bits */
- int ip_notify;
-
- /* Shadow copies of various registers so we don't need to PIO
- * read them constantly
- */
- uint32_t ip_sscr;
- uint32_t ip_tx_prod;
- uint32_t ip_rx_cons;
- unsigned char ip_flags;
-};
-
-/* tx low water mark. We need to notify the driver whenever tx is getting
- * close to empty so it can refill the tx buffer and keep things going.
- * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll
- * have no trouble getting in more chars in time (I certainly hope so).
- */
-#define TX_LOWAT_LATENCY 1000
-#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY)
-#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)
-
-/* Flags per port */
-#define INPUT_HIGH 0x01
- /* used to signify that we have turned off the rx_high
- * temporarily - we need to drain the fifo and don't
- * want to get blasted with interrupts.
- */
-#define DCD_ON 0x02
- /* DCD state is on */
-#define LOWAT_WRITTEN 0x04
-#define READ_ABORTED 0x08
- /* the read was aborted - used to avaoid infinate looping
- * in the interrupt handler
- */
-#define INPUT_ENABLE 0x10
-
-/* Since each port has different register offsets and bitmasks
- * for everything, we'll store those that we need in tables so we
- * don't have to be constantly checking the port we are dealing with.
- */
-struct port_hooks {
- uint32_t intr_delta_dcd;
- uint32_t intr_delta_cts;
- uint32_t intr_tx_mt;
- uint32_t intr_rx_timer;
- uint32_t intr_rx_high;
- uint32_t intr_tx_explicit;
- uint32_t intr_clear;
- uint32_t intr_all;
- char rs422_select_pin;
-};
-
-static struct port_hooks hooks_array[PORTS_PER_CARD] = {
- /* values for port A */
- {
- .intr_delta_dcd = SIO_IR_SA_DELTA_DCD,
- .intr_delta_cts = SIO_IR_SA_DELTA_CTS,
- .intr_tx_mt = SIO_IR_SA_TX_MT,
- .intr_rx_timer = SIO_IR_SA_RX_TIMER,
- .intr_rx_high = SIO_IR_SA_RX_HIGH,
- .intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT,
- .intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL
- | SIO_IR_SA_RX_HIGH
- | SIO_IR_SA_RX_TIMER
- | SIO_IR_SA_DELTA_DCD
- | SIO_IR_SA_DELTA_CTS
- | SIO_IR_SA_INT
- | SIO_IR_SA_TX_EXPLICIT
- | SIO_IR_SA_MEMERR),
- .intr_all = SIO_IR_SA,
- .rs422_select_pin = GPPR_UARTA_MODESEL_PIN,
- },
-
- /* values for port B */
- {
- .intr_delta_dcd = SIO_IR_SB_DELTA_DCD,
- .intr_delta_cts = SIO_IR_SB_DELTA_CTS,
- .intr_tx_mt = SIO_IR_SB_TX_MT,
- .intr_rx_timer = SIO_IR_SB_RX_TIMER,
- .intr_rx_high = SIO_IR_SB_RX_HIGH,
- .intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT,
- .intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL
- | SIO_IR_SB_RX_HIGH
- | SIO_IR_SB_RX_TIMER
- | SIO_IR_SB_DELTA_DCD
- | SIO_IR_SB_DELTA_CTS
- | SIO_IR_SB_INT
- | SIO_IR_SB_TX_EXPLICIT
- | SIO_IR_SB_MEMERR),
- .intr_all = SIO_IR_SB,
- .rs422_select_pin = GPPR_UARTB_MODESEL_PIN,
- }
-};
-
-struct ring_entry {
- union {
- struct {
- uint32_t alldata;
- uint32_t allsc;
- } all;
- struct {
- char data[4]; /* data bytes */
- char sc[4]; /* status/control */
- } s;
- } u;
-};
-
-/* Test the valid bits in any of the 4 sc chars using "allsc" member */
-#define RING_ANY_VALID \
- ((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101)
-
-#define ring_sc u.s.sc
-#define ring_data u.s.data
-#define ring_allsc u.all.allsc
-
-/* Number of entries per ring buffer. */
-#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))
-
-/* An individual ring */
-struct ring {
- struct ring_entry entries[ENTRIES_PER_RING];
-};
-
-/* The whole enchilada */
-struct ring_buffer {
- struct ring TX_A;
- struct ring RX_A;
- struct ring TX_B;
- struct ring RX_B;
-};
-
-/* Get a ring from a port struct */
-#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)
-
-/* for Infinite loop detection */
-#define MAXITER 10000000
-
-
-/**
- * set_baud - Baud rate setting code
- * @port: port to set
- * @baud: baud rate to use
- */
-static int set_baud(struct ioc3_port *port, int baud)
-{
- int divisor;
- int actual_baud;
- int diff;
- int lcr, prediv;
- struct ioc3_uartregs __iomem *uart;
-
- for (prediv = 6; prediv < 64; prediv++) {
- divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv));
- if (!divisor)
- continue; /* invalid divisor */
- actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv));
-
- diff = actual_baud - baud;
- if (diff < 0)
- diff = -diff;
-
- /* if we're within 1% we've found a match */
- if (diff * 100 <= actual_baud)
- break;
- }
-
- /* if the above loop completed, we didn't match
- * the baud rate. give up.
- */
- if (prediv == 64) {
- NOT_PROGRESS();
- return 1;
- }
-
- uart = port->ip_uart_regs;
- lcr = readb(&uart->iu_lcr);
-
- writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr);
- writeb((unsigned char)divisor, &uart->iu_dll);
- writeb((unsigned char)(divisor >> 8), &uart->iu_dlm);
- writeb((unsigned char)prediv, &uart->iu_scr);
- writeb((unsigned char)lcr, &uart->iu_lcr);
-
- return 0;
-}
-
-/**
- * get_ioc3_port - given a uart port, return the control structure
- * @the_port: uart port to find
- */
-static struct ioc3_port *get_ioc3_port(struct uart_port *the_port)
-{
- struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev);
- struct ioc3_card *card_ptr = idd->data[Submodule_slot];
- int ii, jj;
-
- if (!card_ptr) {
- NOT_PROGRESS();
- return NULL;
- }
- for (ii = 0; ii < PORTS_PER_CARD; ii++) {
- for (jj = 0; jj < LOGICAL_PORTS; jj++) {
- if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj])
- return card_ptr->ic_port[ii].icp_port;
- }
- }
- NOT_PROGRESS();
- return NULL;
-}
-
-/**
- * port_init - Initialize the sio and ioc3 hardware for a given port
- * called per port from attach...
- * @port: port to initialize
- */
-static inline int port_init(struct ioc3_port *port)
-{
- uint32_t sio_cr;
- struct port_hooks *hooks = port->ip_hooks;
- struct ioc3_uartregs __iomem *uart;
- int reset_loop_counter = 0xfffff;
- struct ioc3_driver_data *idd = port->ip_idd;
-
- /* Idle the IOC3 serial interface */
- writel(SSCR_RESET, &port->ip_serial_regs->sscr);
-
- /* Wait until any pending bus activity for this port has ceased */
- do {
- sio_cr = readl(&idd->vma->sio_cr);
- if (reset_loop_counter-- <= 0) {
- printk(KERN_WARNING
- "IOC3 unable to come out of reset"
- " scr 0x%x\n", sio_cr);
- return -1;
- }
- } while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) &&
- (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA)
- || sio_cr == SIO_CR_ARB_DIAG_TXB
- || sio_cr == SIO_CR_ARB_DIAG_RXA
- || sio_cr == SIO_CR_ARB_DIAG_RXB));
-
- /* Finish reset sequence */
- writel(0, &port->ip_serial_regs->sscr);
-
- /* Once RESET is done, reload cached tx_prod and rx_cons values
- * and set rings to empty by making prod == cons
- */
- port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
- writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
- port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
- writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir);
-
- /* Disable interrupts for this 16550 */
- uart = port->ip_uart_regs;
- writeb(0, &uart->iu_lcr);
- writeb(0, &uart->iu_ier);
-
- /* Set the default baud */
- set_baud(port, port->ip_baud);
-
- /* Set line control to 8 bits no parity */
- writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr);
- /* UART_LCR_STOP == 1 stop */
-
- /* Enable the FIFOs */
- writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr);
- /* then reset 16550 FIFOs */
- writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
- &uart->iu_fcr);
-
- /* Clear modem control register */
- writeb(0, &uart->iu_mcr);
-
- /* Clear deltas in modem status register */
- writel(0, &port->ip_serial_regs->shadow);
-
- /* Only do this once per port pair */
- if (port->ip_hooks == &hooks_array[0]) {
- unsigned long ring_pci_addr;
- uint32_t __iomem *sbbr_l, *sbbr_h;
-
- sbbr_l = &idd->vma->sbbr_l;
- sbbr_h = &idd->vma->sbbr_h;
- ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
- DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n",
- __func__, (void *)ring_pci_addr));
-
- writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h);
- writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l);
- }
-
- /* Set the receive timeout value to 10 msec */
- writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr);
-
- /* Set rx threshold, enable DMA */
- /* Set high water mark at 3/4 of full ring */
- port->ip_sscr = (ENTRIES_PER_RING * 3 / 4);
-
- /* uart experiences pauses at high baud rate reducing actual
- * throughput by 10% or so unless we enable high speed polling
- * XXX when this hardware bug is resolved we should revert to
- * normal polling speed
- */
- port->ip_sscr |= SSCR_HIGH_SPD;
-
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Disable and clear all serial related interrupt bits */
- port->ip_card->ic_enable &= ~hooks->intr_clear;
- ioc3_disable(port->ip_is, idd, hooks->intr_clear);
- ioc3_ack(port->ip_is, idd, hooks->intr_clear);
- return 0;
-}
-
-/**
- * enable_intrs - enable interrupts
- * @port: port to enable
- * @mask: mask to use
- */
-static void enable_intrs(struct ioc3_port *port, uint32_t mask)
-{
- if ((port->ip_card->ic_enable & mask) != mask) {
- port->ip_card->ic_enable |= mask;
- ioc3_enable(port->ip_is, port->ip_idd, mask);
- }
-}
-
-/**
- * local_open - local open a port
- * @port: port to open
- */
-static inline int local_open(struct ioc3_port *port)
-{
- int spiniter = 0;
-
- port->ip_flags = INPUT_ENABLE;
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & SSCR_DMA_EN) {
- writel(port->ip_sscr | SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while ((readl(&port->ip_serial_regs->sscr)
- & SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER) {
- NOT_PROGRESS();
- return -1;
- }
- }
- }
-
- /* Reset the input fifo. If the uart received chars while the port
- * was closed and DMA is not enabled, the uart may have a bunch of
- * chars hanging around in its rx fifo which will not be discarded
- * by rclr in the upper layer. We must get rid of them here.
- */
- writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR,
- &port->ip_uart_regs->iu_fcr);
-
- writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr);
- /* UART_LCR_STOP == 1 stop */
-
- /* Re-enable DMA, set default threshold to intr whenever there is
- * data available.
- */
- port->ip_sscr &= ~SSCR_RX_THRESHOLD;
- port->ip_sscr |= 1; /* default threshold */
-
- /* Plug in the new sscr. This implicitly clears the DMA_PAUSE
- * flag if it was set above
- */
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- port->ip_tx_lowat = 1;
- return 0;
-}
-
-/**
- * set_rx_timeout - Set rx timeout and threshold values.
- * @port: port to use
- * @timeout: timeout value in ticks
- */
-static inline int set_rx_timeout(struct ioc3_port *port, int timeout)
-{
- int threshold;
-
- port->ip_rx_timeout = timeout;
-
- /* Timeout is in ticks. Let's figure out how many chars we
- * can receive at the current baud rate in that interval
- * and set the rx threshold to that amount. There are 4 chars
- * per ring entry, so we'll divide the number of chars that will
- * arrive in timeout by 4.
- * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.
- */
- threshold = timeout * port->ip_baud / 4000;
- if (threshold == 0)
- threshold = 1; /* otherwise we'll intr all the time! */
-
- if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD)
- return 1;
-
- port->ip_sscr &= ~SSCR_RX_THRESHOLD;
- port->ip_sscr |= threshold;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Now set the rx timeout to the given value
- * again timeout * SRTR_HZ / HZ
- */
- timeout = timeout * SRTR_HZ / 100;
- if (timeout > SRTR_CNT)
- timeout = SRTR_CNT;
- writel(timeout, &port->ip_serial_regs->srtr);
- return 0;
-}
-
-/**
- * config_port - config the hardware
- * @port: port to config
- * @baud: baud rate for the port
- * @byte_size: data size
- * @stop_bits: number of stop bits
- * @parenb: parity enable ?
- * @parodd: odd parity ?
- */
-static inline int
-config_port(struct ioc3_port *port,
- int baud, int byte_size, int stop_bits, int parenb, int parodd)
-{
- char lcr, sizebits;
- int spiniter = 0;
-
- DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d "
- "parodd %d\n",
- __func__, ((struct uart_port *)port->ip_port)->line,
- baud, byte_size, stop_bits, parenb, parodd));
-
- if (set_baud(port, baud))
- return 1;
-
- switch (byte_size) {
- case 5:
- sizebits = UART_LCR_WLEN5;
- break;
- case 6:
- sizebits = UART_LCR_WLEN6;
- break;
- case 7:
- sizebits = UART_LCR_WLEN7;
- break;
- case 8:
- sizebits = UART_LCR_WLEN8;
- break;
- default:
- return 1;
- }
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & SSCR_DMA_EN) {
- writel(port->ip_sscr | SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while ((readl(&port->ip_serial_regs->sscr)
- & SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER)
- return -1;
- }
- }
-
- /* Clear relevant fields in lcr */
- lcr = readb(&port->ip_uart_regs->iu_lcr);
- lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |
- UART_LCR_PARITY | LCR_MASK_STOP_BITS);
-
- /* Set byte size in lcr */
- lcr |= sizebits;
-
- /* Set parity */
- if (parenb) {
- lcr |= UART_LCR_PARITY;
- if (!parodd)
- lcr |= UART_LCR_EPAR;
- }
-
- /* Set stop bits */
- if (stop_bits)
- lcr |= UART_LCR_STOP /* 2 stop bits */ ;
-
- writeb(lcr, &port->ip_uart_regs->iu_lcr);
-
- /* Re-enable the DMA interface if necessary */
- if (port->ip_sscr & SSCR_DMA_EN) {
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- port->ip_baud = baud;
-
- /* When we get within this number of ring entries of filling the
- * entire ring on tx, place an EXPLICIT intr to generate a lowat
- * notification when output has drained.
- */
- port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;
- if (port->ip_tx_lowat == 0)
- port->ip_tx_lowat = 1;
-
- set_rx_timeout(port, 2);
- return 0;
-}
-
-/**
- * do_write - Write bytes to the port. Returns the number of bytes
- * actually written. Called from transmit_chars
- * @port: port to use
- * @buf: the stuff to write
- * @len: how many bytes in 'buf'
- */
-static inline int do_write(struct ioc3_port *port, char *buf, int len)
-{
- int prod_ptr, cons_ptr, total = 0;
- struct ring *outring;
- struct ring_entry *entry;
- struct port_hooks *hooks = port->ip_hooks;
-
- BUG_ON(!(len >= 0));
-
- prod_ptr = port->ip_tx_prod;
- cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
- outring = port->ip_outring;
-
- /* Maintain a 1-entry red-zone. The ring buffer is full when
- * (cons - prod) % ring_size is 1. Rather than do this subtraction
- * in the body of the loop, I'll do it now.
- */
- cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;
-
- /* Stuff the bytes into the output */
- while ((prod_ptr != cons_ptr) && (len > 0)) {
- int xx;
-
- /* Get 4 bytes (one ring entry) at a time */
- entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);
-
- /* Invalidate all entries */
- entry->ring_allsc = 0;
-
- /* Copy in some bytes */
- for (xx = 0; (xx < 4) && (len > 0); xx++) {
- entry->ring_data[xx] = *buf++;
- entry->ring_sc[xx] = TXCB_VALID;
- len--;
- total++;
- }
-
- /* If we are within some small threshold of filling up the
- * entire ring buffer, we must place an EXPLICIT intr here
- * to generate a lowat interrupt in case we subsequently
- * really do fill up the ring and the caller goes to sleep.
- * No need to place more than one though.
- */
- if (!(port->ip_flags & LOWAT_WRITTEN) &&
- ((cons_ptr - prod_ptr) & PROD_CONS_MASK)
- <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) {
- port->ip_flags |= LOWAT_WRITTEN;
- entry->ring_sc[0] |= TXCB_INT_WHEN_DONE;
- }
-
- /* Go on to next entry */
- prod_ptr += sizeof(struct ring_entry);
- prod_ptr &= PROD_CONS_MASK;
- }
-
- /* If we sent something, start DMA if necessary */
- if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) {
- port->ip_sscr |= SSCR_DMA_EN;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
-
- /* Store the new producer pointer. If tx is disabled, we stuff the
- * data into the ring buffer, but we don't actually start tx.
- */
- if (!uart_tx_stopped(port->ip_port)) {
- writel(prod_ptr, &port->ip_serial_regs->stpir);
-
- /* If we are now transmitting, enable tx_mt interrupt so we
- * can disable DMA if necessary when the tx finishes.
- */
- if (total > 0)
- enable_intrs(port, hooks->intr_tx_mt);
- }
- port->ip_tx_prod = prod_ptr;
-
- return total;
-}
-
-/**
- * disable_intrs - disable interrupts
- * @port: port to enable
- * @mask: mask to use
- */
-static inline void disable_intrs(struct ioc3_port *port, uint32_t mask)
-{
- if (port->ip_card->ic_enable & mask) {
- ioc3_disable(port->ip_is, port->ip_idd, mask);
- port->ip_card->ic_enable &= ~mask;
- }
-}
-
-/**
- * set_notification - Modify event notification
- * @port: port to use
- * @mask: events mask
- * @set_on: set ?
- */
-static int set_notification(struct ioc3_port *port, int mask, int set_on)
-{
- struct port_hooks *hooks = port->ip_hooks;
- uint32_t intrbits, sscrbits;
-
- BUG_ON(!mask);
-
- intrbits = sscrbits = 0;
-
- if (mask & N_DATA_READY)
- intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);
- if (mask & N_OUTPUT_LOWAT)
- intrbits |= hooks->intr_tx_explicit;
- if (mask & N_DDCD) {
- intrbits |= hooks->intr_delta_dcd;
- sscrbits |= SSCR_RX_RING_DCD;
- }
- if (mask & N_DCTS)
- intrbits |= hooks->intr_delta_cts;
-
- if (set_on) {
- enable_intrs(port, intrbits);
- port->ip_notify |= mask;
- port->ip_sscr |= sscrbits;
- } else {
- disable_intrs(port, intrbits);
- port->ip_notify &= ~mask;
- port->ip_sscr &= ~sscrbits;
- }
-
- /* We require DMA if either DATA_READY or DDCD notification is
- * currently requested. If neither of these is requested and
- * there is currently no tx in progress, DMA may be disabled.
- */
- if (port->ip_notify & (N_DATA_READY | N_DDCD))
- port->ip_sscr |= SSCR_DMA_EN;
- else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt))
- port->ip_sscr &= ~SSCR_DMA_EN;
-
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- return 0;
-}
-
-/**
- * set_mcr - set the master control reg
- * @the_port: port to use
- * @mask1: mcr mask
- * @mask2: shadow mask
- */
-static inline int set_mcr(struct uart_port *the_port,
- int mask1, int mask2)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
- uint32_t shadow;
- int spiniter = 0;
- char mcr;
-
- if (!port)
- return -1;
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & SSCR_DMA_EN) {
- writel(port->ip_sscr | SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while ((readl(&port->ip_serial_regs->sscr)
- & SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER)
- return -1;
- }
- }
- shadow = readl(&port->ip_serial_regs->shadow);
- mcr = (shadow & 0xff000000) >> 24;
-
- /* Set new value */
- mcr |= mask1;
- shadow |= mask2;
- writeb(mcr, &port->ip_uart_regs->iu_mcr);
- writel(shadow, &port->ip_serial_regs->shadow);
-
- /* Re-enable the DMA interface if necessary */
- if (port->ip_sscr & SSCR_DMA_EN) {
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- return 0;
-}
-
-/**
- * ioc3_set_proto - set the protocol for the port
- * @port: port to use
- * @proto: protocol to use
- */
-static int ioc3_set_proto(struct ioc3_port *port, int proto)
-{
- struct port_hooks *hooks = port->ip_hooks;
-
- switch (proto) {
- default:
- case PROTO_RS232:
- /* Clear the appropriate GIO pin */
- DPRINT_CONFIG(("%s: rs232\n", __func__));
- writel(0, (&port->ip_idd->vma->gppr[0]
- + hooks->rs422_select_pin));
- break;
-
- case PROTO_RS422:
- /* Set the appropriate GIO pin */
- DPRINT_CONFIG(("%s: rs422\n", __func__));
- writel(1, (&port->ip_idd->vma->gppr[0]
- + hooks->rs422_select_pin));
- break;
- }
- return 0;
-}
-
-/**
- * transmit_chars - upper level write, called with the_port->lock
- * @the_port: port to write
- */
-static void transmit_chars(struct uart_port *the_port)
-{
- int xmit_count, tail, head;
- int result;
- char *start;
- struct tty_struct *tty;
- struct ioc3_port *port = get_ioc3_port(the_port);
- struct uart_state *state;
-
- if (!the_port)
- return;
- if (!port)
- return;
-
- state = the_port->state;
- tty = state->port.tty;
-
- if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) {
- /* Nothing to do or hw stopped */
- set_notification(port, N_ALL_OUTPUT, 0);
- return;
- }
-
- head = state->xmit.head;
- tail = state->xmit.tail;
- start = (char *)&state->xmit.buf[tail];
-
- /* write out all the data or until the end of the buffer */
- xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
- if (xmit_count > 0) {
- result = do_write(port, start, xmit_count);
- if (result > 0) {
- /* booking */
- xmit_count -= result;
- the_port->icount.tx += result;
- /* advance the pointers */
- tail += result;
- tail &= UART_XMIT_SIZE - 1;
- state->xmit.tail = tail;
- start = (char *)&state->xmit.buf[tail];
- }
- }
- if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS)
- uart_write_wakeup(the_port);
-
- if (uart_circ_empty(&state->xmit)) {
- set_notification(port, N_OUTPUT_LOWAT, 0);
- } else {
- set_notification(port, N_OUTPUT_LOWAT, 1);
- }
-}
-
-/**
- * ioc3_change_speed - change the speed of the port
- * @the_port: port to change
- * @new_termios: new termios settings
- * @old_termios: old termios settings
- */
-static void
-ioc3_change_speed(struct uart_port *the_port,
- struct ktermios *new_termios, struct ktermios *old_termios)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
- unsigned int cflag, iflag;
- int baud;
- int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
- struct uart_state *state = the_port->state;
-
- cflag = new_termios->c_cflag;
- iflag = new_termios->c_iflag;
-
- switch (cflag & CSIZE) {
- case CS5:
- new_data = 5;
- break;
- case CS6:
- new_data = 6;
- break;
- case CS7:
- new_data = 7;
- break;
- case CS8:
- new_data = 8;
- break;
- default:
- /* cuz we always need a default ... */
- new_data = 5;
- break;
- }
- if (cflag & CSTOPB) {
- new_stop = 1;
- }
- if (cflag & PARENB) {
- new_parity_enable = 1;
- if (cflag & PARODD)
- new_parity = 1;
- }
- baud = uart_get_baud_rate(the_port, new_termios, old_termios,
- MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
- DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud,
- the_port->line));
-
- if (!the_port->fifosize)
- the_port->fifosize = FIFO_SIZE;
- uart_update_timeout(the_port, cflag, baud);
-
- the_port->ignore_status_mask = N_ALL_INPUT;
-
- state->port.low_latency = 1;
-
- if (iflag & IGNPAR)
- the_port->ignore_status_mask &= ~(N_PARITY_ERROR
- | N_FRAMING_ERROR);
- if (iflag & IGNBRK) {
- the_port->ignore_status_mask &= ~N_BREAK;
- if (iflag & IGNPAR)
- the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
- }
- if (!(cflag & CREAD)) {
- /* ignore everything */
- the_port->ignore_status_mask &= ~N_DATA_READY;
- }
-
- if (cflag & CRTSCTS) {
- /* enable hardware flow control */
- port->ip_sscr |= SSCR_HFC_EN;
- }
- else {
- /* disable hardware flow control */
- port->ip_sscr &= ~SSCR_HFC_EN;
- }
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Set the configuration and proper notification call */
- DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o "
- "config_port(baud %d data %d stop %d penable %d "
- " parity %d), notification 0x%x\n",
- __func__, (void *)port, the_port->line, cflag, baud,
- new_data, new_stop, new_parity_enable, new_parity,
- the_port->ignore_status_mask));
-
- if ((config_port(port, baud, /* baud */
- new_data, /* byte size */
- new_stop, /* stop bits */
- new_parity_enable, /* set parity */
- new_parity)) >= 0) { /* parity 1==odd */
- set_notification(port, the_port->ignore_status_mask, 1);
- }
-}
-
-/**
- * ic3_startup_local - Start up the serial port - returns >= 0 if no errors
- * @the_port: Port to operate on
- */
-static inline int ic3_startup_local(struct uart_port *the_port)
-{
- struct ioc3_port *port;
-
- if (!the_port) {
- NOT_PROGRESS();
- return -1;
- }
-
- port = get_ioc3_port(the_port);
- if (!port) {
- NOT_PROGRESS();
- return -1;
- }
-
- local_open(port);
-
- /* set the protocol */
- ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 :
- PROTO_RS422);
- return 0;
-}
-
-/*
- * ioc3_cb_output_lowat - called when the output low water mark is hit
- * @port: port to output
- */
-static void ioc3_cb_output_lowat(struct ioc3_port *port)
-{
- unsigned long pflags;
-
- /* the_port->lock is set on the call here */
- if (port->ip_port) {
- spin_lock_irqsave(&port->ip_port->lock, pflags);
- transmit_chars(port->ip_port);
- spin_unlock_irqrestore(&port->ip_port->lock, pflags);
- }
-}
-
-/*
- * ioc3_cb_post_ncs - called for some basic errors
- * @port: port to use
- * @ncs: event
- */
-static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs)
-{
- struct uart_icount *icount;
-
- icount = &the_port->icount;
-
- if (ncs & NCS_BREAK)
- icount->brk++;
- if (ncs & NCS_FRAMING)
- icount->frame++;
- if (ncs & NCS_OVERRUN)
- icount->overrun++;
- if (ncs & NCS_PARITY)
- icount->parity++;
-}
-
-/**
- * do_read - Read in bytes from the port. Return the number of bytes
- * actually read.
- * @the_port: port to use
- * @buf: place to put the stuff we read
- * @len: how big 'buf' is
- */
-
-static inline int do_read(struct uart_port *the_port, char *buf, int len)
-{
- int prod_ptr, cons_ptr, total;
- struct ioc3_port *port = get_ioc3_port(the_port);
- struct ring *inring;
- struct ring_entry *entry;
- struct port_hooks *hooks;
- int byte_num;
- char *sc;
- int loop_counter;
-
- BUG_ON(!(len >= 0));
- BUG_ON(!port);
- hooks = port->ip_hooks;
-
- /* There is a nasty timing issue in the IOC3. When the rx_timer
- * expires or the rx_high condition arises, we take an interrupt.
- * At some point while servicing the interrupt, we read bytes from
- * the ring buffer and re-arm the rx_timer. However the rx_timer is
- * not started until the first byte is received *after* it is armed,
- * and any bytes pending in the rx construction buffers are not drained
- * to memory until either there are 4 bytes available or the rx_timer
- * expires. This leads to a potential situation where data is left
- * in the construction buffers forever - 1 to 3 bytes were received
- * after the interrupt was generated but before the rx_timer was
- * re-armed. At that point as long as no subsequent bytes are received
- * the timer will never be started and the bytes will remain in the
- * construction buffer forever. The solution is to execute a DRAIN
- * command after rearming the timer. This way any bytes received before
- * the DRAIN will be drained to memory, and any bytes received after
- * the DRAIN will start the TIMER and be drained when it expires.
- * Luckily, this only needs to be done when the DMA buffer is empty
- * since there is no requirement that this function return all
- * available data as long as it returns some.
- */
- /* Re-arm the timer */
-
- writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir);
-
- prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
- cons_ptr = port->ip_rx_cons;
-
- if (prod_ptr == cons_ptr) {
- int reset_dma = 0;
-
- /* Input buffer appears empty, do a flush. */
-
- /* DMA must be enabled for this to work. */
- if (!(port->ip_sscr & SSCR_DMA_EN)) {
- port->ip_sscr |= SSCR_DMA_EN;
- reset_dma = 1;
- }
-
- /* Potential race condition: we must reload the srpir after
- * issuing the drain command, otherwise we could think the rx
- * buffer is empty, then take a very long interrupt, and when
- * we come back it's full and we wait forever for the drain to
- * complete.
- */
- writel(port->ip_sscr | SSCR_RX_DRAIN,
- &port->ip_serial_regs->sscr);
- prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
-
- /* We must not wait for the DRAIN to complete unless there are
- * at least 8 bytes (2 ring entries) available to receive the
- * data otherwise the DRAIN will never complete and we'll
- * deadlock here.
- * In fact, to make things easier, I'll just ignore the flush if
- * there is any data at all now available.
- */
- if (prod_ptr == cons_ptr) {
- loop_counter = 0;
- while (readl(&port->ip_serial_regs->sscr) &
- SSCR_RX_DRAIN) {
- loop_counter++;
- if (loop_counter > MAXITER)
- return -1;
- }
-
- /* SIGH. We have to reload the prod_ptr *again* since
- * the drain may have caused it to change
- */
- prod_ptr = readl(&port->ip_serial_regs->srpir)
- & PROD_CONS_MASK;
- }
- if (reset_dma) {
- port->ip_sscr &= ~SSCR_DMA_EN;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- }
- inring = port->ip_inring;
- port->ip_flags &= ~READ_ABORTED;
-
- total = 0;
- loop_counter = 0xfffff; /* to avoid hangs */
-
- /* Grab bytes from the hardware */
- while ((prod_ptr != cons_ptr) && (len > 0)) {
- entry = (struct ring_entry *)((caddr_t) inring + cons_ptr);
-
- if (loop_counter-- <= 0) {
- printk(KERN_WARNING "IOC3 serial: "
- "possible hang condition/"
- "port stuck on read (line %d).\n",
- the_port->line);
- break;
- }
-
- /* According to the producer pointer, this ring entry
- * must contain some data. But if the PIO happened faster
- * than the DMA, the data may not be available yet, so let's
- * wait until it arrives.
- */
- if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
- /* Indicate the read is aborted so we don't disable
- * the interrupt thinking that the consumer is
- * congested.
- */
- port->ip_flags |= READ_ABORTED;
- len = 0;
- break;
- }
-
- /* Load the bytes/status out of the ring entry */
- for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) {
- sc = &(entry->ring_sc[byte_num]);
-
- /* Check for change in modem state or overrun */
- if ((*sc & RXSB_MODEM_VALID)
- && (port->ip_notify & N_DDCD)) {
- /* Notify upper layer if DCD dropped */
- if ((port->ip_flags & DCD_ON)
- && !(*sc & RXSB_DCD)) {
- /* If we have already copied some data,
- * return it. We'll pick up the carrier
- * drop on the next pass. That way we
- * don't throw away the data that has
- * already been copied back to
- * the caller's buffer.
- */
- if (total > 0) {
- len = 0;
- break;
- }
- port->ip_flags &= ~DCD_ON;
-
- /* Turn off this notification so the
- * carrier drop protocol won't see it
- * again when it does a read.
- */
- *sc &= ~RXSB_MODEM_VALID;
-
- /* To keep things consistent, we need
- * to update the consumer pointer so
- * the next reader won't come in and
- * try to read the same ring entries
- * again. This must be done here before
- * the dcd change.
- */
-
- if ((entry->ring_allsc & RING_ANY_VALID)
- == 0) {
- cons_ptr += (int)sizeof
- (struct ring_entry);
- cons_ptr &= PROD_CONS_MASK;
- }
- writel(cons_ptr,
- &port->ip_serial_regs->srcir);
- port->ip_rx_cons = cons_ptr;
-
- /* Notify upper layer of carrier drop */
- if ((port->ip_notify & N_DDCD)
- && port->ip_port) {
- uart_handle_dcd_change
- (port->ip_port, 0);
- wake_up_interruptible
- (&the_port->state->
- port.delta_msr_wait);
- }
-
- /* If we had any data to return, we
- * would have returned it above.
- */
- return 0;
- }
- }
- if (*sc & RXSB_MODEM_VALID) {
- /* Notify that an input overrun occurred */
- if ((*sc & RXSB_OVERRUN)
- && (port->ip_notify & N_OVERRUN_ERROR)) {
- ioc3_cb_post_ncs(the_port, NCS_OVERRUN);
- }
- /* Don't look at this byte again */
- *sc &= ~RXSB_MODEM_VALID;
- }
-
- /* Check for valid data or RX errors */
- if ((*sc & RXSB_DATA_VALID) &&
- ((*sc & (RXSB_PAR_ERR
- | RXSB_FRAME_ERR | RXSB_BREAK))
- && (port->ip_notify & (N_PARITY_ERROR
- | N_FRAMING_ERROR
- | N_BREAK)))) {
- /* There is an error condition on the next byte.
- * If we have already transferred some bytes,
- * we'll stop here. Otherwise if this is the
- * first byte to be read, we'll just transfer
- * it alone after notifying the
- * upper layer of its status.
- */
- if (total > 0) {
- len = 0;
- break;
- } else {
- if ((*sc & RXSB_PAR_ERR) &&
- (port->
- ip_notify & N_PARITY_ERROR)) {
- ioc3_cb_post_ncs(the_port,
- NCS_PARITY);
- }
- if ((*sc & RXSB_FRAME_ERR) &&
- (port->
- ip_notify & N_FRAMING_ERROR)) {
- ioc3_cb_post_ncs(the_port,
- NCS_FRAMING);
- }
- if ((*sc & RXSB_BREAK)
- && (port->ip_notify & N_BREAK)) {
- ioc3_cb_post_ncs
- (the_port, NCS_BREAK);
- }
- len = 1;
- }
- }
- if (*sc & RXSB_DATA_VALID) {
- *sc &= ~RXSB_DATA_VALID;
- *buf = entry->ring_data[byte_num];
- buf++;
- len--;
- total++;
- }
- }
-
- /* If we used up this entry entirely, go on to the next one,
- * otherwise we must have run out of buffer space, so
- * leave the consumer pointer here for the next read in case
- * there are still unread bytes in this entry.
- */
- if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
- cons_ptr += (int)sizeof(struct ring_entry);
- cons_ptr &= PROD_CONS_MASK;
- }
- }
-
- /* Update consumer pointer and re-arm rx timer interrupt */
- writel(cons_ptr, &port->ip_serial_regs->srcir);
- port->ip_rx_cons = cons_ptr;
-
- /* If we have now dipped below the rx high water mark and we have
- * rx_high interrupt turned off, we can now turn it back on again.
- */
- if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr)
- & PROD_CONS_MASK) <
- ((port->
- ip_sscr &
- SSCR_RX_THRESHOLD)
- << PROD_CONS_PTR_OFF))) {
- port->ip_flags &= ~INPUT_HIGH;
- enable_intrs(port, hooks->intr_rx_high);
- }
- return total;
-}
-
-/**
- * receive_chars - upper level read.
- * @the_port: port to read from
- */
-static int receive_chars(struct uart_port *the_port)
-{
- unsigned char ch[MAX_CHARS];
- int read_count = 0, read_room, flip = 0;
- struct uart_state *state = the_port->state;
- struct ioc3_port *port = get_ioc3_port(the_port);
- unsigned long pflags;
-
- /* Make sure all the pointers are "good" ones */
- if (!state)
- return 0;
-
- if (!(port->ip_flags & INPUT_ENABLE))
- return 0;
-
- spin_lock_irqsave(&the_port->lock, pflags);
-
- read_count = do_read(the_port, ch, MAX_CHARS);
- if (read_count > 0) {
- flip = 1;
- read_room = tty_insert_flip_string(&state->port, ch,
- read_count);
- the_port->icount.rx += read_count;
- }
- spin_unlock_irqrestore(&the_port->lock, pflags);
-
- if (flip)
- tty_flip_buffer_push(&state->port);
-
- return read_count;
-}
-
-/**
- * ioc3uart_intr_one - lowest level (per port) interrupt handler.
- * @is : submodule
- * @idd: driver data
- * @pending: interrupts to handle
- */
-
-static inline int
-ioc3uart_intr_one(struct ioc3_submodule *is,
- struct ioc3_driver_data *idd,
- unsigned int pending)
-{
- int port_num = GET_PORT_FROM_SIO_IR(pending);
- struct port_hooks *hooks;
- unsigned int rx_high_rd_aborted = 0;
- unsigned long flags;
- struct uart_port *the_port;
- struct ioc3_port *port;
- int loop_counter;
- struct ioc3_card *card_ptr;
- unsigned int sio_ir;
-
- card_ptr = idd->data[is->id];
- port = card_ptr->ic_port[port_num].icp_port;
- hooks = port->ip_hooks;
-
- /* Possible race condition here: The tx_mt interrupt bit may be
- * cleared without the intervention of the interrupt handler,
- * e.g. by a write. If the top level interrupt handler reads a
- * tx_mt, then some other processor does a write, starting up
- * output, then we come in here, see the tx_mt and stop DMA, the
- * output started by the other processor will hang. Thus we can
- * only rely on tx_mt being legitimate if it is read while the
- * port lock is held. Therefore this bit must be ignored in the
- * passed in interrupt mask which was read by the top level
- * interrupt handler since the port lock was not held at the time
- * it was read. We can only rely on this bit being accurate if it
- * is read while the port lock is held. So we'll clear it for now,
- * and reload it later once we have the port lock.
- */
-
- sio_ir = pending & ~(hooks->intr_tx_mt);
- spin_lock_irqsave(&port->ip_lock, flags);
-
- loop_counter = MAXITER; /* to avoid hangs */
-
- do {
- uint32_t shadow;
-
- if (loop_counter-- <= 0) {
- printk(KERN_WARNING "IOC3 serial: "
- "possible hang condition/"
- "port stuck on interrupt (line %d).\n",
- ((struct uart_port *)port->ip_port)->line);
- break;
- }
- /* Handle a DCD change */
- if (sio_ir & hooks->intr_delta_dcd) {
- ioc3_ack(is, idd, hooks->intr_delta_dcd);
- shadow = readl(&port->ip_serial_regs->shadow);
-
- if ((port->ip_notify & N_DDCD)
- && (shadow & SHADOW_DCD)
- && (port->ip_port)) {
- the_port = port->ip_port;
- uart_handle_dcd_change(the_port,
- shadow & SHADOW_DCD);
- wake_up_interruptible
- (&the_port->state->port.delta_msr_wait);
- } else if ((port->ip_notify & N_DDCD)
- && !(shadow & SHADOW_DCD)) {
- /* Flag delta DCD/no DCD */
- uart_handle_dcd_change(port->ip_port,
- shadow & SHADOW_DCD);
- port->ip_flags |= DCD_ON;
- }
- }
-
- /* Handle a CTS change */
- if (sio_ir & hooks->intr_delta_cts) {
- ioc3_ack(is, idd, hooks->intr_delta_cts);
- shadow = readl(&port->ip_serial_regs->shadow);
-
- if ((port->ip_notify & N_DCTS) && (port->ip_port)) {
- the_port = port->ip_port;
- uart_handle_cts_change(the_port, shadow
- & SHADOW_CTS);
- wake_up_interruptible
- (&the_port->state->port.delta_msr_wait);
- }
- }
-
- /* rx timeout interrupt. Must be some data available. Put this
- * before the check for rx_high since servicing this condition
- * may cause that condition to clear.
- */
- if (sio_ir & hooks->intr_rx_timer) {
- ioc3_ack(is, idd, hooks->intr_rx_timer);
- if ((port->ip_notify & N_DATA_READY)
- && (port->ip_port)) {
- receive_chars(port->ip_port);
- }
- }
-
- /* rx high interrupt. Must be after rx_timer. */
- else if (sio_ir & hooks->intr_rx_high) {
- /* Data available, notify upper layer */
- if ((port->ip_notify & N_DATA_READY) && port->ip_port) {
- receive_chars(port->ip_port);
- }
-
- /* We can't ACK this interrupt. If receive_chars didn't
- * cause the condition to clear, we'll have to disable
- * the interrupt until the data is drained.
- * If the read was aborted, don't disable the interrupt
- * as this may cause us to hang indefinitely. An
- * aborted read generally means that this interrupt
- * hasn't been delivered to the cpu yet anyway, even
- * though we see it as asserted when we read the sio_ir.
- */
- if ((sio_ir = PENDING(card_ptr, idd))
- & hooks->intr_rx_high) {
- if (port->ip_flags & READ_ABORTED) {
- rx_high_rd_aborted++;
- }
- else {
- card_ptr->ic_enable &= ~hooks->intr_rx_high;
- port->ip_flags |= INPUT_HIGH;
- }
- }
- }
-
- /* We got a low water interrupt: notify upper layer to
- * send more data. Must come before tx_mt since servicing
- * this condition may cause that condition to clear.
- */
- if (sio_ir & hooks->intr_tx_explicit) {
- port->ip_flags &= ~LOWAT_WRITTEN;
- ioc3_ack(is, idd, hooks->intr_tx_explicit);
- if (port->ip_notify & N_OUTPUT_LOWAT)
- ioc3_cb_output_lowat(port);
- }
-
- /* Handle tx_mt. Must come after tx_explicit. */
- else if (sio_ir & hooks->intr_tx_mt) {
- /* If we are expecting a lowat notification
- * and we get to this point it probably means that for
- * some reason the tx_explicit didn't work as expected
- * (that can legitimately happen if the output buffer is
- * filled up in just the right way).
- * So send the notification now.
- */
- if (port->ip_notify & N_OUTPUT_LOWAT) {
- ioc3_cb_output_lowat(port);
-
- /* We need to reload the sio_ir since the lowat
- * call may have caused another write to occur,
- * clearing the tx_mt condition.
- */
- sio_ir = PENDING(card_ptr, idd);
- }
-
- /* If the tx_mt condition still persists even after the
- * lowat call, we've got some work to do.
- */
- if (sio_ir & hooks->intr_tx_mt) {
- /* If we are not currently expecting DMA input,
- * and the transmitter has just gone idle,
- * there is no longer any reason for DMA, so
- * disable it.
- */
- if (!(port->ip_notify
- & (N_DATA_READY | N_DDCD))) {
- BUG_ON(!(port->ip_sscr
- & SSCR_DMA_EN));
- port->ip_sscr &= ~SSCR_DMA_EN;
- writel(port->ip_sscr,
- &port->ip_serial_regs->sscr);
- }
- /* Prevent infinite tx_mt interrupt */
- card_ptr->ic_enable &= ~hooks->intr_tx_mt;
- }
- }
- sio_ir = PENDING(card_ptr, idd);
-
- /* if the read was aborted and only hooks->intr_rx_high,
- * clear hooks->intr_rx_high, so we do not loop forever.
- */
-
- if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) {
- sio_ir &= ~hooks->intr_rx_high;
- }
- } while (sio_ir & hooks->intr_all);
-
- spin_unlock_irqrestore(&port->ip_lock, flags);
- ioc3_enable(is, idd, card_ptr->ic_enable);
- return 0;
-}
-
-/**
- * ioc3uart_intr - field all serial interrupts
- * @is : submodule
- * @idd: driver data
- * @pending: interrupts to handle
- *
- */
-
-static int ioc3uart_intr(struct ioc3_submodule *is,
- struct ioc3_driver_data *idd,
- unsigned int pending)
-{
- int ret = 0;
-
- /*
- * The upper level interrupt handler sends interrupts for both ports
- * here. So we need to call for each port with its interrupts.
- */
-
- if (pending & SIO_IR_SA)
- ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA);
- if (pending & SIO_IR_SB)
- ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB);
-
- return ret;
-}
-
-/**
- * ic3_type
- * @port: Port to operate with (we ignore since we only have one port)
- *
- */
-static const char *ic3_type(struct uart_port *the_port)
-{
- if (IS_RS232(the_port->line))
- return "SGI IOC3 Serial [rs232]";
- else
- return "SGI IOC3 Serial [rs422]";
-}
-
-/**
- * ic3_tx_empty - Is the transmitter empty?
- * @port: Port to operate on
- *
- */
-static unsigned int ic3_tx_empty(struct uart_port *the_port)
-{
- unsigned int ret = 0;
- struct ioc3_port *port = get_ioc3_port(the_port);
-
- if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT)
- ret = TIOCSER_TEMT;
- return ret;
-}
-
-/**
- * ic3_stop_tx - stop the transmitter
- * @port: Port to operate on
- *
- */
-static void ic3_stop_tx(struct uart_port *the_port)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
-
- if (port)
- set_notification(port, N_OUTPUT_LOWAT, 0);
-}
-
-/**
- * ic3_stop_rx - stop the receiver
- * @port: Port to operate on
- *
- */
-static void ic3_stop_rx(struct uart_port *the_port)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
-
- if (port)
- port->ip_flags &= ~INPUT_ENABLE;
-}
-
-/**
- * null_void_function
- * @port: Port to operate on
- *
- */
-static void null_void_function(struct uart_port *the_port)
-{
-}
-
-/**
- * ic3_shutdown - shut down the port - free irq and disable
- * @port: port to shut down
- *
- */
-static void ic3_shutdown(struct uart_port *the_port)
-{
- unsigned long port_flags;
- struct ioc3_port *port;
- struct uart_state *state;
-
- port = get_ioc3_port(the_port);
- if (!port)
- return;
-
- state = the_port->state;
- wake_up_interruptible(&state->port.delta_msr_wait);
-
- spin_lock_irqsave(&the_port->lock, port_flags);
- set_notification(port, N_ALL, 0);
- spin_unlock_irqrestore(&the_port->lock, port_flags);
-}
-
-/**
- * ic3_set_mctrl - set control lines (dtr, rts, etc)
- * @port: Port to operate on
- * @mctrl: Lines to set/unset
- *
- */
-static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
-{
- unsigned char mcr = 0;
-
- if (mctrl & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
- if (mctrl & TIOCM_DTR)
- mcr |= UART_MCR_DTR;
- if (mctrl & TIOCM_OUT1)
- mcr |= UART_MCR_OUT1;
- if (mctrl & TIOCM_OUT2)
- mcr |= UART_MCR_OUT2;
- if (mctrl & TIOCM_LOOP)
- mcr |= UART_MCR_LOOP;
-
- set_mcr(the_port, mcr, SHADOW_DTR);
-}
-
-/**
- * ic3_get_mctrl - get control line info
- * @port: port to operate on
- *
- */
-static unsigned int ic3_get_mctrl(struct uart_port *the_port)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
- uint32_t shadow;
- unsigned int ret = 0;
-
- if (!port)
- return 0;
-
- shadow = readl(&port->ip_serial_regs->shadow);
- if (shadow & SHADOW_DCD)
- ret |= TIOCM_CD;
- if (shadow & SHADOW_DR)
- ret |= TIOCM_DSR;
- if (shadow & SHADOW_CTS)
- ret |= TIOCM_CTS;
- return ret;
-}
-
-/**
- * ic3_start_tx - Start transmitter. Called with the_port->lock
- * @port: Port to operate on
- *
- */
-static void ic3_start_tx(struct uart_port *the_port)
-{
- struct ioc3_port *port = get_ioc3_port(the_port);
-
- if (port) {
- set_notification(port, N_OUTPUT_LOWAT, 1);
- enable_intrs(port, port->ip_hooks->intr_tx_mt);
- }
-}
-
-/**
- * ic3_break_ctl - handle breaks
- * @port: Port to operate on
- * @break_state: Break state
- *
- */
-static void ic3_break_ctl(struct uart_port *the_port, int break_state)
-{
-}
-
-/**
- * ic3_startup - Start up the serial port - always return 0 (We're always on)
- * @port: Port to operate on
- *
- */
-static int ic3_startup(struct uart_port *the_port)
-{
- int retval;
- struct ioc3_port *port;
- struct ioc3_card *card_ptr;
- unsigned long port_flags;
-
- if (!the_port) {
- NOT_PROGRESS();
- return -ENODEV;
- }
- port = get_ioc3_port(the_port);
- if (!port) {
- NOT_PROGRESS();
- return -ENODEV;
- }
- card_ptr = port->ip_card;
- port->ip_port = the_port;
-
- if (!card_ptr) {
- NOT_PROGRESS();
- return -ENODEV;
- }
-
- /* Start up the serial port */
- spin_lock_irqsave(&the_port->lock, port_flags);
- retval = ic3_startup_local(the_port);
- spin_unlock_irqrestore(&the_port->lock, port_flags);
- return retval;
-}
-
-/**
- * ic3_set_termios - set termios stuff
- * @port: port to operate on
- * @termios: New settings
- * @termios: Old
- *
- */
-static void
-ic3_set_termios(struct uart_port *the_port,
- struct ktermios *termios, struct ktermios *old_termios)
-{
- unsigned long port_flags;
-
- spin_lock_irqsave(&the_port->lock, port_flags);
- ioc3_change_speed(the_port, termios, old_termios);
- spin_unlock_irqrestore(&the_port->lock, port_flags);
-}
-
-/**
- * ic3_request_port - allocate resources for port - no op....
- * @port: port to operate on
- *
- */
-static int ic3_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-/* Associate the uart functions above - given to serial core */
-static const struct uart_ops ioc3_ops = {
- .tx_empty = ic3_tx_empty,
- .set_mctrl = ic3_set_mctrl,
- .get_mctrl = ic3_get_mctrl,
- .stop_tx = ic3_stop_tx,
- .start_tx = ic3_start_tx,
- .stop_rx = ic3_stop_rx,
- .break_ctl = ic3_break_ctl,
- .startup = ic3_startup,
- .shutdown = ic3_shutdown,
- .set_termios = ic3_set_termios,
- .type = ic3_type,
- .release_port = null_void_function,
- .request_port = ic3_request_port,
-};
-
-/*
- * Boot-time initialization code
- */
-
-static struct uart_driver ioc3_uart = {
- .owner = THIS_MODULE,
- .driver_name = "ioc3_serial",
- .dev_name = DEVICE_NAME,
- .major = DEVICE_MAJOR,
- .minor = DEVICE_MINOR,
- .nr = MAX_LOGICAL_PORTS
-};
-
-/**
- * ioc3_serial_core_attach - register with serial core
- * This is done during pci probing
- * @is: submodule struct for this
- * @idd: handle for this card
- */
-static inline int ioc3_serial_core_attach( struct ioc3_submodule *is,
- struct ioc3_driver_data *idd)
-{
- struct ioc3_port *port;
- struct uart_port *the_port;
- struct ioc3_card *card_ptr = idd->data[is->id];
- int ii, phys_port;
- struct pci_dev *pdev = idd->pdev;
-
- DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n",
- __func__, pdev, (void *)card_ptr));
-
- if (!card_ptr)
- return -ENODEV;
-
- /* once around for each logical port on this card */
- for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) {
- phys_port = GET_PHYSICAL_PORT(ii);
- the_port = &card_ptr->ic_port[phys_port].
- icp_uart_port[GET_LOGICAL_PORT(ii)];
- port = card_ptr->ic_port[phys_port].icp_port;
- port->ip_port = the_port;
-
- DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n",
- __func__, (void *)the_port, (void *)port,
- phys_port, ii));
-
- /* membase, iobase and mapbase just need to be non-0 */
- the_port->membase = (unsigned char __iomem *)1;
- the_port->iobase = (pdev->bus->number << 16) | ii;
- the_port->line = (Num_of_ioc3_cards << 2) | ii;
- the_port->mapbase = 1;
- the_port->type = PORT_16550A;
- the_port->fifosize = FIFO_SIZE;
- the_port->ops = &ioc3_ops;
- the_port->irq = idd->irq_io;
- the_port->dev = &pdev->dev;
-
- if (uart_add_one_port(&ioc3_uart, the_port) < 0) {
- printk(KERN_WARNING
- "%s: unable to add port %d bus %d\n",
- __func__, the_port->line, pdev->bus->number);
- } else {
- DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n",
- the_port->line, the_port->irq, pdev->bus->number));
- }
-
- /* all ports are rs232 for now */
- if (IS_PHYSICAL_PORT(ii))
- ioc3_set_proto(port, PROTO_RS232);
- }
- return 0;
-}
-
-/**
- * ioc3uart_remove - register detach function
- * @is: submodule struct for this submodule
- * @idd: ioc3 driver data for this submodule
- */
-
-static int ioc3uart_remove(struct ioc3_submodule *is,
- struct ioc3_driver_data *idd)
-{
- struct ioc3_card *card_ptr = idd->data[is->id];
- struct uart_port *the_port;
- struct ioc3_port *port;
- int ii;
-
- if (card_ptr) {
- for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) {
- the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].
- icp_uart_port[GET_LOGICAL_PORT(ii)];
- if (the_port)
- uart_remove_one_port(&ioc3_uart, the_port);
- port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port;
- if (port && IS_PHYSICAL_PORT(ii)
- && (GET_PHYSICAL_PORT(ii) == 0)) {
- pci_free_consistent(port->ip_idd->pdev,
- TOTAL_RING_BUF_SIZE,
- (void *)port->ip_cpu_ringbuf,
- port->ip_dma_ringbuf);
- kfree(port);
- card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].
- icp_port = NULL;
- }
- }
- kfree(card_ptr);
- idd->data[is->id] = NULL;
- }
- return 0;
-}
-
-/**
- * ioc3uart_probe - card probe function called from shim driver
- * @is: submodule struct for this submodule
- * @idd: ioc3 driver data for this card
- */
-
-static int
-ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
-{
- struct pci_dev *pdev = idd->pdev;
- struct ioc3_card *card_ptr;
- int ret = 0;
- struct ioc3_port *port;
- struct ioc3_port *ports[PORTS_PER_CARD];
- int phys_port;
- int cnt;
-
- DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd));
-
- card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL);
- if (!card_ptr) {
- printk(KERN_WARNING "ioc3_attach_one"
- ": unable to get memory for the IOC3\n");
- return -ENOMEM;
- }
- idd->data[is->id] = card_ptr;
- Submodule_slot = is->id;
-
- writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) |
- ((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) |
- (0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr);
-
- pci_write_config_dword(pdev, PCI_LAT, 0xff00);
-
- /* Enable serial port mode select generic PIO pins as outputs */
- ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL);
-
- /* Create port structures for each port */
- for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) {
- port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL);
- if (!port) {
- printk(KERN_WARNING
- "IOC3 serial memory not available for port\n");
- ret = -ENOMEM;
- goto out4;
- }
- spin_lock_init(&port->ip_lock);
-
- /* we need to remember the previous ones, to point back to
- * them farther down - setting up the ring buffers.
- */
- ports[phys_port] = port;
-
- /* init to something useful */
- card_ptr->ic_port[phys_port].icp_port = port;
- port->ip_is = is;
- port->ip_idd = idd;
- port->ip_baud = 9600;
- port->ip_card = card_ptr;
- port->ip_hooks = &hooks_array[phys_port];
-
- /* Setup each port */
- if (phys_port == 0) {
- port->ip_serial_regs = &idd->vma->port_a;
- port->ip_uart_regs = &idd->vma->sregs.uarta;
-
- DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p "
- "ip_uart_regs 0x%p\n",
- __func__,
- (void *)port->ip_serial_regs,
- (void *)port->ip_uart_regs));
-
- /* setup ring buffers */
- port->ip_cpu_ringbuf = pci_alloc_consistent(pdev,
- TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf);
-
- BUG_ON(!((((int64_t) port->ip_dma_ringbuf) &
- (TOTAL_RING_BUF_SIZE - 1)) == 0));
- port->ip_inring = RING(port, RX_A);
- port->ip_outring = RING(port, TX_A);
- DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p "
- "ip_dma_ringbuf 0x%p, ip_inring 0x%p "
- "ip_outring 0x%p\n",
- __func__,
- (void *)port->ip_cpu_ringbuf,
- (void *)port->ip_dma_ringbuf,
- (void *)port->ip_inring,
- (void *)port->ip_outring));
- }
- else {
- port->ip_serial_regs = &idd->vma->port_b;
- port->ip_uart_regs = &idd->vma->sregs.uartb;
-
- DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p "
- "ip_uart_regs 0x%p\n",
- __func__,
- (void *)port->ip_serial_regs,
- (void *)port->ip_uart_regs));
-
- /* share the ring buffers */
- port->ip_dma_ringbuf =
- ports[phys_port - 1]->ip_dma_ringbuf;
- port->ip_cpu_ringbuf =
- ports[phys_port - 1]->ip_cpu_ringbuf;
- port->ip_inring = RING(port, RX_B);
- port->ip_outring = RING(port, TX_B);
- DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p "
- "ip_dma_ringbuf 0x%p, ip_inring 0x%p "
- "ip_outring 0x%p\n",
- __func__,
- (void *)port->ip_cpu_ringbuf,
- (void *)port->ip_dma_ringbuf,
- (void *)port->ip_inring,
- (void *)port->ip_outring));
- }
-
- DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p",
- __func__,
- phys_port, (void *)port, (void *)card_ptr));
- DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
- (void *)port->ip_serial_regs,
- (void *)port->ip_uart_regs));
-
- /* Initialize the hardware for IOC3 */
- port_init(port);
-
- DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p "
- "outring 0x%p\n",
- __func__,
- phys_port, (void *)port,
- (void *)port->ip_inring,
- (void *)port->ip_outring));
-
- }
-
- /* register port with the serial core */
-
- ret = ioc3_serial_core_attach(is, idd);
- if (ret)
- goto out4;
-
- Num_of_ioc3_cards++;
-
- return ret;
-
- /* error exits that give back resources */
-out4:
- for (cnt = 0; cnt < phys_port; cnt++)
- kfree(ports[cnt]);
-
- kfree(card_ptr);
- return ret;
-}
-
-static struct ioc3_submodule ioc3uart_ops = {
- .name = "IOC3uart",
- .probe = ioc3uart_probe,
- .remove = ioc3uart_remove,
- /* call .intr for both ports initially */
- .irq_mask = SIO_IR_SA | SIO_IR_SB,
- .intr = ioc3uart_intr,
- .owner = THIS_MODULE,
-};
-
-/**
- * ioc3_detect - module init called,
- */
-static int __init ioc3uart_init(void)
-{
- int ret;
-
- /* register with serial core */
- if ((ret = uart_register_driver(&ioc3_uart)) < 0) {
- printk(KERN_WARNING
- "%s: Couldn't register IOC3 uart serial driver\n",
- __func__);
- return ret;
- }
- ret = ioc3_register_submodule(&ioc3uart_ops);
- if (ret)
- uart_unregister_driver(&ioc3_uart);
- return ret;
-}
-
-static void __exit ioc3uart_exit(void)
-{
- ioc3_unregister_submodule(&ioc3uart_ops);
- uart_unregister_driver(&ioc3_uart);
-}
-
-module_init(ioc3uart_init);
-module_exit(ioc3uart_exit);
-
-MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
-MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c
deleted file mode 100644
index db5b979..0000000
--- a/drivers/tty/serial/ioc4_serial.c
+++ /dev/null
@@ -1,2955 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2003-2006 Silicon Graphics, Inc. All Rights Reserved.
- */
-
-
-/*
- * This file contains a module version of the ioc4 serial driver. This
- * includes all the support functions needed (support functions, etc.)
- * and the serial driver itself.
- */
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/circ_buf.h>
-#include <linux/serial_reg.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/ioc4.h>
-#include <linux/serial_core.h>
-#include <linux/slab.h>
-
-/*
- * interesting things about the ioc4
- */
-
-#define IOC4_NUM_SERIAL_PORTS 4 /* max ports per card */
-#define IOC4_NUM_CARDS 8 /* max cards per partition */
-
-#define GET_SIO_IR(_n) (_n == 0) ? (IOC4_SIO_IR_S0) : \
- (_n == 1) ? (IOC4_SIO_IR_S1) : \
- (_n == 2) ? (IOC4_SIO_IR_S2) : \
- (IOC4_SIO_IR_S3)
-
-#define GET_OTHER_IR(_n) (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \
- (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \
- (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \
- (IOC4_OTHER_IR_S3_MEMERR)
-
-
-/*
- * All IOC4 registers are 32 bits wide.
- */
-
-/*
- * PCI Memory Space Map
- */
-#define IOC4_PCI_ERR_ADDR_L 0x000 /* Low Error Address */
-#define IOC4_PCI_ERR_ADDR_VLD (0x1 << 0)
-#define IOC4_PCI_ERR_ADDR_MST_ID_MSK (0xf << 1)
-#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK (0xe << 1)
-#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK (0x1 << 1)
-#define IOC4_PCI_ERR_ADDR_MUL_ERR (0x1 << 5)
-#define IOC4_PCI_ERR_ADDR_ADDR_MSK (0x3ffffff << 6)
-
-/* Interrupt types */
-#define IOC4_SIO_INTR_TYPE 0
-#define IOC4_OTHER_INTR_TYPE 1
-#define IOC4_NUM_INTR_TYPES 2
-
-/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES */
-#define IOC4_SIO_IR_S0_TX_MT 0x00000001 /* Serial port 0 TX empty */
-#define IOC4_SIO_IR_S0_RX_FULL 0x00000002 /* Port 0 RX buf full */
-#define IOC4_SIO_IR_S0_RX_HIGH 0x00000004 /* Port 0 RX hiwat */
-#define IOC4_SIO_IR_S0_RX_TIMER 0x00000008 /* Port 0 RX timeout */
-#define IOC4_SIO_IR_S0_DELTA_DCD 0x00000010 /* Port 0 delta DCD */
-#define IOC4_SIO_IR_S0_DELTA_CTS 0x00000020 /* Port 0 delta CTS */
-#define IOC4_SIO_IR_S0_INT 0x00000040 /* Port 0 pass-thru intr */
-#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080 /* Port 0 explicit TX thru */
-#define IOC4_SIO_IR_S1_TX_MT 0x00000100 /* Serial port 1 */
-#define IOC4_SIO_IR_S1_RX_FULL 0x00000200 /* */
-#define IOC4_SIO_IR_S1_RX_HIGH 0x00000400 /* */
-#define IOC4_SIO_IR_S1_RX_TIMER 0x00000800 /* */
-#define IOC4_SIO_IR_S1_DELTA_DCD 0x00001000 /* */
-#define IOC4_SIO_IR_S1_DELTA_CTS 0x00002000 /* */
-#define IOC4_SIO_IR_S1_INT 0x00004000 /* */
-#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000 /* */
-#define IOC4_SIO_IR_S2_TX_MT 0x00010000 /* Serial port 2 */
-#define IOC4_SIO_IR_S2_RX_FULL 0x00020000 /* */
-#define IOC4_SIO_IR_S2_RX_HIGH 0x00040000 /* */
-#define IOC4_SIO_IR_S2_RX_TIMER 0x00080000 /* */
-#define IOC4_SIO_IR_S2_DELTA_DCD 0x00100000 /* */
-#define IOC4_SIO_IR_S2_DELTA_CTS 0x00200000 /* */
-#define IOC4_SIO_IR_S2_INT 0x00400000 /* */
-#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000 /* */
-#define IOC4_SIO_IR_S3_TX_MT 0x01000000 /* Serial port 3 */
-#define IOC4_SIO_IR_S3_RX_FULL 0x02000000 /* */
-#define IOC4_SIO_IR_S3_RX_HIGH 0x04000000 /* */
-#define IOC4_SIO_IR_S3_RX_TIMER 0x08000000 /* */
-#define IOC4_SIO_IR_S3_DELTA_DCD 0x10000000 /* */
-#define IOC4_SIO_IR_S3_DELTA_CTS 0x20000000 /* */
-#define IOC4_SIO_IR_S3_INT 0x40000000 /* */
-#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000 /* */
-
-/* Per device interrupt masks */
-#define IOC4_SIO_IR_S0 (IOC4_SIO_IR_S0_TX_MT | \
- IOC4_SIO_IR_S0_RX_FULL | \
- IOC4_SIO_IR_S0_RX_HIGH | \
- IOC4_SIO_IR_S0_RX_TIMER | \
- IOC4_SIO_IR_S0_DELTA_DCD | \
- IOC4_SIO_IR_S0_DELTA_CTS | \
- IOC4_SIO_IR_S0_INT | \
- IOC4_SIO_IR_S0_TX_EXPLICIT)
-#define IOC4_SIO_IR_S1 (IOC4_SIO_IR_S1_TX_MT | \
- IOC4_SIO_IR_S1_RX_FULL | \
- IOC4_SIO_IR_S1_RX_HIGH | \
- IOC4_SIO_IR_S1_RX_TIMER | \
- IOC4_SIO_IR_S1_DELTA_DCD | \
- IOC4_SIO_IR_S1_DELTA_CTS | \
- IOC4_SIO_IR_S1_INT | \
- IOC4_SIO_IR_S1_TX_EXPLICIT)
-#define IOC4_SIO_IR_S2 (IOC4_SIO_IR_S2_TX_MT | \
- IOC4_SIO_IR_S2_RX_FULL | \
- IOC4_SIO_IR_S2_RX_HIGH | \
- IOC4_SIO_IR_S2_RX_TIMER | \
- IOC4_SIO_IR_S2_DELTA_DCD | \
- IOC4_SIO_IR_S2_DELTA_CTS | \
- IOC4_SIO_IR_S2_INT | \
- IOC4_SIO_IR_S2_TX_EXPLICIT)
-#define IOC4_SIO_IR_S3 (IOC4_SIO_IR_S3_TX_MT | \
- IOC4_SIO_IR_S3_RX_FULL | \
- IOC4_SIO_IR_S3_RX_HIGH | \
- IOC4_SIO_IR_S3_RX_TIMER | \
- IOC4_SIO_IR_S3_DELTA_DCD | \
- IOC4_SIO_IR_S3_DELTA_CTS | \
- IOC4_SIO_IR_S3_INT | \
- IOC4_SIO_IR_S3_TX_EXPLICIT)
-
-/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */
-#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */
-#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */
-#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */
-#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */
-#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */
-#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */
-#define IOC4_OTHER_IR_KBD_INT 0x00000040 /* Keyboard/mouse */
-#define IOC4_OTHER_IR_RESERVED 0x007fff80 /* Reserved */
-#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT section output */
-#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* Generic pins */
-
-#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \
- IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR)
-
-/* Bitmasks for IOC4_SIO_CR */
-#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */
-#define IOC4_SIO_CR_ARB_DIAG_TX0 0x00000000
-#define IOC4_SIO_CR_ARB_DIAG_RX0 0x00000010
-#define IOC4_SIO_CR_ARB_DIAG_TX1 0x00000020
-#define IOC4_SIO_CR_ARB_DIAG_RX1 0x00000030
-#define IOC4_SIO_CR_ARB_DIAG_TX2 0x00000040
-#define IOC4_SIO_CR_ARB_DIAG_RX2 0x00000050
-#define IOC4_SIO_CR_ARB_DIAG_TX3 0x00000060
-#define IOC4_SIO_CR_ARB_DIAG_RX3 0x00000070
-#define IOC4_SIO_CR_SIO_DIAG_IDLE 0x00000080 /* 0 -> active request among
- serial ports (ro) */
-/* Defs for some of the generic I/O pins */
-#define IOC4_GPCR_UART0_MODESEL 0x10 /* Pin is output to port 0
- mode sel */
-#define IOC4_GPCR_UART1_MODESEL 0x20 /* Pin is output to port 1
- mode sel */
-#define IOC4_GPCR_UART2_MODESEL 0x40 /* Pin is output to port 2
- mode sel */
-#define IOC4_GPCR_UART3_MODESEL 0x80 /* Pin is output to port 3
- mode sel */
-
-#define IOC4_GPPR_UART0_MODESEL_PIN 4 /* GIO pin controlling
- uart 0 mode select */
-#define IOC4_GPPR_UART1_MODESEL_PIN 5 /* GIO pin controlling
- uart 1 mode select */
-#define IOC4_GPPR_UART2_MODESEL_PIN 6 /* GIO pin controlling
- uart 2 mode select */
-#define IOC4_GPPR_UART3_MODESEL_PIN 7 /* GIO pin controlling
- uart 3 mode select */
-
-/* Bitmasks for serial RX status byte */
-#define IOC4_RXSB_OVERRUN 0x01 /* Char(s) lost */
-#define IOC4_RXSB_PAR_ERR 0x02 /* Parity error */
-#define IOC4_RXSB_FRAME_ERR 0x04 /* Framing error */
-#define IOC4_RXSB_BREAK 0x08 /* Break character */
-#define IOC4_RXSB_CTS 0x10 /* State of CTS */
-#define IOC4_RXSB_DCD 0x20 /* State of DCD */
-#define IOC4_RXSB_MODEM_VALID 0x40 /* DCD, CTS, and OVERRUN are valid */
-#define IOC4_RXSB_DATA_VALID 0x80 /* Data byte, FRAME_ERR PAR_ERR
- * & BREAK valid */
-
-/* Bitmasks for serial TX control byte */
-#define IOC4_TXCB_INT_WHEN_DONE 0x20 /* Interrupt after this byte is sent */
-#define IOC4_TXCB_INVALID 0x00 /* Byte is invalid */
-#define IOC4_TXCB_VALID 0x40 /* Byte is valid */
-#define IOC4_TXCB_MCR 0x80 /* Data<7:0> to modem control reg */
-#define IOC4_TXCB_DELAY 0xc0 /* Delay data<7:0> mSec */
-
-/* Bitmasks for IOC4_SBBR_L */
-#define IOC4_SBBR_L_SIZE 0x00000001 /* 0 == 1KB rings, 1 == 4KB rings */
-
-/* Bitmasks for IOC4_SSCR_<3:0> */
-#define IOC4_SSCR_RX_THRESHOLD 0x000001ff /* Hiwater mark */
-#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */
-#define IOC4_SSCR_HFC_EN 0x00020000 /* Hardware flow control enabled */
-#define IOC4_SSCR_RX_RING_DCD 0x00040000 /* Post RX record on delta-DCD */
-#define IOC4_SSCR_RX_RING_CTS 0x00080000 /* Post RX record on delta-CTS */
-#define IOC4_SSCR_DIAG 0x00200000 /* Bypass clock divider for sim */
-#define IOC4_SSCR_RX_DRAIN 0x08000000 /* Drain RX buffer to memory */
-#define IOC4_SSCR_DMA_EN 0x10000000 /* Enable ring buffer DMA */
-#define IOC4_SSCR_DMA_PAUSE 0x20000000 /* Pause DMA */
-#define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */
-#define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */
-
-/* All producer/consumer pointers are the same bitfield */
-#define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */
-#define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */
-#define IOC4_PROD_CONS_PTR_OFF 3
-
-/* Bitmasks for IOC4_SRCIR_<3:0> */
-#define IOC4_SRCIR_ARM 0x80000000 /* Arm RX timer */
-
-/* Bitmasks for IOC4_SHADOW_<3:0> */
-#define IOC4_SHADOW_DR 0x00000001 /* Data ready */
-#define IOC4_SHADOW_OE 0x00000002 /* Overrun error */
-#define IOC4_SHADOW_PE 0x00000004 /* Parity error */
-#define IOC4_SHADOW_FE 0x00000008 /* Framing error */
-#define IOC4_SHADOW_BI 0x00000010 /* Break interrupt */
-#define IOC4_SHADOW_THRE 0x00000020 /* Xmit holding register empty */
-#define IOC4_SHADOW_TEMT 0x00000040 /* Xmit shift register empty */
-#define IOC4_SHADOW_RFCE 0x00000080 /* Char in RX fifo has an error */
-#define IOC4_SHADOW_DCTS 0x00010000 /* Delta clear to send */
-#define IOC4_SHADOW_DDCD 0x00080000 /* Delta data carrier detect */
-#define IOC4_SHADOW_CTS 0x00100000 /* Clear to send */
-#define IOC4_SHADOW_DCD 0x00800000 /* Data carrier detect */
-#define IOC4_SHADOW_DTR 0x01000000 /* Data terminal ready */
-#define IOC4_SHADOW_RTS 0x02000000 /* Request to send */
-#define IOC4_SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */
-#define IOC4_SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */
-#define IOC4_SHADOW_LOOP 0x10000000 /* Loopback enabled */
-
-/* Bitmasks for IOC4_SRTR_<3:0> */
-#define IOC4_SRTR_CNT 0x00000fff /* Reload value for RX timer */
-#define IOC4_SRTR_CNT_VAL 0x0fff0000 /* Current value of RX timer */
-#define IOC4_SRTR_CNT_VAL_SHIFT 16
-#define IOC4_SRTR_HZ 16000 /* SRTR clock frequency */
-
-/* Serial port register map used for DMA and PIO serial I/O */
-struct ioc4_serialregs {
- uint32_t sscr;
- uint32_t stpir;
- uint32_t stcir;
- uint32_t srpir;
- uint32_t srcir;
- uint32_t srtr;
- uint32_t shadow;
-};
-
-/* IOC4 UART register map */
-struct ioc4_uartregs {
- char i4u_lcr;
- union {
- char iir; /* read only */
- char fcr; /* write only */
- } u3;
- union {
- char ier; /* DLAB == 0 */
- char dlm; /* DLAB == 1 */
- } u2;
- union {
- char rbr; /* read only, DLAB == 0 */
- char thr; /* write only, DLAB == 0 */
- char dll; /* DLAB == 1 */
- } u1;
- char i4u_scr;
- char i4u_msr;
- char i4u_lsr;
- char i4u_mcr;
-};
-
-/* short names */
-#define i4u_dll u1.dll
-#define i4u_ier u2.ier
-#define i4u_dlm u2.dlm
-#define i4u_fcr u3.fcr
-
-/* Serial port registers used for DMA serial I/O */
-struct ioc4_serial {
- uint32_t sbbr01_l;
- uint32_t sbbr01_h;
- uint32_t sbbr23_l;
- uint32_t sbbr23_h;
-
- struct ioc4_serialregs port_0;
- struct ioc4_serialregs port_1;
- struct ioc4_serialregs port_2;
- struct ioc4_serialregs port_3;
- struct ioc4_uartregs uart_0;
- struct ioc4_uartregs uart_1;
- struct ioc4_uartregs uart_2;
- struct ioc4_uartregs uart_3;
-};
-
-/* UART clock speed */
-#define IOC4_SER_XIN_CLK_66 66666667
-#define IOC4_SER_XIN_CLK_33 33333333
-
-#define IOC4_W_IES 0
-#define IOC4_W_IEC 1
-
-typedef void ioc4_intr_func_f(void *, uint32_t);
-typedef ioc4_intr_func_f *ioc4_intr_func_t;
-
-static unsigned int Num_of_ioc4_cards;
-
-/* defining this will get you LOTS of great debug info */
-//#define DEBUG_INTERRUPTS
-#define DPRINT_CONFIG(_x...) ;
-//#define DPRINT_CONFIG(_x...) printk _x
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/* number of characters we want to transmit to the lower level at a time */
-#define IOC4_MAX_CHARS 256
-#define IOC4_FIFO_CHARS 255
-
-/* Device name we're using */
-#define DEVICE_NAME_RS232 "ttyIOC"
-#define DEVICE_NAME_RS422 "ttyAIOC"
-#define DEVICE_MAJOR 204
-#define DEVICE_MINOR_RS232 50
-#define DEVICE_MINOR_RS422 84
-
-
-/* register offsets */
-#define IOC4_SERIAL_OFFSET 0x300
-
-/* flags for next_char_state */
-#define NCS_BREAK 0x1
-#define NCS_PARITY 0x2
-#define NCS_FRAMING 0x4
-#define NCS_OVERRUN 0x8
-
-/* cause we need SOME parameters ... */
-#define MIN_BAUD_SUPPORTED 1200
-#define MAX_BAUD_SUPPORTED 115200
-
-/* protocol types supported */
-#define PROTO_RS232 3
-#define PROTO_RS422 7
-
-/* Notification types */
-#define N_DATA_READY 0x01
-#define N_OUTPUT_LOWAT 0x02
-#define N_BREAK 0x04
-#define N_PARITY_ERROR 0x08
-#define N_FRAMING_ERROR 0x10
-#define N_OVERRUN_ERROR 0x20
-#define N_DDCD 0x40
-#define N_DCTS 0x80
-
-#define N_ALL_INPUT (N_DATA_READY | N_BREAK | \
- N_PARITY_ERROR | N_FRAMING_ERROR | \
- N_OVERRUN_ERROR | N_DDCD | N_DCTS)
-
-#define N_ALL_OUTPUT N_OUTPUT_LOWAT
-
-#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR)
-
-#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK | \
- N_PARITY_ERROR | N_FRAMING_ERROR | \
- N_OVERRUN_ERROR | N_DDCD | N_DCTS)
-
-#define SER_DIVISOR(_x, clk) (((clk) + (_x) * 8) / ((_x) * 16))
-#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div))
-
-/* Some masks */
-#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \
- | UART_LCR_WLEN7 | UART_LCR_WLEN8)
-#define LCR_MASK_STOP_BITS (UART_LCR_STOP)
-
-#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb)
-#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw)
-
-/* Default to 4k buffers */
-#ifdef IOC4_1K_BUFFERS
-#define RING_BUF_SIZE 1024
-#define IOC4_BUF_SIZE_BIT 0
-#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K
-#else
-#define RING_BUF_SIZE 4096
-#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE
-#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K
-#endif
-
-#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)
-
-/*
- * This is the entry saved by the driver - one per card
- */
-
-#define UART_PORT_MIN 0
-#define UART_PORT_RS232 UART_PORT_MIN
-#define UART_PORT_RS422 1
-#define UART_PORT_COUNT 2 /* one for each mode */
-
-struct ioc4_control {
- int ic_irq;
- struct {
- /* uart ports are allocated here - 1 for rs232, 1 for rs422 */
- struct uart_port icp_uart_port[UART_PORT_COUNT];
- /* Handy reference material */
- struct ioc4_port *icp_port;
- } ic_port[IOC4_NUM_SERIAL_PORTS];
- struct ioc4_soft *ic_soft;
-};
-
-/*
- * per-IOC4 data structure
- */
-#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t))
-struct ioc4_soft {
- struct ioc4_misc_regs __iomem *is_ioc4_misc_addr;
- struct ioc4_serial __iomem *is_ioc4_serial_addr;
-
- /* Each interrupt type has an entry in the array */
- struct ioc4_intr_type {
-
- /*
- * Each in-use entry in this array contains at least
- * one nonzero bit in sd_bits; no two entries in this
- * array have overlapping sd_bits values.
- */
- struct ioc4_intr_info {
- uint32_t sd_bits;
- ioc4_intr_func_f *sd_intr;
- void *sd_info;
- } is_intr_info[MAX_IOC4_INTR_ENTS];
-
- /* Number of entries active in the above array */
- atomic_t is_num_intrs;
- } is_intr_type[IOC4_NUM_INTR_TYPES];
-
- /* is_ir_lock must be held while
- * modifying sio_ie values, so
- * we can be sure that sio_ie is
- * not changing when we read it
- * along with sio_ir.
- */
- spinlock_t is_ir_lock; /* SIO_IE[SC] mod lock */
-};
-
-/* Local port info for each IOC4 serial ports */
-struct ioc4_port {
- struct uart_port *ip_port; /* current active port ptr */
- /* Ptrs for all ports */
- struct uart_port *ip_all_ports[UART_PORT_COUNT];
- /* Back ptrs for this port */
- struct ioc4_control *ip_control;
- struct pci_dev *ip_pdev;
- struct ioc4_soft *ip_ioc4_soft;
-
- /* pci mem addresses */
- struct ioc4_misc_regs __iomem *ip_mem;
- struct ioc4_serial __iomem *ip_serial;
- struct ioc4_serialregs __iomem *ip_serial_regs;
- struct ioc4_uartregs __iomem *ip_uart_regs;
-
- /* Ring buffer page for this port */
- dma_addr_t ip_dma_ringbuf;
- /* vaddr of ring buffer */
- struct ring_buffer *ip_cpu_ringbuf;
-
- /* Rings for this port */
- struct ring *ip_inring;
- struct ring *ip_outring;
-
- /* Hook to port specific values */
- struct hooks *ip_hooks;
-
- spinlock_t ip_lock;
-
- /* Various rx/tx parameters */
- int ip_baud;
- int ip_tx_lowat;
- int ip_rx_timeout;
-
- /* Copy of notification bits */
- int ip_notify;
-
- /* Shadow copies of various registers so we don't need to PIO
- * read them constantly
- */
- uint32_t ip_ienb; /* Enabled interrupts */
- uint32_t ip_sscr;
- uint32_t ip_tx_prod;
- uint32_t ip_rx_cons;
- int ip_pci_bus_speed;
- unsigned char ip_flags;
-};
-
-/* tx low water mark. We need to notify the driver whenever tx is getting
- * close to empty so it can refill the tx buffer and keep things going.
- * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll
- * have no trouble getting in more chars in time (I certainly hope so).
- */
-#define TX_LOWAT_LATENCY 1000
-#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY)
-#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)
-
-/* Flags per port */
-#define INPUT_HIGH 0x01
-#define DCD_ON 0x02
-#define LOWAT_WRITTEN 0x04
-#define READ_ABORTED 0x08
-#define PORT_ACTIVE 0x10
-#define PORT_INACTIVE 0 /* This is the value when "off" */
-
-
-/* Since each port has different register offsets and bitmasks
- * for everything, we'll store those that we need in tables so we
- * don't have to be constantly checking the port we are dealing with.
- */
-struct hooks {
- uint32_t intr_delta_dcd;
- uint32_t intr_delta_cts;
- uint32_t intr_tx_mt;
- uint32_t intr_rx_timer;
- uint32_t intr_rx_high;
- uint32_t intr_tx_explicit;
- uint32_t intr_dma_error;
- uint32_t intr_clear;
- uint32_t intr_all;
- int rs422_select_pin;
-};
-
-static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {
- /* Values for port 0 */
- {
- IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS,
- IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER,
- IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT,
- IOC4_OTHER_IR_S0_MEMERR,
- (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL |
- IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER |
- IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS |
- IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT),
- IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN,
- },
-
- /* Values for port 1 */
- {
- IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS,
- IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER,
- IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT,
- IOC4_OTHER_IR_S1_MEMERR,
- (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL |
- IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER |
- IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS |
- IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT),
- IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN,
- },
-
- /* Values for port 2 */
- {
- IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS,
- IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER,
- IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT,
- IOC4_OTHER_IR_S2_MEMERR,
- (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL |
- IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER |
- IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS |
- IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT),
- IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN,
- },
-
- /* Values for port 3 */
- {
- IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS,
- IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER,
- IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT,
- IOC4_OTHER_IR_S3_MEMERR,
- (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL |
- IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER |
- IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS |
- IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT),
- IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN,
- }
-};
-
-/* A ring buffer entry */
-struct ring_entry {
- union {
- struct {
- uint32_t alldata;
- uint32_t allsc;
- } all;
- struct {
- char data[4]; /* data bytes */
- char sc[4]; /* status/control */
- } s;
- } u;
-};
-
-/* Test the valid bits in any of the 4 sc chars using "allsc" member */
-#define RING_ANY_VALID \
- ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101)
-
-#define ring_sc u.s.sc
-#define ring_data u.s.data
-#define ring_allsc u.all.allsc
-
-/* Number of entries per ring buffer. */
-#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))
-
-/* An individual ring */
-struct ring {
- struct ring_entry entries[ENTRIES_PER_RING];
-};
-
-/* The whole enchilada */
-struct ring_buffer {
- struct ring TX_0_OR_2;
- struct ring RX_0_OR_2;
- struct ring TX_1_OR_3;
- struct ring RX_1_OR_3;
-};
-
-/* Get a ring from a port struct */
-#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)
-
-/* Infinite loop detection.
- */
-#define MAXITER 10000000
-
-/* Prototypes */
-static void receive_chars(struct uart_port *);
-static void handle_intr(void *arg, uint32_t sio_ir);
-
-/*
- * port_is_active - determines if this port is currently active
- * @port: ptr to soft struct for this port
- * @uart_port: uart port to test for
- */
-static inline int port_is_active(struct ioc4_port *port,
- struct uart_port *uart_port)
-{
- if (port) {
- if ((port->ip_flags & PORT_ACTIVE)
- && (port->ip_port == uart_port))
- return 1;
- }
- return 0;
-}
-
-
-/**
- * write_ireg - write the interrupt regs
- * @ioc4_soft: ptr to soft struct for this port
- * @val: value to write
- * @which: which register
- * @type: which ireg set
- */
-static inline void
-write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
-{
- struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);
-
- switch (type) {
- case IOC4_SIO_INTR_TYPE:
- switch (which) {
- case IOC4_W_IES:
- writel(val, &mem->sio_ies.raw);
- break;
-
- case IOC4_W_IEC:
- writel(val, &mem->sio_iec.raw);
- break;
- }
- break;
-
- case IOC4_OTHER_INTR_TYPE:
- switch (which) {
- case IOC4_W_IES:
- writel(val, &mem->other_ies.raw);
- break;
-
- case IOC4_W_IEC:
- writel(val, &mem->other_iec.raw);
- break;
- }
- break;
-
- default:
- break;
- }
- spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags);
-}
-
-/**
- * set_baud - Baud rate setting code
- * @port: port to set
- * @baud: baud rate to use
- */
-static int set_baud(struct ioc4_port *port, int baud)
-{
- int actual_baud;
- int diff;
- int lcr;
- unsigned short divisor;
- struct ioc4_uartregs __iomem *uart;
-
- divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed);
- if (!divisor)
- return 1;
- actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed);
-
- diff = actual_baud - baud;
- if (diff < 0)
- diff = -diff;
-
- /* If we're within 1%, we've found a match */
- if (diff * 100 > actual_baud)
- return 1;
-
- uart = port->ip_uart_regs;
- lcr = readb(&uart->i4u_lcr);
- writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr);
- writeb((unsigned char)divisor, &uart->i4u_dll);
- writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm);
- writeb(lcr, &uart->i4u_lcr);
- return 0;
-}
-
-
-/**
- * get_ioc4_port - given a uart port, return the control structure
- * @port: uart port
- * @set: set this port as current
- */
-static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set)
-{
- struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev);
- struct ioc4_control *control = idd->idd_serial_data;
- struct ioc4_port *port;
- int port_num, port_type;
-
- if (control) {
- for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS;
- port_num++ ) {
- port = control->ic_port[port_num].icp_port;
- if (!port)
- continue;
- for (port_type = UART_PORT_MIN;
- port_type < UART_PORT_COUNT;
- port_type++) {
- if (the_port == port->ip_all_ports
- [port_type]) {
- /* set local copy */
- if (set) {
- port->ip_port = the_port;
- }
- return port;
- }
- }
- }
- }
- return NULL;
-}
-
-/* The IOC4 hardware provides no atomic way to determine if interrupts
- * are pending since two reads are required to do so. The handler must
- * read the SIO_IR and the SIO_IES, and take the logical and of the
- * two. When this value is zero, all interrupts have been serviced and
- * the handler may return.
- *
- * This has the unfortunate "hole" that, if some other CPU or
- * some other thread or some higher level interrupt manages to
- * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may
- * think we have observed SIO_IR&SIO_IE==0 when in fact this
- * condition never really occurred.
- *
- * To solve this, we use a simple spinlock that must be held
- * whenever modifying SIO_IE; holding this lock while observing
- * both SIO_IR and SIO_IE guarantees that we do not falsely
- * conclude that no enabled interrupts are pending.
- */
-
-static inline uint32_t
-pending_intrs(struct ioc4_soft *soft, int type)
-{
- struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
- unsigned long flag;
- uint32_t intrs = 0;
-
- BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
- || (type == IOC4_OTHER_INTR_TYPE)));
-
- spin_lock_irqsave(&soft->is_ir_lock, flag);
-
- switch (type) {
- case IOC4_SIO_INTR_TYPE:
- intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw);
- break;
-
- case IOC4_OTHER_INTR_TYPE:
- intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw);
-
- /* Don't process any ATA interrupte */
- intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
- break;
-
- default:
- break;
- }
- spin_unlock_irqrestore(&soft->is_ir_lock, flag);
- return intrs;
-}
-
-/**
- * port_init - Initialize the sio and ioc4 hardware for a given port
- * called per port from attach...
- * @port: port to initialize
- */
-static inline int port_init(struct ioc4_port *port)
-{
- uint32_t sio_cr;
- struct hooks *hooks = port->ip_hooks;
- struct ioc4_uartregs __iomem *uart;
-
- /* Idle the IOC4 serial interface */
- writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr);
-
- /* Wait until any pending bus activity for this port has ceased */
- do
- sio_cr = readl(&port->ip_mem->sio_cr.raw);
- while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));
-
- /* Finish reset sequence */
- writel(0, &port->ip_serial_regs->sscr);
-
- /* Once RESET is done, reload cached tx_prod and rx_cons values
- * and set rings to empty by making prod == cons
- */
- port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
- writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
- port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
- writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
-
- /* Disable interrupts for this 16550 */
- uart = port->ip_uart_regs;
- writeb(0, &uart->i4u_lcr);
- writeb(0, &uart->i4u_ier);
-
- /* Set the default baud */
- set_baud(port, port->ip_baud);
-
- /* Set line control to 8 bits no parity */
- writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr);
- /* UART_LCR_STOP == 1 stop */
-
- /* Enable the FIFOs */
- writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr);
- /* then reset 16550 FIFOs */
- writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
- &uart->i4u_fcr);
-
- /* Clear modem control register */
- writeb(0, &uart->i4u_mcr);
-
- /* Clear deltas in modem status register */
- readb(&uart->i4u_msr);
-
- /* Only do this once per port pair */
- if (port->ip_hooks == &hooks_array[0]
- || port->ip_hooks == &hooks_array[2]) {
- unsigned long ring_pci_addr;
- uint32_t __iomem *sbbr_l;
- uint32_t __iomem *sbbr_h;
-
- if (port->ip_hooks == &hooks_array[0]) {
- sbbr_l = &port->ip_serial->sbbr01_l;
- sbbr_h = &port->ip_serial->sbbr01_h;
- } else {
- sbbr_l = &port->ip_serial->sbbr23_l;
- sbbr_h = &port->ip_serial->sbbr23_h;
- }
-
- ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
- DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n",
- __func__, ring_pci_addr));
-
- writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h);
- writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l);
- }
-
- /* Set the receive timeout value to 10 msec */
- writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr);
-
- /* Set rx threshold, enable DMA */
- /* Set high water mark at 3/4 of full ring */
- port->ip_sscr = (ENTRIES_PER_RING * 3 / 4);
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Disable and clear all serial related interrupt bits */
- write_ireg(port->ip_ioc4_soft, hooks->intr_clear,
- IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
- port->ip_ienb &= ~hooks->intr_clear;
- writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw);
- return 0;
-}
-
-/**
- * handle_dma_error_intr - service any pending DMA error interrupts for the
- * given port - 2nd level called via sd_intr
- * @arg: handler arg
- * @other_ir: ioc4regs
- */
-static void handle_dma_error_intr(void *arg, uint32_t other_ir)
-{
- struct ioc4_port *port = (struct ioc4_port *)arg;
- struct hooks *hooks = port->ip_hooks;
- unsigned long flags;
-
- spin_lock_irqsave(&port->ip_lock, flags);
-
- /* ACK the interrupt */
- writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw);
-
- if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) {
- printk(KERN_ERR
- "PCI error address is 0x%llx, "
- "master is serial port %c %s\n",
- (((uint64_t)readl(&port->ip_mem->pci_err_addr_h)
- << 32)
- | readl(&port->ip_mem->pci_err_addr_l.raw))
- & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' +
- ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) &
- IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1),
- (readl(&port->ip_mem->pci_err_addr_l.raw)
- & IOC4_PCI_ERR_ADDR_MST_TYP_MSK)
- ? "RX" : "TX");
-
- if (readl(&port->ip_mem->pci_err_addr_l.raw)
- & IOC4_PCI_ERR_ADDR_MUL_ERR) {
- printk(KERN_ERR
- "Multiple errors occurred\n");
- }
- }
- spin_unlock_irqrestore(&port->ip_lock, flags);
-
- /* Re-enable DMA error interrupts */
- write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES,
- IOC4_OTHER_INTR_TYPE);
-}
-
-/**
- * intr_connect - interrupt connect function
- * @soft: soft struct for this card
- * @type: interrupt type
- * @intrbits: bit pattern to set
- * @intr: handler function
- * @info: handler arg
- */
-static void
-intr_connect(struct ioc4_soft *soft, int type,
- uint32_t intrbits, ioc4_intr_func_f * intr, void *info)
-{
- int i;
- struct ioc4_intr_info *intr_ptr;
-
- BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
- || (type == IOC4_OTHER_INTR_TYPE)));
-
- i = atomic_inc_return(&soft-> is_intr_type[type].is_num_intrs) - 1;
- BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)));
-
- /* Save off the lower level interrupt handler */
- intr_ptr = &soft->is_intr_type[type].is_intr_info[i];
- intr_ptr->sd_bits = intrbits;
- intr_ptr->sd_intr = intr;
- intr_ptr->sd_info = info;
-}
-
-/**
- * ioc4_intr - Top level IOC4 interrupt handler.
- * @irq: irq value
- * @arg: handler arg
- */
-
-static irqreturn_t ioc4_intr(int irq, void *arg)
-{
- struct ioc4_soft *soft;
- uint32_t this_ir, this_mir;
- int xx, num_intrs = 0;
- int intr_type;
- int handled = 0;
- struct ioc4_intr_info *intr_info;
-
- soft = arg;
- for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) {
- num_intrs = (int)atomic_read(
- &soft->is_intr_type[intr_type].is_num_intrs);
-
- this_mir = this_ir = pending_intrs(soft, intr_type);
-
- /* Farm out the interrupt to the various drivers depending on
- * which interrupt bits are set.
- */
- for (xx = 0; xx < num_intrs; xx++) {
- intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx];
- this_mir = this_ir & intr_info->sd_bits;
- if (this_mir) {
- /* Disable owned interrupts, call handler */
- handled++;
- write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC,
- intr_type);
- intr_info->sd_intr(intr_info->sd_info, this_mir);
- this_ir &= ~this_mir;
- }
- }
- }
-#ifdef DEBUG_INTERRUPTS
- {
- struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
- unsigned long flag;
-
- spin_lock_irqsave(&soft->is_ir_lock, flag);
- printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x "
- "other_ir 0x%x other_ies 0x%x mask 0x%x\n",
- __func__, __LINE__,
- (void *)mem, readl(&mem->sio_ir.raw),
- readl(&mem->sio_ies.raw),
- readl(&mem->other_ir.raw),
- readl(&mem->other_ies.raw),
- IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
- spin_unlock_irqrestore(&soft->is_ir_lock, flag);
- }
-#endif
- return handled ? IRQ_HANDLED : IRQ_NONE;
-}
-
-/**
- * ioc4_attach_local - Device initialization.
- * Called at *_attach() time for each
- * IOC4 with serial ports in the system.
- * @idd: Master module data for this IOC4
- */
-static inline int ioc4_attach_local(struct ioc4_driver_data *idd)
-{
- struct ioc4_port *port;
- struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
- int port_number;
- uint16_t ioc4_revid_min = 62;
- uint16_t ioc4_revid;
- struct pci_dev *pdev = idd->idd_pdev;
- struct ioc4_control* control = idd->idd_serial_data;
- struct ioc4_soft *soft = control->ic_soft;
- void __iomem *ioc4_misc = idd->idd_misc_regs;
- void __iomem *ioc4_serial = soft->is_ioc4_serial_addr;
-
- /* IOC4 firmware must be at least rev 62 */
- pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
-
- printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid);
- if (ioc4_revid < ioc4_revid_min) {
- printk(KERN_WARNING
- "IOC4 serial not supported on firmware rev %d, "
- "please upgrade to rev %d or higher\n",
- ioc4_revid, ioc4_revid_min);
- return -EPERM;
- }
- BUG_ON(ioc4_misc == NULL);
- BUG_ON(ioc4_serial == NULL);
-
- /* Create port structures for each port */
- for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS;
- port_number++) {
- port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL);
- if (!port) {
- printk(KERN_WARNING
- "IOC4 serial memory not available for port\n");
- goto free;
- }
- spin_lock_init(&port->ip_lock);
-
- /* we need to remember the previous ones, to point back to
- * them farther down - setting up the ring buffers.
- */
- ports[port_number] = port;
-
- /* Allocate buffers and jumpstart the hardware. */
- control->ic_port[port_number].icp_port = port;
- port->ip_ioc4_soft = soft;
- port->ip_pdev = pdev;
- port->ip_ienb = 0;
- /* Use baud rate calculations based on detected PCI
- * bus speed. Simply test whether the PCI clock is
- * running closer to 66MHz or 33MHz.
- */
- if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) {
- port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66;
- } else {
- port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33;
- }
- port->ip_baud = 9600;
- port->ip_control = control;
- port->ip_mem = ioc4_misc;
- port->ip_serial = ioc4_serial;
-
- /* point to the right hook */
- port->ip_hooks = &hooks_array[port_number];
-
- /* Get direct hooks to the serial regs and uart regs
- * for this port
- */
- switch (port_number) {
- case 0:
- port->ip_serial_regs = &(port->ip_serial->port_0);
- port->ip_uart_regs = &(port->ip_serial->uart_0);
- break;
- case 1:
- port->ip_serial_regs = &(port->ip_serial->port_1);
- port->ip_uart_regs = &(port->ip_serial->uart_1);
- break;
- case 2:
- port->ip_serial_regs = &(port->ip_serial->port_2);
- port->ip_uart_regs = &(port->ip_serial->uart_2);
- break;
- default:
- case 3:
- port->ip_serial_regs = &(port->ip_serial->port_3);
- port->ip_uart_regs = &(port->ip_serial->uart_3);
- break;
- }
-
- /* ring buffers are 1 to a pair of ports */
- if (port_number && (port_number & 1)) {
- /* odd use the evens buffer */
- port->ip_dma_ringbuf =
- ports[port_number - 1]->ip_dma_ringbuf;
- port->ip_cpu_ringbuf =
- ports[port_number - 1]->ip_cpu_ringbuf;
- port->ip_inring = RING(port, RX_1_OR_3);
- port->ip_outring = RING(port, TX_1_OR_3);
-
- } else {
- if (port->ip_dma_ringbuf == 0) {
- port->ip_cpu_ringbuf = pci_alloc_consistent
- (pdev, TOTAL_RING_BUF_SIZE,
- &port->ip_dma_ringbuf);
-
- }
- BUG_ON(!((((int64_t)port->ip_dma_ringbuf) &
- (TOTAL_RING_BUF_SIZE - 1)) == 0));
- DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p "
- "ip_dma_ringbuf 0x%p\n",
- __func__,
- (void *)port->ip_cpu_ringbuf,
- (void *)port->ip_dma_ringbuf));
- port->ip_inring = RING(port, RX_0_OR_2);
- port->ip_outring = RING(port, TX_0_OR_2);
- }
- DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p",
- __func__,
- port_number, (void *)port, (void *)control));
- DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
- (void *)port->ip_serial_regs,
- (void *)port->ip_uart_regs));
-
- /* Initialize the hardware for IOC4 */
- port_init(port);
-
- DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p "
- "outring 0x%p\n",
- __func__,
- port_number, (void *)port,
- (void *)port->ip_inring,
- (void *)port->ip_outring));
-
- /* Attach interrupt handlers */
- intr_connect(soft, IOC4_SIO_INTR_TYPE,
- GET_SIO_IR(port_number),
- handle_intr, port);
-
- intr_connect(soft, IOC4_OTHER_INTR_TYPE,
- GET_OTHER_IR(port_number),
- handle_dma_error_intr, port);
- }
- return 0;
-
-free:
- while (port_number)
- kfree(ports[--port_number]);
- return -ENOMEM;
-}
-
-/**
- * enable_intrs - enable interrupts
- * @port: port to enable
- * @mask: mask to use
- */
-static void enable_intrs(struct ioc4_port *port, uint32_t mask)
-{
- struct hooks *hooks = port->ip_hooks;
-
- if ((port->ip_ienb & mask) != mask) {
- write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES,
- IOC4_SIO_INTR_TYPE);
- port->ip_ienb |= mask;
- }
-
- if (port->ip_ienb)
- write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
- IOC4_W_IES, IOC4_OTHER_INTR_TYPE);
-}
-
-/**
- * local_open - local open a port
- * @port: port to open
- */
-static inline int local_open(struct ioc4_port *port)
-{
- int spiniter = 0;
-
- port->ip_flags = PORT_ACTIVE;
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
- writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while((readl(&port->ip_serial_regs-> sscr)
- & IOC4_SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER) {
- port->ip_flags = PORT_INACTIVE;
- return -1;
- }
- }
- }
-
- /* Reset the input fifo. If the uart received chars while the port
- * was closed and DMA is not enabled, the uart may have a bunch of
- * chars hanging around in its rx fifo which will not be discarded
- * by rclr in the upper layer. We must get rid of them here.
- */
- writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR,
- &port->ip_uart_regs->i4u_fcr);
-
- writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr);
- /* UART_LCR_STOP == 1 stop */
-
- /* Re-enable DMA, set default threshold to intr whenever there is
- * data available.
- */
- port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
- port->ip_sscr |= 1; /* default threshold */
-
- /* Plug in the new sscr. This implicitly clears the DMA_PAUSE
- * flag if it was set above
- */
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- port->ip_tx_lowat = 1;
- return 0;
-}
-
-/**
- * set_rx_timeout - Set rx timeout and threshold values.
- * @port: port to use
- * @timeout: timeout value in ticks
- */
-static inline int set_rx_timeout(struct ioc4_port *port, int timeout)
-{
- int threshold;
-
- port->ip_rx_timeout = timeout;
-
- /* Timeout is in ticks. Let's figure out how many chars we
- * can receive at the current baud rate in that interval
- * and set the rx threshold to that amount. There are 4 chars
- * per ring entry, so we'll divide the number of chars that will
- * arrive in timeout by 4.
- * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.
- */
- threshold = timeout * port->ip_baud / 4000;
- if (threshold == 0)
- threshold = 1; /* otherwise we'll intr all the time! */
-
- if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD)
- return 1;
-
- port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
- port->ip_sscr |= threshold;
-
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Now set the rx timeout to the given value
- * again timeout * IOC4_SRTR_HZ / HZ
- */
- timeout = timeout * IOC4_SRTR_HZ / 100;
- if (timeout > IOC4_SRTR_CNT)
- timeout = IOC4_SRTR_CNT;
-
- writel(timeout, &port->ip_serial_regs->srtr);
- return 0;
-}
-
-/**
- * config_port - config the hardware
- * @port: port to config
- * @baud: baud rate for the port
- * @byte_size: data size
- * @stop_bits: number of stop bits
- * @parenb: parity enable ?
- * @parodd: odd parity ?
- */
-static inline int
-config_port(struct ioc4_port *port,
- int baud, int byte_size, int stop_bits, int parenb, int parodd)
-{
- char lcr, sizebits;
- int spiniter = 0;
-
- DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n",
- __func__, baud, byte_size, stop_bits, parenb, parodd));
-
- if (set_baud(port, baud))
- return 1;
-
- switch (byte_size) {
- case 5:
- sizebits = UART_LCR_WLEN5;
- break;
- case 6:
- sizebits = UART_LCR_WLEN6;
- break;
- case 7:
- sizebits = UART_LCR_WLEN7;
- break;
- case 8:
- sizebits = UART_LCR_WLEN8;
- break;
- default:
- return 1;
- }
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
- writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while((readl(&port->ip_serial_regs->sscr)
- & IOC4_SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER)
- return -1;
- }
- }
-
- /* Clear relevant fields in lcr */
- lcr = readb(&port->ip_uart_regs->i4u_lcr);
- lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |
- UART_LCR_PARITY | LCR_MASK_STOP_BITS);
-
- /* Set byte size in lcr */
- lcr |= sizebits;
-
- /* Set parity */
- if (parenb) {
- lcr |= UART_LCR_PARITY;
- if (!parodd)
- lcr |= UART_LCR_EPAR;
- }
-
- /* Set stop bits */
- if (stop_bits)
- lcr |= UART_LCR_STOP /* 2 stop bits */ ;
-
- writeb(lcr, &port->ip_uart_regs->i4u_lcr);
-
- /* Re-enable the DMA interface if necessary */
- if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- port->ip_baud = baud;
-
- /* When we get within this number of ring entries of filling the
- * entire ring on tx, place an EXPLICIT intr to generate a lowat
- * notification when output has drained.
- */
- port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;
- if (port->ip_tx_lowat == 0)
- port->ip_tx_lowat = 1;
-
- set_rx_timeout(port, 2);
-
- return 0;
-}
-
-/**
- * do_write - Write bytes to the port. Returns the number of bytes
- * actually written. Called from transmit_chars
- * @port: port to use
- * @buf: the stuff to write
- * @len: how many bytes in 'buf'
- */
-static inline int do_write(struct ioc4_port *port, char *buf, int len)
-{
- int prod_ptr, cons_ptr, total = 0;
- struct ring *outring;
- struct ring_entry *entry;
- struct hooks *hooks = port->ip_hooks;
-
- BUG_ON(!(len >= 0));
-
- prod_ptr = port->ip_tx_prod;
- cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
- outring = port->ip_outring;
-
- /* Maintain a 1-entry red-zone. The ring buffer is full when
- * (cons - prod) % ring_size is 1. Rather than do this subtraction
- * in the body of the loop, I'll do it now.
- */
- cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;
-
- /* Stuff the bytes into the output */
- while ((prod_ptr != cons_ptr) && (len > 0)) {
- int xx;
-
- /* Get 4 bytes (one ring entry) at a time */
- entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);
-
- /* Invalidate all entries */
- entry->ring_allsc = 0;
-
- /* Copy in some bytes */
- for (xx = 0; (xx < 4) && (len > 0); xx++) {
- entry->ring_data[xx] = *buf++;
- entry->ring_sc[xx] = IOC4_TXCB_VALID;
- len--;
- total++;
- }
-
- /* If we are within some small threshold of filling up the
- * entire ring buffer, we must place an EXPLICIT intr here
- * to generate a lowat interrupt in case we subsequently
- * really do fill up the ring and the caller goes to sleep.
- * No need to place more than one though.
- */
- if (!(port->ip_flags & LOWAT_WRITTEN) &&
- ((cons_ptr - prod_ptr) & PROD_CONS_MASK)
- <= port->ip_tx_lowat
- * (int)sizeof(struct ring_entry)) {
- port->ip_flags |= LOWAT_WRITTEN;
- entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE;
- }
-
- /* Go on to next entry */
- prod_ptr += sizeof(struct ring_entry);
- prod_ptr &= PROD_CONS_MASK;
- }
-
- /* If we sent something, start DMA if necessary */
- if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
- port->ip_sscr |= IOC4_SSCR_DMA_EN;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
-
- /* Store the new producer pointer. If tx is disabled, we stuff the
- * data into the ring buffer, but we don't actually start tx.
- */
- if (!uart_tx_stopped(port->ip_port)) {
- writel(prod_ptr, &port->ip_serial_regs->stpir);
-
- /* If we are now transmitting, enable tx_mt interrupt so we
- * can disable DMA if necessary when the tx finishes.
- */
- if (total > 0)
- enable_intrs(port, hooks->intr_tx_mt);
- }
- port->ip_tx_prod = prod_ptr;
- return total;
-}
-
-/**
- * disable_intrs - disable interrupts
- * @port: port to enable
- * @mask: mask to use
- */
-static void disable_intrs(struct ioc4_port *port, uint32_t mask)
-{
- struct hooks *hooks = port->ip_hooks;
-
- if (port->ip_ienb & mask) {
- write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC,
- IOC4_SIO_INTR_TYPE);
- port->ip_ienb &= ~mask;
- }
-
- if (!port->ip_ienb)
- write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
- IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
-}
-
-/**
- * set_notification - Modify event notification
- * @port: port to use
- * @mask: events mask
- * @set_on: set ?
- */
-static int set_notification(struct ioc4_port *port, int mask, int set_on)
-{
- struct hooks *hooks = port->ip_hooks;
- uint32_t intrbits, sscrbits;
-
- BUG_ON(!mask);
-
- intrbits = sscrbits = 0;
-
- if (mask & N_DATA_READY)
- intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);
- if (mask & N_OUTPUT_LOWAT)
- intrbits |= hooks->intr_tx_explicit;
- if (mask & N_DDCD) {
- intrbits |= hooks->intr_delta_dcd;
- sscrbits |= IOC4_SSCR_RX_RING_DCD;
- }
- if (mask & N_DCTS)
- intrbits |= hooks->intr_delta_cts;
-
- if (set_on) {
- enable_intrs(port, intrbits);
- port->ip_notify |= mask;
- port->ip_sscr |= sscrbits;
- } else {
- disable_intrs(port, intrbits);
- port->ip_notify &= ~mask;
- port->ip_sscr &= ~sscrbits;
- }
-
- /* We require DMA if either DATA_READY or DDCD notification is
- * currently requested. If neither of these is requested and
- * there is currently no tx in progress, DMA may be disabled.
- */
- if (port->ip_notify & (N_DATA_READY | N_DDCD))
- port->ip_sscr |= IOC4_SSCR_DMA_EN;
- else if (!(port->ip_ienb & hooks->intr_tx_mt))
- port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
-
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- return 0;
-}
-
-/**
- * set_mcr - set the master control reg
- * @the_port: port to use
- * @mask1: mcr mask
- * @mask2: shadow mask
- */
-static inline int set_mcr(struct uart_port *the_port,
- int mask1, int mask2)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- uint32_t shadow;
- int spiniter = 0;
- char mcr;
-
- if (!port)
- return -1;
-
- /* Pause the DMA interface if necessary */
- if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
- writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
- &port->ip_serial_regs->sscr);
- while ((readl(&port->ip_serial_regs->sscr)
- & IOC4_SSCR_PAUSE_STATE) == 0) {
- spiniter++;
- if (spiniter > MAXITER)
- return -1;
- }
- }
- shadow = readl(&port->ip_serial_regs->shadow);
- mcr = (shadow & 0xff000000) >> 24;
-
- /* Set new value */
- mcr |= mask1;
- shadow |= mask2;
-
- writeb(mcr, &port->ip_uart_regs->i4u_mcr);
- writel(shadow, &port->ip_serial_regs->shadow);
-
- /* Re-enable the DMA interface if necessary */
- if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- return 0;
-}
-
-/**
- * ioc4_set_proto - set the protocol for the port
- * @port: port to use
- * @proto: protocol to use
- */
-static int ioc4_set_proto(struct ioc4_port *port, int proto)
-{
- struct hooks *hooks = port->ip_hooks;
-
- switch (proto) {
- case PROTO_RS232:
- /* Clear the appropriate GIO pin */
- writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
- break;
-
- case PROTO_RS422:
- /* Set the appropriate GIO pin */
- writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
- break;
-
- default:
- return 1;
- }
- return 0;
-}
-
-/**
- * transmit_chars - upper level write, called with ip_lock
- * @the_port: port to write
- */
-static void transmit_chars(struct uart_port *the_port)
-{
- int xmit_count, tail, head;
- int result;
- char *start;
- struct tty_struct *tty;
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- struct uart_state *state;
-
- if (!the_port)
- return;
- if (!port)
- return;
-
- state = the_port->state;
- tty = state->port.tty;
-
- if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) {
- /* Nothing to do or hw stopped */
- set_notification(port, N_ALL_OUTPUT, 0);
- return;
- }
-
- head = state->xmit.head;
- tail = state->xmit.tail;
- start = (char *)&state->xmit.buf[tail];
-
- /* write out all the data or until the end of the buffer */
- xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
- if (xmit_count > 0) {
- result = do_write(port, start, xmit_count);
- if (result > 0) {
- /* booking */
- xmit_count -= result;
- the_port->icount.tx += result;
- /* advance the pointers */
- tail += result;
- tail &= UART_XMIT_SIZE - 1;
- state->xmit.tail = tail;
- start = (char *)&state->xmit.buf[tail];
- }
- }
- if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS)
- uart_write_wakeup(the_port);
-
- if (uart_circ_empty(&state->xmit)) {
- set_notification(port, N_OUTPUT_LOWAT, 0);
- } else {
- set_notification(port, N_OUTPUT_LOWAT, 1);
- }
-}
-
-/**
- * ioc4_change_speed - change the speed of the port
- * @the_port: port to change
- * @new_termios: new termios settings
- * @old_termios: old termios settings
- */
-static void
-ioc4_change_speed(struct uart_port *the_port,
- struct ktermios *new_termios, struct ktermios *old_termios)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- int baud, bits;
- unsigned cflag, iflag;
- int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
- struct uart_state *state = the_port->state;
-
- cflag = new_termios->c_cflag;
- iflag = new_termios->c_iflag;
-
- switch (cflag & CSIZE) {
- case CS5:
- new_data = 5;
- bits = 7;
- break;
- case CS6:
- new_data = 6;
- bits = 8;
- break;
- case CS7:
- new_data = 7;
- bits = 9;
- break;
- case CS8:
- new_data = 8;
- bits = 10;
- break;
- default:
- /* cuz we always need a default ... */
- new_data = 5;
- bits = 7;
- break;
- }
- if (cflag & CSTOPB) {
- bits++;
- new_stop = 1;
- }
- if (cflag & PARENB) {
- bits++;
- new_parity_enable = 1;
- if (cflag & PARODD)
- new_parity = 1;
- }
- baud = uart_get_baud_rate(the_port, new_termios, old_termios,
- MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
- DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud));
-
- /* default is 9600 */
- if (!baud)
- baud = 9600;
-
- if (!the_port->fifosize)
- the_port->fifosize = IOC4_FIFO_CHARS;
- the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10));
- the_port->timeout += HZ / 50; /* Add .02 seconds of slop */
-
- the_port->ignore_status_mask = N_ALL_INPUT;
-
- state->port.low_latency = 1;
-
- if (iflag & IGNPAR)
- the_port->ignore_status_mask &= ~(N_PARITY_ERROR
- | N_FRAMING_ERROR);
- if (iflag & IGNBRK) {
- the_port->ignore_status_mask &= ~N_BREAK;
- if (iflag & IGNPAR)
- the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
- }
- if (!(cflag & CREAD)) {
- /* ignore everything */
- the_port->ignore_status_mask &= ~N_DATA_READY;
- }
-
- if (cflag & CRTSCTS) {
- port->ip_sscr |= IOC4_SSCR_HFC_EN;
- }
- else {
- port->ip_sscr &= ~IOC4_SSCR_HFC_EN;
- }
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
-
- /* Set the configuration and proper notification call */
- DPRINT_CONFIG(("%s : port 0x%p cflag 0%o "
- "config_port(baud %d data %d stop %d p enable %d parity %d),"
- " notification 0x%x\n",
- __func__, (void *)port, cflag, baud, new_data, new_stop,
- new_parity_enable, new_parity, the_port->ignore_status_mask));
-
- if ((config_port(port, baud, /* baud */
- new_data, /* byte size */
- new_stop, /* stop bits */
- new_parity_enable, /* set parity */
- new_parity)) >= 0) { /* parity 1==odd */
- set_notification(port, the_port->ignore_status_mask, 1);
- }
-}
-
-/**
- * ic4_startup_local - Start up the serial port - returns >= 0 if no errors
- * @the_port: Port to operate on
- */
-static inline int ic4_startup_local(struct uart_port *the_port)
-{
- struct ioc4_port *port;
- struct uart_state *state;
-
- if (!the_port)
- return -1;
-
- port = get_ioc4_port(the_port, 0);
- if (!port)
- return -1;
-
- state = the_port->state;
-
- local_open(port);
-
- /* set the protocol - mapbase has the port type */
- ioc4_set_proto(port, the_port->mapbase);
-
- /* set the speed of the serial port */
- ioc4_change_speed(the_port, &state->port.tty->termios,
- (struct ktermios *)0);
-
- return 0;
-}
-
-/*
- * ioc4_cb_output_lowat - called when the output low water mark is hit
- * @the_port: port to output
- */
-static void ioc4_cb_output_lowat(struct uart_port *the_port)
-{
- unsigned long pflags;
-
- /* ip_lock is set on the call here */
- if (the_port) {
- spin_lock_irqsave(&the_port->lock, pflags);
- transmit_chars(the_port);
- spin_unlock_irqrestore(&the_port->lock, pflags);
- }
-}
-
-/**
- * handle_intr - service any interrupts for the given port - 2nd level
- * called via sd_intr
- * @arg: handler arg
- * @sio_ir: ioc4regs
- */
-static void handle_intr(void *arg, uint32_t sio_ir)
-{
- struct ioc4_port *port = (struct ioc4_port *)arg;
- struct hooks *hooks = port->ip_hooks;
- unsigned int rx_high_rd_aborted = 0;
- unsigned long flags;
- struct uart_port *the_port;
- int loop_counter;
-
- /* Possible race condition here: The tx_mt interrupt bit may be
- * cleared without the intervention of the interrupt handler,
- * e.g. by a write. If the top level interrupt handler reads a
- * tx_mt, then some other processor does a write, starting up
- * output, then we come in here, see the tx_mt and stop DMA, the
- * output started by the other processor will hang. Thus we can
- * only rely on tx_mt being legitimate if it is read while the
- * port lock is held. Therefore this bit must be ignored in the
- * passed in interrupt mask which was read by the top level
- * interrupt handler since the port lock was not held at the time
- * it was read. We can only rely on this bit being accurate if it
- * is read while the port lock is held. So we'll clear it for now,
- * and reload it later once we have the port lock.
- */
- sio_ir &= ~(hooks->intr_tx_mt);
-
- spin_lock_irqsave(&port->ip_lock, flags);
-
- loop_counter = MAXITER; /* to avoid hangs */
-
- do {
- uint32_t shadow;
-
- if ( loop_counter-- <= 0 ) {
- printk(KERN_WARNING "IOC4 serial: "
- "possible hang condition/"
- "port stuck on interrupt.\n");
- break;
- }
-
- /* Handle a DCD change */
- if (sio_ir & hooks->intr_delta_dcd) {
- /* ACK the interrupt */
- writel(hooks->intr_delta_dcd,
- &port->ip_mem->sio_ir.raw);
-
- shadow = readl(&port->ip_serial_regs->shadow);
-
- if ((port->ip_notify & N_DDCD)
- && (shadow & IOC4_SHADOW_DCD)
- && (port->ip_port)) {
- the_port = port->ip_port;
- the_port->icount.dcd = 1;
- wake_up_interruptible
- (&the_port->state->port.delta_msr_wait);
- } else if ((port->ip_notify & N_DDCD)
- && !(shadow & IOC4_SHADOW_DCD)) {
- /* Flag delta DCD/no DCD */
- port->ip_flags |= DCD_ON;
- }
- }
-
- /* Handle a CTS change */
- if (sio_ir & hooks->intr_delta_cts) {
- /* ACK the interrupt */
- writel(hooks->intr_delta_cts,
- &port->ip_mem->sio_ir.raw);
-
- shadow = readl(&port->ip_serial_regs->shadow);
-
- if ((port->ip_notify & N_DCTS)
- && (port->ip_port)) {
- the_port = port->ip_port;
- the_port->icount.cts =
- (shadow & IOC4_SHADOW_CTS) ? 1 : 0;
- wake_up_interruptible
- (&the_port->state->port.delta_msr_wait);
- }
- }
-
- /* rx timeout interrupt. Must be some data available. Put this
- * before the check for rx_high since servicing this condition
- * may cause that condition to clear.
- */
- if (sio_ir & hooks->intr_rx_timer) {
- /* ACK the interrupt */
- writel(hooks->intr_rx_timer,
- &port->ip_mem->sio_ir.raw);
-
- if ((port->ip_notify & N_DATA_READY)
- && (port->ip_port)) {
- /* ip_lock is set on call here */
- receive_chars(port->ip_port);
- }
- }
-
- /* rx high interrupt. Must be after rx_timer. */
- else if (sio_ir & hooks->intr_rx_high) {
- /* Data available, notify upper layer */
- if ((port->ip_notify & N_DATA_READY)
- && port->ip_port) {
- /* ip_lock is set on call here */
- receive_chars(port->ip_port);
- }
-
- /* We can't ACK this interrupt. If receive_chars didn't
- * cause the condition to clear, we'll have to disable
- * the interrupt until the data is drained.
- * If the read was aborted, don't disable the interrupt
- * as this may cause us to hang indefinitely. An
- * aborted read generally means that this interrupt
- * hasn't been delivered to the cpu yet anyway, even
- * though we see it as asserted when we read the sio_ir.
- */
- if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) {
- if ((port->ip_flags & READ_ABORTED) == 0) {
- port->ip_ienb &= ~hooks->intr_rx_high;
- port->ip_flags |= INPUT_HIGH;
- } else {
- rx_high_rd_aborted++;
- }
- }
- }
-
- /* We got a low water interrupt: notify upper layer to
- * send more data. Must come before tx_mt since servicing
- * this condition may cause that condition to clear.
- */
- if (sio_ir & hooks->intr_tx_explicit) {
- port->ip_flags &= ~LOWAT_WRITTEN;
-
- /* ACK the interrupt */
- writel(hooks->intr_tx_explicit,
- &port->ip_mem->sio_ir.raw);
-
- if (port->ip_notify & N_OUTPUT_LOWAT)
- ioc4_cb_output_lowat(port->ip_port);
- }
-
- /* Handle tx_mt. Must come after tx_explicit. */
- else if (sio_ir & hooks->intr_tx_mt) {
- /* If we are expecting a lowat notification
- * and we get to this point it probably means that for
- * some reason the tx_explicit didn't work as expected
- * (that can legitimately happen if the output buffer is
- * filled up in just the right way).
- * So send the notification now.
- */
- if (port->ip_notify & N_OUTPUT_LOWAT) {
- ioc4_cb_output_lowat(port->ip_port);
-
- /* We need to reload the sio_ir since the lowat
- * call may have caused another write to occur,
- * clearing the tx_mt condition.
- */
- sio_ir = PENDING(port);
- }
-
- /* If the tx_mt condition still persists even after the
- * lowat call, we've got some work to do.
- */
- if (sio_ir & hooks->intr_tx_mt) {
-
- /* If we are not currently expecting DMA input,
- * and the transmitter has just gone idle,
- * there is no longer any reason for DMA, so
- * disable it.
- */
- if (!(port->ip_notify
- & (N_DATA_READY | N_DDCD))) {
- BUG_ON(!(port->ip_sscr
- & IOC4_SSCR_DMA_EN));
- port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
- writel(port->ip_sscr,
- &port->ip_serial_regs->sscr);
- }
-
- /* Prevent infinite tx_mt interrupt */
- port->ip_ienb &= ~hooks->intr_tx_mt;
- }
- }
- sio_ir = PENDING(port);
-
- /* if the read was aborted and only hooks->intr_rx_high,
- * clear hooks->intr_rx_high, so we do not loop forever.
- */
-
- if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) {
- sio_ir &= ~hooks->intr_rx_high;
- }
- } while (sio_ir & hooks->intr_all);
-
- spin_unlock_irqrestore(&port->ip_lock, flags);
-
- /* Re-enable interrupts before returning from interrupt handler.
- * Getting interrupted here is okay. It'll just v() our semaphore, and
- * we'll come through the loop again.
- */
-
- write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES,
- IOC4_SIO_INTR_TYPE);
-}
-
-/*
- * ioc4_cb_post_ncs - called for some basic errors
- * @port: port to use
- * @ncs: event
- */
-static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs)
-{
- struct uart_icount *icount;
-
- icount = &the_port->icount;
-
- if (ncs & NCS_BREAK)
- icount->brk++;
- if (ncs & NCS_FRAMING)
- icount->frame++;
- if (ncs & NCS_OVERRUN)
- icount->overrun++;
- if (ncs & NCS_PARITY)
- icount->parity++;
-}
-
-/**
- * do_read - Read in bytes from the port. Return the number of bytes
- * actually read.
- * @the_port: port to use
- * @buf: place to put the stuff we read
- * @len: how big 'buf' is
- */
-
-static inline int do_read(struct uart_port *the_port, unsigned char *buf,
- int len)
-{
- int prod_ptr, cons_ptr, total;
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- struct ring *inring;
- struct ring_entry *entry;
- struct hooks *hooks;
- int byte_num;
- char *sc;
- int loop_counter;
-
- BUG_ON(!(len >= 0));
- BUG_ON(!port);
- hooks = port->ip_hooks;
-
- /* There is a nasty timing issue in the IOC4. When the rx_timer
- * expires or the rx_high condition arises, we take an interrupt.
- * At some point while servicing the interrupt, we read bytes from
- * the ring buffer and re-arm the rx_timer. However the rx_timer is
- * not started until the first byte is received *after* it is armed,
- * and any bytes pending in the rx construction buffers are not drained
- * to memory until either there are 4 bytes available or the rx_timer
- * expires. This leads to a potential situation where data is left
- * in the construction buffers forever - 1 to 3 bytes were received
- * after the interrupt was generated but before the rx_timer was
- * re-armed. At that point as long as no subsequent bytes are received
- * the timer will never be started and the bytes will remain in the
- * construction buffer forever. The solution is to execute a DRAIN
- * command after rearming the timer. This way any bytes received before
- * the DRAIN will be drained to memory, and any bytes received after
- * the DRAIN will start the TIMER and be drained when it expires.
- * Luckily, this only needs to be done when the DMA buffer is empty
- * since there is no requirement that this function return all
- * available data as long as it returns some.
- */
- /* Re-arm the timer */
- writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
-
- prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
- cons_ptr = port->ip_rx_cons;
-
- if (prod_ptr == cons_ptr) {
- int reset_dma = 0;
-
- /* Input buffer appears empty, do a flush. */
-
- /* DMA must be enabled for this to work. */
- if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
- port->ip_sscr |= IOC4_SSCR_DMA_EN;
- reset_dma = 1;
- }
-
- /* Potential race condition: we must reload the srpir after
- * issuing the drain command, otherwise we could think the rx
- * buffer is empty, then take a very long interrupt, and when
- * we come back it's full and we wait forever for the drain to
- * complete.
- */
- writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN,
- &port->ip_serial_regs->sscr);
- prod_ptr = readl(&port->ip_serial_regs->srpir)
- & PROD_CONS_MASK;
-
- /* We must not wait for the DRAIN to complete unless there are
- * at least 8 bytes (2 ring entries) available to receive the
- * data otherwise the DRAIN will never complete and we'll
- * deadlock here.
- * In fact, to make things easier, I'll just ignore the flush if
- * there is any data at all now available.
- */
- if (prod_ptr == cons_ptr) {
- loop_counter = 0;
- while (readl(&port->ip_serial_regs->sscr) &
- IOC4_SSCR_RX_DRAIN) {
- loop_counter++;
- if (loop_counter > MAXITER)
- return -1;
- }
-
- /* SIGH. We have to reload the prod_ptr *again* since
- * the drain may have caused it to change
- */
- prod_ptr = readl(&port->ip_serial_regs->srpir)
- & PROD_CONS_MASK;
- }
- if (reset_dma) {
- port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
- writel(port->ip_sscr, &port->ip_serial_regs->sscr);
- }
- }
- inring = port->ip_inring;
- port->ip_flags &= ~READ_ABORTED;
-
- total = 0;
- loop_counter = 0xfffff; /* to avoid hangs */
-
- /* Grab bytes from the hardware */
- while ((prod_ptr != cons_ptr) && (len > 0)) {
- entry = (struct ring_entry *)((caddr_t)inring + cons_ptr);
-
- if ( loop_counter-- <= 0 ) {
- printk(KERN_WARNING "IOC4 serial: "
- "possible hang condition/"
- "port stuck on read.\n");
- break;
- }
-
- /* According to the producer pointer, this ring entry
- * must contain some data. But if the PIO happened faster
- * than the DMA, the data may not be available yet, so let's
- * wait until it arrives.
- */
- if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
- /* Indicate the read is aborted so we don't disable
- * the interrupt thinking that the consumer is
- * congested.
- */
- port->ip_flags |= READ_ABORTED;
- len = 0;
- break;
- }
-
- /* Load the bytes/status out of the ring entry */
- for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) {
- sc = &(entry->ring_sc[byte_num]);
-
- /* Check for change in modem state or overrun */
- if ((*sc & IOC4_RXSB_MODEM_VALID)
- && (port->ip_notify & N_DDCD)) {
- /* Notify upper layer if DCD dropped */
-
- if ((port->ip_flags & DCD_ON)
- && !(*sc & IOC4_RXSB_DCD)) {
-
- /* If we have already copied some data,
- * return it. We'll pick up the carrier
- * drop on the next pass. That way we
- * don't throw away the data that has
- * already been copied back to
- * the caller's buffer.
- */
- if (total > 0) {
- len = 0;
- break;
- }
- port->ip_flags &= ~DCD_ON;
-
- /* Turn off this notification so the
- * carrier drop protocol won't see it
- * again when it does a read.
- */
- *sc &= ~IOC4_RXSB_MODEM_VALID;
-
- /* To keep things consistent, we need
- * to update the consumer pointer so
- * the next reader won't come in and
- * try to read the same ring entries
- * again. This must be done here before
- * the dcd change.
- */
-
- if ((entry->ring_allsc & RING_ANY_VALID)
- == 0) {
- cons_ptr += (int)sizeof
- (struct ring_entry);
- cons_ptr &= PROD_CONS_MASK;
- }
- writel(cons_ptr,
- &port->ip_serial_regs->srcir);
- port->ip_rx_cons = cons_ptr;
-
- /* Notify upper layer of carrier drop */
- if ((port->ip_notify & N_DDCD)
- && port->ip_port) {
- the_port->icount.dcd = 0;
- wake_up_interruptible
- (&the_port->state->
- port.delta_msr_wait);
- }
-
- /* If we had any data to return, we
- * would have returned it above.
- */
- return 0;
- }
- }
- if (*sc & IOC4_RXSB_MODEM_VALID) {
- /* Notify that an input overrun occurred */
- if ((*sc & IOC4_RXSB_OVERRUN)
- && (port->ip_notify & N_OVERRUN_ERROR)) {
- ioc4_cb_post_ncs(the_port, NCS_OVERRUN);
- }
- /* Don't look at this byte again */
- *sc &= ~IOC4_RXSB_MODEM_VALID;
- }
-
- /* Check for valid data or RX errors */
- if ((*sc & IOC4_RXSB_DATA_VALID) &&
- ((*sc & (IOC4_RXSB_PAR_ERR
- | IOC4_RXSB_FRAME_ERR
- | IOC4_RXSB_BREAK))
- && (port->ip_notify & (N_PARITY_ERROR
- | N_FRAMING_ERROR
- | N_BREAK)))) {
- /* There is an error condition on the next byte.
- * If we have already transferred some bytes,
- * we'll stop here. Otherwise if this is the
- * first byte to be read, we'll just transfer
- * it alone after notifying the
- * upper layer of its status.
- */
- if (total > 0) {
- len = 0;
- break;
- } else {
- if ((*sc & IOC4_RXSB_PAR_ERR) &&
- (port->ip_notify & N_PARITY_ERROR)) {
- ioc4_cb_post_ncs(the_port,
- NCS_PARITY);
- }
- if ((*sc & IOC4_RXSB_FRAME_ERR) &&
- (port->ip_notify & N_FRAMING_ERROR)){
- ioc4_cb_post_ncs(the_port,
- NCS_FRAMING);
- }
- if ((*sc & IOC4_RXSB_BREAK)
- && (port->ip_notify & N_BREAK)) {
- ioc4_cb_post_ncs
- (the_port,
- NCS_BREAK);
- }
- len = 1;
- }
- }
- if (*sc & IOC4_RXSB_DATA_VALID) {
- *sc &= ~IOC4_RXSB_DATA_VALID;
- *buf = entry->ring_data[byte_num];
- buf++;
- len--;
- total++;
- }
- }
-
- /* If we used up this entry entirely, go on to the next one,
- * otherwise we must have run out of buffer space, so
- * leave the consumer pointer here for the next read in case
- * there are still unread bytes in this entry.
- */
- if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
- cons_ptr += (int)sizeof(struct ring_entry);
- cons_ptr &= PROD_CONS_MASK;
- }
- }
-
- /* Update consumer pointer and re-arm rx timer interrupt */
- writel(cons_ptr, &port->ip_serial_regs->srcir);
- port->ip_rx_cons = cons_ptr;
-
- /* If we have now dipped below the rx high water mark and we have
- * rx_high interrupt turned off, we can now turn it back on again.
- */
- if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr)
- & PROD_CONS_MASK) < ((port->ip_sscr &
- IOC4_SSCR_RX_THRESHOLD)
- << IOC4_PROD_CONS_PTR_OFF))) {
- port->ip_flags &= ~INPUT_HIGH;
- enable_intrs(port, hooks->intr_rx_high);
- }
- return total;
-}
-
-/**
- * receive_chars - upper level read. Called with ip_lock.
- * @the_port: port to read from
- */
-static void receive_chars(struct uart_port *the_port)
-{
- unsigned char ch[IOC4_MAX_CHARS];
- int read_count, request_count = IOC4_MAX_CHARS;
- struct uart_icount *icount;
- struct uart_state *state = the_port->state;
- unsigned long pflags;
-
- /* Make sure all the pointers are "good" ones */
- if (!state)
- return;
-
- spin_lock_irqsave(&the_port->lock, pflags);
-
- request_count = tty_buffer_request_room(&state->port, IOC4_MAX_CHARS);
-
- if (request_count > 0) {
- icount = &the_port->icount;
- read_count = do_read(the_port, ch, request_count);
- if (read_count > 0) {
- tty_insert_flip_string(&state->port, ch, read_count);
- icount->rx += read_count;
- }
- }
-
- spin_unlock_irqrestore(&the_port->lock, pflags);
-
- tty_flip_buffer_push(&state->port);
-}
-
-/**
- * ic4_type - What type of console are we?
- * @port: Port to operate with (we ignore since we only have one port)
- *
- */
-static const char *ic4_type(struct uart_port *the_port)
-{
- if (the_port->mapbase == PROTO_RS232)
- return "SGI IOC4 Serial [rs232]";
- else
- return "SGI IOC4 Serial [rs422]";
-}
-
-/**
- * ic4_tx_empty - Is the transmitter empty?
- * @port: Port to operate on
- *
- */
-static unsigned int ic4_tx_empty(struct uart_port *the_port)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- unsigned int ret = 0;
-
- if (port_is_active(port, the_port)) {
- if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT)
- ret = TIOCSER_TEMT;
- }
- return ret;
-}
-
-/**
- * ic4_stop_tx - stop the transmitter
- * @port: Port to operate on
- *
- */
-static void ic4_stop_tx(struct uart_port *the_port)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
-
- if (port_is_active(port, the_port))
- set_notification(port, N_OUTPUT_LOWAT, 0);
-}
-
-/**
- * null_void_function -
- * @port: Port to operate on
- *
- */
-static void null_void_function(struct uart_port *the_port)
-{
-}
-
-/**
- * ic4_shutdown - shut down the port - free irq and disable
- * @port: Port to shut down
- *
- */
-static void ic4_shutdown(struct uart_port *the_port)
-{
- unsigned long port_flags;
- struct ioc4_port *port;
- struct uart_state *state;
-
- port = get_ioc4_port(the_port, 0);
- if (!port)
- return;
-
- state = the_port->state;
- port->ip_port = NULL;
-
- wake_up_interruptible(&state->port.delta_msr_wait);
-
- if (state->port.tty)
- set_bit(TTY_IO_ERROR, &state->port.tty->flags);
-
- spin_lock_irqsave(&the_port->lock, port_flags);
- set_notification(port, N_ALL, 0);
- port->ip_flags = PORT_INACTIVE;
- spin_unlock_irqrestore(&the_port->lock, port_flags);
-}
-
-/**
- * ic4_set_mctrl - set control lines (dtr, rts, etc)
- * @port: Port to operate on
- * @mctrl: Lines to set/unset
- *
- */
-static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
-{
- unsigned char mcr = 0;
- struct ioc4_port *port;
-
- port = get_ioc4_port(the_port, 0);
- if (!port_is_active(port, the_port))
- return;
-
- if (mctrl & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
- if (mctrl & TIOCM_DTR)
- mcr |= UART_MCR_DTR;
- if (mctrl & TIOCM_OUT1)
- mcr |= UART_MCR_OUT1;
- if (mctrl & TIOCM_OUT2)
- mcr |= UART_MCR_OUT2;
- if (mctrl & TIOCM_LOOP)
- mcr |= UART_MCR_LOOP;
-
- set_mcr(the_port, mcr, IOC4_SHADOW_DTR);
-}
-
-/**
- * ic4_get_mctrl - get control line info
- * @port: port to operate on
- *
- */
-static unsigned int ic4_get_mctrl(struct uart_port *the_port)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
- uint32_t shadow;
- unsigned int ret = 0;
-
- if (!port_is_active(port, the_port))
- return 0;
-
- shadow = readl(&port->ip_serial_regs->shadow);
- if (shadow & IOC4_SHADOW_DCD)
- ret |= TIOCM_CAR;
- if (shadow & IOC4_SHADOW_DR)
- ret |= TIOCM_DSR;
- if (shadow & IOC4_SHADOW_CTS)
- ret |= TIOCM_CTS;
- return ret;
-}
-
-/**
- * ic4_start_tx - Start transmitter, flush any output
- * @port: Port to operate on
- *
- */
-static void ic4_start_tx(struct uart_port *the_port)
-{
- struct ioc4_port *port = get_ioc4_port(the_port, 0);
-
- if (port_is_active(port, the_port)) {
- set_notification(port, N_OUTPUT_LOWAT, 1);
- enable_intrs(port, port->ip_hooks->intr_tx_mt);
- }
-}
-
-/**
- * ic4_break_ctl - handle breaks
- * @port: Port to operate on
- * @break_state: Break state
- *
- */
-static void ic4_break_ctl(struct uart_port *the_port, int break_state)
-{
-}
-
-/**
- * ic4_startup - Start up the serial port
- * @port: Port to operate on
- *
- */
-static int ic4_startup(struct uart_port *the_port)
-{
- int retval;
- struct ioc4_port *port;
- struct ioc4_control *control;
- struct uart_state *state;
- unsigned long port_flags;
-
- if (!the_port)
- return -ENODEV;
- port = get_ioc4_port(the_port, 1);
- if (!port)
- return -ENODEV;
- state = the_port->state;
-
- control = port->ip_control;
- if (!control) {
- port->ip_port = NULL;
- return -ENODEV;
- }
-
- /* Start up the serial port */
- spin_lock_irqsave(&the_port->lock, port_flags);
- retval = ic4_startup_local(the_port);
- spin_unlock_irqrestore(&the_port->lock, port_flags);
- return retval;
-}
-
-/**
- * ic4_set_termios - set termios stuff
- * @port: port to operate on
- * @termios: New settings
- * @termios: Old
- *
- */
-static void
-ic4_set_termios(struct uart_port *the_port,
- struct ktermios *termios, struct ktermios *old_termios)
-{
- unsigned long port_flags;
-
- spin_lock_irqsave(&the_port->lock, port_flags);
- ioc4_change_speed(the_port, termios, old_termios);
- spin_unlock_irqrestore(&the_port->lock, port_flags);
-}
-
-/**
- * ic4_request_port - allocate resources for port - no op....
- * @port: port to operate on
- *
- */
-static int ic4_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-/* Associate the uart functions above - given to serial core */
-
-static const struct uart_ops ioc4_ops = {
- .tx_empty = ic4_tx_empty,
- .set_mctrl = ic4_set_mctrl,
- .get_mctrl = ic4_get_mctrl,
- .stop_tx = ic4_stop_tx,
- .start_tx = ic4_start_tx,
- .stop_rx = null_void_function,
- .break_ctl = ic4_break_ctl,
- .startup = ic4_startup,
- .shutdown = ic4_shutdown,
- .set_termios = ic4_set_termios,
- .type = ic4_type,
- .release_port = null_void_function,
- .request_port = ic4_request_port,
-};
-
-/*
- * Boot-time initialization code
- */
-
-static struct uart_driver ioc4_uart_rs232 = {
- .owner = THIS_MODULE,
- .driver_name = "ioc4_serial_rs232",
- .dev_name = DEVICE_NAME_RS232,
- .major = DEVICE_MAJOR,
- .minor = DEVICE_MINOR_RS232,
- .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
-};
-
-static struct uart_driver ioc4_uart_rs422 = {
- .owner = THIS_MODULE,
- .driver_name = "ioc4_serial_rs422",
- .dev_name = DEVICE_NAME_RS422,
- .major = DEVICE_MAJOR,
- .minor = DEVICE_MINOR_RS422,
- .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
-};
-
-
-/**
- * ioc4_serial_remove_one - detach function
- *
- * @idd: IOC4 master module data for this IOC4
- */
-
-static int ioc4_serial_remove_one(struct ioc4_driver_data *idd)
-{
- int port_num, port_type;
- struct ioc4_control *control;
- struct uart_port *the_port;
- struct ioc4_port *port;
- struct ioc4_soft *soft;
-
- /* If serial driver did not attach, don't try to detach */
- control = idd->idd_serial_data;
- if (!control)
- return 0;
-
- for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
- for (port_type = UART_PORT_MIN;
- port_type < UART_PORT_COUNT;
- port_type++) {
- the_port = &control->ic_port[port_num].icp_uart_port
- [port_type];
- if (the_port) {
- switch (port_type) {
- case UART_PORT_RS422:
- uart_remove_one_port(&ioc4_uart_rs422,
- the_port);
- break;
- default:
- case UART_PORT_RS232:
- uart_remove_one_port(&ioc4_uart_rs232,
- the_port);
- break;
- }
- }
- }
- port = control->ic_port[port_num].icp_port;
- /* we allocate in pairs */
- if (!(port_num & 1) && port) {
- pci_free_consistent(port->ip_pdev,
- TOTAL_RING_BUF_SIZE,
- port->ip_cpu_ringbuf,
- port->ip_dma_ringbuf);
- kfree(port);
- }
- }
- soft = control->ic_soft;
- if (soft) {
- free_irq(control->ic_irq, soft);
- if (soft->is_ioc4_serial_addr) {
- iounmap(soft->is_ioc4_serial_addr);
- release_mem_region((unsigned long)
- soft->is_ioc4_serial_addr,
- sizeof(struct ioc4_serial));
- }
- kfree(soft);
- }
- kfree(control);
- idd->idd_serial_data = NULL;
-
- return 0;
-}
-
-
-/**
- * ioc4_serial_core_attach_rs232 - register with serial core
- * This is done during pci probing
- * @pdev: handle for this card
- */
-static inline int
-ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
-{
- struct ioc4_port *port;
- struct uart_port *the_port;
- struct ioc4_driver_data *idd = pci_get_drvdata(pdev);
- struct ioc4_control *control = idd->idd_serial_data;
- int port_num;
- int port_type_idx;
- struct uart_driver *u_driver;
-
-
- DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n",
- __func__, pdev, (void *)control));
-
- if (!control)
- return -ENODEV;
-
- port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232
- : UART_PORT_RS422;
-
- u_driver = (port_type == PROTO_RS232) ? &ioc4_uart_rs232
- : &ioc4_uart_rs422;
-
- /* once around for each port on this card */
- for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
- the_port = &control->ic_port[port_num].icp_uart_port
- [port_type_idx];
- port = control->ic_port[port_num].icp_port;
- port->ip_all_ports[port_type_idx] = the_port;
-
- DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n",
- __func__, (void *)the_port,
- (void *)port,
- port_type == PROTO_RS232 ? "rs232" : "rs422"));
-
- /* membase, iobase and mapbase just need to be non-0 */
- the_port->membase = (unsigned char __iomem *)1;
- the_port->iobase = (pdev->bus->number << 16) | port_num;
- the_port->line = (Num_of_ioc4_cards << 2) | port_num;
- the_port->mapbase = port_type;
- the_port->type = PORT_16550A;
- the_port->fifosize = IOC4_FIFO_CHARS;
- the_port->ops = &ioc4_ops;
- the_port->irq = control->ic_irq;
- the_port->dev = &pdev->dev;
- spin_lock_init(&the_port->lock);
- if (uart_add_one_port(u_driver, the_port) < 0) {
- printk(KERN_WARNING
- "%s: unable to add port %d bus %d\n",
- __func__, the_port->line, pdev->bus->number);
- } else {
- DPRINT_CONFIG(
- ("IOC4 serial port %d irq = %d, bus %d\n",
- the_port->line, the_port->irq, pdev->bus->number));
- }
- }
- return 0;
-}
-
-/**
- * ioc4_serial_attach_one - register attach function
- * called per card found from IOC4 master module.
- * @idd: Master module data for this IOC4
- */
-static int
-ioc4_serial_attach_one(struct ioc4_driver_data *idd)
-{
- unsigned long tmp_addr1;
- struct ioc4_serial __iomem *serial;
- struct ioc4_soft *soft;
- struct ioc4_control *control;
- int ret = 0;
-
-
- DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev,
- idd->idd_pci_id));
-
- /* PCI-RT does not bring out serial connections.
- * Do not attach to this particular IOC4.
- */
- if (idd->idd_variant == IOC4_VARIANT_PCI_RT)
- return 0;
-
- /* request serial registers */
- tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET;
-
- if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial),
- "sioc4_uart")) {
- printk(KERN_WARNING
- "ioc4 (%p): unable to get request region for "
- "uart space\n", (void *)idd->idd_pdev);
- ret = -ENODEV;
- goto out1;
- }
- serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial));
- if (!serial) {
- printk(KERN_WARNING
- "ioc4 (%p) : unable to remap ioc4 serial register\n",
- (void *)idd->idd_pdev);
- ret = -ENODEV;
- goto out2;
- }
- DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n",
- __func__, (void *)idd->idd_misc_regs,
- (void *)serial));
-
- /* Get memory for the new card */
- control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL);
-
- if (!control) {
- printk(KERN_WARNING "ioc4_attach_one"
- ": unable to get memory for the IOC4\n");
- ret = -ENOMEM;
- goto out2;
- }
- idd->idd_serial_data = control;
-
- /* Allocate the soft structure */
- soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
- if (!soft) {
- printk(KERN_WARNING
- "ioc4 (%p): unable to get memory for the soft struct\n",
- (void *)idd->idd_pdev);
- ret = -ENOMEM;
- goto out3;
- }
-
- spin_lock_init(&soft->is_ir_lock);
- soft->is_ioc4_misc_addr = idd->idd_misc_regs;
- soft->is_ioc4_serial_addr = serial;
-
- /* Init the IOC4 */
- writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT,
- &idd->idd_misc_regs->sio_cr.raw);
-
- /* Enable serial port mode select generic PIO pins as outputs */
- writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL
- | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL,
- &idd->idd_misc_regs->gpcr_s.raw);
-
- /* Clear and disable all serial interrupts */
- write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
- writel(~0, &idd->idd_misc_regs->sio_ir.raw);
- write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC,
- IOC4_OTHER_INTR_TYPE);
- writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw);
- control->ic_soft = soft;
-
- /* Hook up interrupt handler */
- if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED,
- "sgi-ioc4serial", soft)) {
- control->ic_irq = idd->idd_pdev->irq;
- } else {
- printk(KERN_WARNING
- "%s : request_irq fails for IRQ 0x%x\n ",
- __func__, idd->idd_pdev->irq);
- }
- ret = ioc4_attach_local(idd);
- if (ret)
- goto out4;
-
- /* register port with the serial core - 1 rs232, 1 rs422 */
-
- ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232);
- if (ret)
- goto out4;
-
- ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422);
- if (ret)
- goto out5;
-
- Num_of_ioc4_cards++;
-
- return ret;
-
- /* error exits that give back resources */
-out5:
- ioc4_serial_remove_one(idd);
- return ret;
-out4:
- kfree(soft);
-out3:
- kfree(control);
-out2:
- if (serial)
- iounmap(serial);
- release_mem_region(tmp_addr1, sizeof(struct ioc4_serial));
-out1:
-
- return ret;
-}
-
-
-static struct ioc4_submodule ioc4_serial_submodule = {
- .is_name = "IOC4_serial",
- .is_owner = THIS_MODULE,
- .is_probe = ioc4_serial_attach_one,
- .is_remove = ioc4_serial_remove_one,
-};
-
-/**
- * ioc4_serial_init - module init
- */
-static int __init ioc4_serial_init(void)
-{
- int ret;
-
- /* register with serial core */
- if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) {
- printk(KERN_WARNING
- "%s: Couldn't register rs232 IOC4 serial driver\n",
- __func__);
- goto out;
- }
- if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
- printk(KERN_WARNING
- "%s: Couldn't register rs422 IOC4 serial driver\n",
- __func__);
- goto out_uart_rs232;
- }
-
- /* register with IOC4 main module */
- ret = ioc4_register_submodule(&ioc4_serial_submodule);
- if (ret)
- goto out_uart_rs422;
- return 0;
-
-out_uart_rs422:
- uart_unregister_driver(&ioc4_uart_rs422);
-out_uart_rs232:
- uart_unregister_driver(&ioc4_uart_rs232);
-out:
- return ret;
-}
-
-static void __exit ioc4_serial_exit(void)
-{
- ioc4_unregister_submodule(&ioc4_serial_submodule);
- uart_unregister_driver(&ioc4_uart_rs232);
- uart_unregister_driver(&ioc4_uart_rs422);
-}
-
-late_initcall(ioc4_serial_init); /* Call only after tty init is done */
-module_exit(ioc4_serial_exit);
-
-MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
-MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/jsm/Makefile b/drivers/tty/serial/jsm/Makefile
index 705d1ff..4f2dbad 100644
--- a/drivers/tty/serial/jsm/Makefile
+++ b/drivers/tty/serial/jsm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Jasmine adapter
#
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 93d3a0e..c7d51b5 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -8,6 +8,9 @@
*
* 2007-2008 (c) Jason Wessel - Wind River Systems, Inc.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/kgdb.h>
@@ -145,8 +148,10 @@
char *cptr = config;
struct console *cons;
- if (!strlen(config) || isspace(config[0]))
+ if (!strlen(config) || isspace(config[0])) {
+ err = 0;
goto noconfig;
+ }
kgdboc_io_ops.is_console = 0;
kgdb_tty_driver = NULL;
@@ -233,7 +238,7 @@
size_t len = strlen(kmessage);
if (len >= MAX_CONFIG_LEN) {
- printk(KERN_ERR "kgdboc: config string too long\n");
+ pr_err("config string too long\n");
return -ENOSPC;
}
@@ -244,8 +249,7 @@
}
if (kgdb_connected) {
- printk(KERN_ERR
- "kgdboc: Cannot reconfigure while KGDB is connected.\n");
+ pr_err("Cannot reconfigure while KGDB is connected.\n");
return -EBUSY;
}
@@ -273,10 +277,14 @@
/* Increment the module count when the debugger is active */
if (!kgdb_connected)
try_module_get(THIS_MODULE);
+
+ atomic_inc(&ignore_console_lock_warning);
}
static void kgdboc_post_exp_handler(void)
{
+ atomic_dec(&ignore_console_lock_warning);
+
/* decrement the module count when the debugger detaches */
if (!kgdb_connected)
module_put(THIS_MODULE);
diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c
index 0441282..fcbea43 100644
--- a/drivers/tty/serial/lantiq.c
+++ b/drivers/tty/serial/lantiq.c
@@ -8,24 +8,23 @@
* Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com>
*/
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/console.h>
-#include <linux/sysrq.h>
#include <linux/device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/serial.h>
-#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/lantiq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/gpio.h>
-
-#include <lantiq_soc.h>
+#include <linux/of_platform.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
#define PORT_LTQ_ASC 111
#define MAXPORTS 2
@@ -58,6 +57,7 @@
#define ASC_IRNCR_TIR 0x1
#define ASC_IRNCR_RIR 0x2
#define ASC_IRNCR_EIR 0x4
+#define ASC_IRNCR_MASK GENMASK(2, 0)
#define ASCOPT_CSIZE 0x3
#define TXFIFO_FL 1
@@ -100,19 +100,35 @@
static void lqasc_tx_chars(struct uart_port *port);
static struct ltq_uart_port *lqasc_port[MAXPORTS];
static struct uart_driver lqasc_reg;
-static DEFINE_SPINLOCK(ltq_asc_lock);
+
+struct ltq_soc_data {
+ int (*fetch_irq)(struct device *dev, struct ltq_uart_port *ltq_port);
+ int (*request_irq)(struct uart_port *port);
+ void (*free_irq)(struct uart_port *port);
+};
struct ltq_uart_port {
struct uart_port port;
/* clock used to derive divider */
- struct clk *fpiclk;
+ struct clk *freqclk;
/* clock gating of the ASC core */
struct clk *clk;
unsigned int tx_irq;
unsigned int rx_irq;
unsigned int err_irq;
+ unsigned int common_irq;
+ spinlock_t lock; /* exclusive access for multi core */
+
+ const struct ltq_soc_data *soc;
};
+static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg)
+{
+ u32 tmp = __raw_readl(reg);
+
+ __raw_writel((tmp & ~clear) | set, reg);
+}
+
static inline struct
ltq_uart_port *to_ltq_uart_port(struct uart_port *port)
{
@@ -129,16 +145,18 @@
lqasc_start_tx(struct uart_port *port)
{
unsigned long flags;
- spin_lock_irqsave(<q_asc_lock, flags);
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
lqasc_tx_chars(port);
- spin_unlock_irqrestore(<q_asc_lock, flags);
+ spin_unlock_irqrestore(<q_port->lock, flags);
return;
}
static void
lqasc_stop_rx(struct uart_port *port)
{
- ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
+ __raw_writel(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
}
static int
@@ -147,11 +165,12 @@
struct tty_port *tport = &port->state->port;
unsigned int ch = 0, rsr = 0, fifocnt;
- fifocnt = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
+ fifocnt = __raw_readl(port->membase + LTQ_ASC_FSTAT) &
+ ASCFSTAT_RXFFLMASK;
while (fifocnt--) {
u8 flag = TTY_NORMAL;
- ch = ltq_r8(port->membase + LTQ_ASC_RBUF);
- rsr = (ltq_r32(port->membase + LTQ_ASC_STATE)
+ ch = readb(port->membase + LTQ_ASC_RBUF);
+ rsr = (__raw_readl(port->membase + LTQ_ASC_STATE)
& ASCSTATE_ANY) | UART_DUMMY_UER_RX;
tty_flip_buffer_push(tport);
port->icount.rx++;
@@ -163,16 +182,16 @@
if (rsr & ASCSTATE_ANY) {
if (rsr & ASCSTATE_PE) {
port->icount.parity++;
- ltq_w32_mask(0, ASCWHBSTATE_CLRPE,
+ asc_update_bits(0, ASCWHBSTATE_CLRPE,
port->membase + LTQ_ASC_WHBSTATE);
} else if (rsr & ASCSTATE_FE) {
port->icount.frame++;
- ltq_w32_mask(0, ASCWHBSTATE_CLRFE,
+ asc_update_bits(0, ASCWHBSTATE_CLRFE,
port->membase + LTQ_ASC_WHBSTATE);
}
if (rsr & ASCSTATE_ROE) {
port->icount.overrun++;
- ltq_w32_mask(0, ASCWHBSTATE_CLRROE,
+ asc_update_bits(0, ASCWHBSTATE_CLRROE,
port->membase + LTQ_ASC_WHBSTATE);
}
@@ -211,10 +230,10 @@
return;
}
- while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) &
+ while (((__raw_readl(port->membase + LTQ_ASC_FSTAT) &
ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) {
if (port->x_char) {
- ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF);
+ writeb(port->x_char, port->membase + LTQ_ASC_TBUF);
port->icount.tx++;
port->x_char = 0;
continue;
@@ -223,7 +242,7 @@
if (uart_circ_empty(xmit))
break;
- ltq_w8(port->state->xmit.buf[port->state->xmit.tail],
+ writeb(port->state->xmit.buf[port->state->xmit.tail],
port->membase + LTQ_ASC_TBUF);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
@@ -238,9 +257,11 @@
{
unsigned long flags;
struct uart_port *port = (struct uart_port *)_port;
- spin_lock_irqsave(<q_asc_lock, flags);
- ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
- spin_unlock_irqrestore(<q_asc_lock, flags);
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
+ __raw_writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
+ spin_unlock_irqrestore(<q_port->lock, flags);
lqasc_start_tx(port);
return IRQ_HANDLED;
}
@@ -250,11 +271,13 @@
{
unsigned long flags;
struct uart_port *port = (struct uart_port *)_port;
- spin_lock_irqsave(<q_asc_lock, flags);
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
/* clear any pending interrupts */
- ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
+ asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
- spin_unlock_irqrestore(<q_asc_lock, flags);
+ spin_unlock_irqrestore(<q_port->lock, flags);
return IRQ_HANDLED;
}
@@ -263,10 +286,37 @@
{
unsigned long flags;
struct uart_port *port = (struct uart_port *)_port;
- spin_lock_irqsave(<q_asc_lock, flags);
- ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
+ __raw_writel(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
lqasc_rx_chars(port);
- spin_unlock_irqrestore(<q_asc_lock, flags);
+ spin_unlock_irqrestore(<q_port->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lqasc_irq(int irq, void *p)
+{
+ unsigned long flags;
+ u32 stat;
+ struct uart_port *port = p;
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
+ stat = readl(port->membase + LTQ_ASC_IRNCR);
+ spin_unlock_irqrestore(<q_port->lock, flags);
+ if (!(stat & ASC_IRNCR_MASK))
+ return IRQ_NONE;
+
+ if (stat & ASC_IRNCR_TIR)
+ lqasc_tx_int(irq, p);
+
+ if (stat & ASC_IRNCR_RIR)
+ lqasc_rx_int(irq, p);
+
+ if (stat & ASC_IRNCR_EIR)
+ lqasc_err_int(irq, p);
+
return IRQ_HANDLED;
}
@@ -274,7 +324,8 @@
lqasc_tx_empty(struct uart_port *port)
{
int status;
- status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK;
+ status = __raw_readl(port->membase + LTQ_ASC_FSTAT) &
+ ASCFSTAT_TXFFLMASK;
return status ? 0 : TIOCSER_TEMT;
}
@@ -299,20 +350,22 @@
{
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
int retval;
+ unsigned long flags;
if (!IS_ERR(ltq_port->clk))
- clk_enable(ltq_port->clk);
- port->uartclk = clk_get_rate(ltq_port->fpiclk);
+ clk_prepare_enable(ltq_port->clk);
+ port->uartclk = clk_get_rate(ltq_port->freqclk);
- ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
+ spin_lock_irqsave(<q_port->lock, flags);
+ asc_update_bits(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
port->membase + LTQ_ASC_CLC);
- ltq_w32(0, port->membase + LTQ_ASC_PISEL);
- ltq_w32(
+ __raw_writel(0, port->membase + LTQ_ASC_PISEL);
+ __raw_writel(
((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON);
- ltq_w32(
+ __raw_writel(
((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
| ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
port->membase + LTQ_ASC_RXFCON);
@@ -320,38 +373,17 @@
* setting enable bits
*/
wmb();
- ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
+ asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
ASCCON_ROEN, port->membase + LTQ_ASC_CON);
- retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
- 0, "asc_tx", port);
- if (retval) {
- pr_err("failed to request lqasc_tx_int\n");
+ spin_unlock_irqrestore(<q_port->lock, flags);
+
+ retval = ltq_port->soc->request_irq(port);
+ if (retval)
return retval;
- }
- retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
- 0, "asc_rx", port);
- if (retval) {
- pr_err("failed to request lqasc_rx_int\n");
- goto err1;
- }
-
- retval = request_irq(ltq_port->err_irq, lqasc_err_int,
- 0, "asc_err", port);
- if (retval) {
- pr_err("failed to request lqasc_err_int\n");
- goto err2;
- }
-
- ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
+ __raw_writel(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
port->membase + LTQ_ASC_IRNREN);
- return 0;
-
-err2:
- free_irq(ltq_port->rx_irq, port);
-err1:
- free_irq(ltq_port->tx_irq, port);
return retval;
}
@@ -359,17 +391,19 @@
lqasc_shutdown(struct uart_port *port)
{
struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
- free_irq(ltq_port->tx_irq, port);
- free_irq(ltq_port->rx_irq, port);
- free_irq(ltq_port->err_irq, port);
+ unsigned long flags;
- ltq_w32(0, port->membase + LTQ_ASC_CON);
- ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
+ ltq_port->soc->free_irq(port);
+
+ spin_lock_irqsave(<q_port->lock, flags);
+ __raw_writel(0, port->membase + LTQ_ASC_CON);
+ asc_update_bits(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
port->membase + LTQ_ASC_RXFCON);
- ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
+ asc_update_bits(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
port->membase + LTQ_ASC_TXFCON);
+ spin_unlock_irqrestore(<q_port->lock, flags);
if (!IS_ERR(ltq_port->clk))
- clk_disable(ltq_port->clk);
+ clk_disable_unprepare(ltq_port->clk);
}
static void
@@ -382,6 +416,7 @@
unsigned int baud;
unsigned int con = 0;
unsigned long flags;
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
cflag = new->c_cflag;
iflag = new->c_iflag;
@@ -435,10 +470,10 @@
/* set error signals - framing, parity and overrun, enable receiver */
con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
- spin_lock_irqsave(<q_asc_lock, flags);
+ spin_lock_irqsave(<q_port->lock, flags);
/* set up CON */
- ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON);
+ asc_update_bits(0, con, port->membase + LTQ_ASC_CON);
/* Set baud rate - take a divider of 2 into account */
baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
@@ -446,24 +481,24 @@
divisor = divisor / 2 - 1;
/* disable the baudrate generator */
- ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
+ asc_update_bits(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
/* make sure the fractional divider is off */
- ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
+ asc_update_bits(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
/* set up to use divisor of 2 */
- ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
+ asc_update_bits(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
/* now we can write the new baudrate into the register */
- ltq_w32(divisor, port->membase + LTQ_ASC_BG);
+ __raw_writel(divisor, port->membase + LTQ_ASC_BG);
/* turn the baudrate generator back on */
- ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON);
+ asc_update_bits(0, ASCCON_R, port->membase + LTQ_ASC_CON);
/* enable rx */
- ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
+ __raw_writel(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
- spin_unlock_irqrestore(<q_asc_lock, flags);
+ spin_unlock_irqrestore(<q_port->lock, flags);
/* Don't rewrite B0 */
if (tty_termios_baud_rate(new))
@@ -572,26 +607,23 @@
return;
do {
- fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT)
+ fifofree = (__raw_readl(port->membase + LTQ_ASC_FSTAT)
& ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF;
} while (fifofree == 0);
- ltq_w8(ch, port->membase + LTQ_ASC_TBUF);
+ writeb(ch, port->membase + LTQ_ASC_TBUF);
}
static void lqasc_serial_port_write(struct uart_port *port, const char *s,
u_int count)
{
- unsigned long flags;
-
- spin_lock_irqsave(<q_asc_lock, flags);
uart_console_write(port, s, count, lqasc_console_putchar);
- spin_unlock_irqrestore(<q_asc_lock, flags);
}
static void
lqasc_console_write(struct console *co, const char *s, u_int count)
{
struct ltq_uart_port *ltq_port;
+ unsigned long flags;
if (co->index >= MAXPORTS)
return;
@@ -600,7 +632,9 @@
if (!ltq_port)
return;
+ spin_lock_irqsave(<q_port->lock, flags);
lqasc_serial_port_write(<q_port->port, s, count);
+ spin_unlock_irqrestore(<q_port->lock, flags);
}
static int __init
@@ -623,9 +657,9 @@
port = <q_port->port;
if (!IS_ERR(ltq_port->clk))
- clk_enable(ltq_port->clk);
+ clk_prepare_enable(ltq_port->clk);
- port->uartclk = clk_get_rate(ltq_port->fpiclk);
+ port->uartclk = clk_get_rate(ltq_port->freqclk);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -669,7 +703,8 @@
device->con->write = lqasc_serial_early_console_write;
return 0;
}
-OF_EARLYCON_DECLARE(lantiq, DRVNAME, lqasc_serial_early_console_setup);
+OF_EARLYCON_DECLARE(lantiq, "lantiq,asc", lqasc_serial_early_console_setup);
+OF_EARLYCON_DECLARE(lantiq, "intel,lgm-asc", lqasc_serial_early_console_setup);
static struct uart_driver lqasc_reg = {
.owner = THIS_MODULE,
@@ -681,40 +716,154 @@
.cons = &lqasc_console,
};
+static int fetch_irq_lantiq(struct device *dev, struct ltq_uart_port *ltq_port)
+{
+ struct uart_port *port = <q_port->port;
+ struct resource irqres[3];
+ int ret;
+
+ ret = of_irq_to_resource_table(dev->of_node, irqres, 3);
+ if (ret != 3) {
+ dev_err(dev,
+ "failed to get IRQs for serial port\n");
+ return -ENODEV;
+ }
+ ltq_port->tx_irq = irqres[0].start;
+ ltq_port->rx_irq = irqres[1].start;
+ ltq_port->err_irq = irqres[2].start;
+ port->irq = irqres[0].start;
+
+ return 0;
+}
+
+static int request_irq_lantiq(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+ int retval;
+
+ retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
+ 0, "asc_tx", port);
+ if (retval) {
+ dev_err(port->dev, "failed to request asc_tx\n");
+ return retval;
+ }
+
+ retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
+ 0, "asc_rx", port);
+ if (retval) {
+ dev_err(port->dev, "failed to request asc_rx\n");
+ goto err1;
+ }
+
+ retval = request_irq(ltq_port->err_irq, lqasc_err_int,
+ 0, "asc_err", port);
+ if (retval) {
+ dev_err(port->dev, "failed to request asc_err\n");
+ goto err2;
+ }
+ return 0;
+
+err2:
+ free_irq(ltq_port->rx_irq, port);
+err1:
+ free_irq(ltq_port->tx_irq, port);
+ return retval;
+}
+
+static void free_irq_lantiq(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ free_irq(ltq_port->tx_irq, port);
+ free_irq(ltq_port->rx_irq, port);
+ free_irq(ltq_port->err_irq, port);
+}
+
+static int fetch_irq_intel(struct device *dev, struct ltq_uart_port *ltq_port)
+{
+ struct uart_port *port = <q_port->port;
+ int ret;
+
+ ret = of_irq_get(dev->of_node, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to fetch IRQ for serial port\n");
+ return ret;
+ }
+ ltq_port->common_irq = ret;
+ port->irq = ret;
+
+ return 0;
+}
+
+static int request_irq_intel(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+ int retval;
+
+ retval = request_irq(ltq_port->common_irq, lqasc_irq, 0,
+ "asc_irq", port);
+ if (retval)
+ dev_err(port->dev, "failed to request asc_irq\n");
+
+ return retval;
+}
+
+static void free_irq_intel(struct uart_port *port)
+{
+ struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+
+ free_irq(ltq_port->common_irq, port);
+}
+
static int __init
lqasc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct ltq_uart_port *ltq_port;
struct uart_port *port;
- struct resource *mmres, irqres[3];
- int line = 0;
+ struct resource *mmres;
+ int line;
int ret;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- ret = of_irq_to_resource_table(node, irqres, 3);
- if (!mmres || (ret != 3)) {
+ if (!mmres) {
dev_err(&pdev->dev,
- "failed to get memory/irq for serial port\n");
+ "failed to get memory for serial port\n");
return -ENODEV;
}
- /* check if this is the console port */
- if (mmres->start != CPHYSADDR(LTQ_EARLY_ASC))
- line = 1;
+ ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
+ GFP_KERNEL);
+ if (!ltq_port)
+ return -ENOMEM;
+
+ port = <q_port->port;
+
+ ltq_port->soc = of_device_get_match_data(&pdev->dev);
+ ret = ltq_port->soc->fetch_irq(&pdev->dev, ltq_port);
+ if (ret)
+ return ret;
+
+ /* get serial id */
+ line = of_alias_get_id(node, "serial");
+ if (line < 0) {
+ if (IS_ENABLED(CONFIG_LANTIQ)) {
+ if (mmres->start == CPHYSADDR(LTQ_EARLY_ASC))
+ line = 0;
+ else
+ line = 1;
+ } else {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
+ line);
+ return line;
+ }
+ }
if (lqasc_port[line]) {
dev_err(&pdev->dev, "port %d already allocated\n", line);
return -EBUSY;
}
- ltq_port = devm_kzalloc(&pdev->dev, sizeof(struct ltq_uart_port),
- GFP_KERNEL);
- if (!ltq_port)
- return -ENOMEM;
-
- port = <q_port->port;
-
port->iotype = SERIAL_IO_MEM;
port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
port->ops = &lqasc_pops;
@@ -723,22 +872,26 @@
port->line = line;
port->dev = &pdev->dev;
/* unused, just to be backward-compatible */
- port->irq = irqres[0].start;
port->mapbase = mmres->start;
- ltq_port->fpiclk = clk_get_fpi();
- if (IS_ERR(ltq_port->fpiclk)) {
+ if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
+ ltq_port->freqclk = clk_get_fpi();
+ else
+ ltq_port->freqclk = devm_clk_get(&pdev->dev, "freq");
+
+
+ if (IS_ERR(ltq_port->freqclk)) {
pr_err("failed to get fpi clk\n");
return -ENOENT;
}
/* not all asc ports have clock gates, lets ignore the return code */
- ltq_port->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK))
+ ltq_port->clk = clk_get(&pdev->dev, NULL);
+ else
+ ltq_port->clk = devm_clk_get(&pdev->dev, "asc");
- ltq_port->tx_irq = irqres[0].start;
- ltq_port->rx_irq = irqres[1].start;
- ltq_port->err_irq = irqres[2].start;
-
+ spin_lock_init(<q_port->lock);
lqasc_port[line] = ltq_port;
platform_set_drvdata(pdev, ltq_port);
@@ -747,8 +900,21 @@
return ret;
}
+static const struct ltq_soc_data soc_data_lantiq = {
+ .fetch_irq = fetch_irq_lantiq,
+ .request_irq = request_irq_lantiq,
+ .free_irq = free_irq_lantiq,
+};
+
+static const struct ltq_soc_data soc_data_intel = {
+ .fetch_irq = fetch_irq_intel,
+ .request_irq = request_irq_intel,
+ .free_irq = free_irq_intel,
+};
+
static const struct of_device_id ltq_asc_match[] = {
- { .compatible = DRVNAME },
+ { .compatible = "lantiq,asc", .data = &soc_data_lantiq },
+ { .compatible = "intel,lgm-asc", .data = &soc_data_intel },
{},
};
@@ -759,7 +925,7 @@
},
};
-int __init
+static int __init
init_lqasc(void)
{
int ret;
diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c
index d1d7326..9a836dc 100644
--- a/drivers/tty/serial/lpc32xx_hs.c
+++ b/drivers/tty/serial/lpc32xx_hs.c
@@ -25,8 +25,8 @@
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/of.h>
-#include <mach/platform.h>
-#include <mach/hardware.h>
+#include <linux/sizes.h>
+#include <linux/soc/nxp/lpc32xx-misc.h>
/*
* High Speed UART register offsets
@@ -81,6 +81,8 @@
#define LPC32XX_HSU_TX_TL8B (0x2 << 0)
#define LPC32XX_HSU_TX_TL16B (0x3 << 0)
+#define LPC32XX_MAIN_OSC_FREQ 13000000
+
#define MODNAME "lpc32xx_hsuart"
struct lpc32xx_hsuart_port {
@@ -170,6 +172,8 @@
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
+ lpc32xx_loopback_set(port->mapbase, 0); /* get out of loopback mode */
+
return uart_set_options(port, co, baud, parity, bits, flow);
}
@@ -435,35 +439,6 @@
spin_unlock_irqrestore(&port->lock, flags);
}
-/* LPC3250 Errata HSUART.1: Hang workaround via loopback mode on inactivity */
-static void lpc32xx_loopback_set(resource_size_t mapbase, int state)
-{
- int bit;
- u32 tmp;
-
- switch (mapbase) {
- case LPC32XX_HS_UART1_BASE:
- bit = 0;
- break;
- case LPC32XX_HS_UART2_BASE:
- bit = 1;
- break;
- case LPC32XX_HS_UART7_BASE:
- bit = 6;
- break;
- default:
- WARN(1, "lpc32xx_hs: Warning: Unknown port at %08x\n", mapbase);
- return;
- }
-
- tmp = readl(LPC32XX_UARTCTL_CLOOP);
- if (state)
- tmp |= (1 << bit);
- else
- tmp &= ~(1 << bit);
- writel(tmp, LPC32XX_UARTCTL_CLOOP);
-}
-
/* port->lock is not held. */
static int serial_lpc32xx_startup(struct uart_port *port)
{
@@ -683,11 +658,8 @@
p->port.membase = NULL;
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n",
- uarts_registered);
+ if (ret < 0)
return ret;
- }
p->port.irq = ret;
p->port.iotype = UPIO_MEM32;
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 3db48fc..8434bd5 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -248,6 +248,7 @@
struct max310x_devtype {
char name[9];
int nr;
+ u8 mode1;
int (*detect)(struct device *);
void (*power)(struct uart_port *, int);
};
@@ -257,12 +258,17 @@
struct work_struct tx_work;
struct work_struct md_work;
struct work_struct rs_work;
+
+ u8 wr_header;
+ u8 rd_header;
+ u8 rx_buf[MAX310X_FIFO_SIZE];
};
+#define to_max310x_port(_port) \
+ container_of(_port, struct max310x_one, port)
struct max310x_port {
struct max310x_devtype *devtype;
struct regmap *regmap;
- struct mutex mutex;
struct clk *clk;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
@@ -410,6 +416,7 @@
static const struct max310x_devtype max3107_devtype = {
.name = "MAX3107",
.nr = 1,
+ .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT | MAX310X_MODE1_IRQSEL_BIT,
.detect = max3107_detect,
.power = max310x_power,
};
@@ -417,6 +424,7 @@
static const struct max310x_devtype max3108_devtype = {
.name = "MAX3108",
.nr = 1,
+ .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT,
.detect = max3108_detect,
.power = max310x_power,
};
@@ -424,6 +432,7 @@
static const struct max310x_devtype max3109_devtype = {
.name = "MAX3109",
.nr = 2,
+ .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT,
.detect = max3109_detect,
.power = max310x_power,
};
@@ -431,6 +440,7 @@
static const struct max310x_devtype max14830_devtype = {
.name = "MAX14830",
.nr = 4,
+ .mode1 = MAX310X_MODE1_IRQSEL_BIT,
.detect = max14830_detect,
.power = max14830_power,
};
@@ -491,37 +501,48 @@
static int max310x_set_baud(struct uart_port *port, int baud)
{
- unsigned int mode = 0, clk = port->uartclk, div = clk / baud;
+ unsigned int mode = 0, div = 0, frac = 0, c = 0, F = 0;
- /* Check for minimal value for divider */
- if (div < 16)
- div = 16;
-
- if (clk % baud && (div / 16) < 0x8000) {
+ /*
+ * Calculate the integer divisor first. Select a proper mode
+ * in case if the requested baud is too high for the pre-defined
+ * clocks frequency.
+ */
+ div = port->uartclk / baud;
+ if (div < 8) {
+ /* Mode x4 */
+ c = 4;
+ mode = MAX310X_BRGCFG_4XMODE_BIT;
+ } else if (div < 16) {
/* Mode x2 */
+ c = 8;
mode = MAX310X_BRGCFG_2XMODE_BIT;
- clk = port->uartclk * 2;
- div = clk / baud;
-
- if (clk % baud && (div / 16) < 0x8000) {
- /* Mode x4 */
- mode = MAX310X_BRGCFG_4XMODE_BIT;
- clk = port->uartclk * 4;
- div = clk / baud;
- }
+ } else {
+ c = 16;
}
- max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8);
- max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16);
- max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode);
+ /* Calculate the divisor in accordance with the fraction coefficient */
+ div /= c;
+ F = c*baud;
- return DIV_ROUND_CLOSEST(clk, div);
+ /* Calculate the baud rate fraction */
+ if (div > 0)
+ frac = (16*(port->uartclk % F)) / F;
+ else
+ div = 1;
+
+ max310x_port_write(port, MAX310X_BRGDIVMSB_REG, div >> 8);
+ max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div);
+ max310x_port_write(port, MAX310X_BRGCFG_REG, frac | mode);
+
+ /* Return the actual baud rate we just programmed */
+ return (16*port->uartclk) / (c*(16*div + frac));
}
static int max310x_update_best_err(unsigned long f, long *besterr)
{
/* Use baudrate 115200 for calculate error */
- long err = f % (115200 * 16);
+ long err = f % (460800 * 16);
if ((*besterr < 0) || (*besterr > err)) {
*besterr = err;
@@ -576,7 +597,7 @@
}
/* Configure clock source */
- clksrc = xtal ? MAX310X_CLKSRC_CRYST_BIT : MAX310X_CLKSRC_EXTCLK_BIT;
+ clksrc = MAX310X_CLKSRC_EXTCLK_BIT | (xtal ? MAX310X_CLKSRC_CRYST_BIT : 0);
/* Configure PLL */
if (pllcfg) {
@@ -602,11 +623,11 @@
static void max310x_batch_write(struct uart_port *port, u8 *txbuf, unsigned int len)
{
- u8 header[] = { (port->iobase + MAX310X_THR_REG) | MAX310X_WRITE_BIT };
+ struct max310x_one *one = to_max310x_port(port);
struct spi_transfer xfer[] = {
{
- .tx_buf = &header,
- .len = sizeof(header),
+ .tx_buf = &one->wr_header,
+ .len = sizeof(one->wr_header),
}, {
.tx_buf = txbuf,
.len = len,
@@ -617,11 +638,11 @@
static void max310x_batch_read(struct uart_port *port, u8 *rxbuf, unsigned int len)
{
- u8 header[] = { port->iobase + MAX310X_RHR_REG };
+ struct max310x_one *one = to_max310x_port(port);
struct spi_transfer xfer[] = {
{
- .tx_buf = &header,
- .len = sizeof(header),
+ .tx_buf = &one->rd_header,
+ .len = sizeof(one->rd_header),
}, {
.rx_buf = rxbuf,
.len = len,
@@ -632,8 +653,8 @@
static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
{
+ struct max310x_one *one = to_max310x_port(port);
unsigned int sts, ch, flag, i;
- u8 buf[MAX310X_FIFO_SIZE];
if (port->read_status_mask == MAX310X_LSR_RXOVR_BIT) {
/* We are just reading, happily ignoring any error conditions.
@@ -648,7 +669,7 @@
* */
sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG);
- max310x_batch_read(port, buf, rxlen);
+ max310x_batch_read(port, one->rx_buf, rxlen);
port->icount.rx += rxlen;
flag = TTY_NORMAL;
@@ -659,9 +680,16 @@
port->icount.overrun++;
}
- for (i = 0; i < rxlen; ++i) {
- uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, buf[i], flag);
- }
+ for (i = 0; i < (rxlen - 1); ++i)
+ uart_insert_char(port, sts, 0, one->rx_buf[i], flag);
+
+ /*
+ * Handle the overrun case for the last character only, since
+ * the RxFIFO overflow happens after it is pushed to the FIFO
+ * tail.
+ */
+ uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT,
+ one->rx_buf[rxlen-1], flag);
} else {
if (unlikely(rxlen >= port->fifosize)) {
@@ -761,10 +789,9 @@
static void max310x_start_tx(struct uart_port *port)
{
- struct max310x_one *one = container_of(port, struct max310x_one, port);
+ struct max310x_one *one = to_max310x_port(port);
- if (!work_pending(&one->tx_work))
- schedule_work(&one->tx_work);
+ schedule_work(&one->tx_work);
}
static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno)
@@ -821,24 +848,18 @@
return IRQ_RETVAL(handled);
}
-static void max310x_wq_proc(struct work_struct *ws)
+static void max310x_tx_proc(struct work_struct *ws)
{
struct max310x_one *one = container_of(ws, struct max310x_one, tx_work);
- struct max310x_port *s = dev_get_drvdata(one->port.dev);
- mutex_lock(&s->mutex);
max310x_handle_tx(&one->port);
- mutex_unlock(&s->mutex);
}
static unsigned int max310x_tx_empty(struct uart_port *port)
{
- unsigned int lvl, sts;
+ u8 lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
- lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
- sts = max310x_port_read(port, MAX310X_IRQSTS_REG);
-
- return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0;
+ return lvl ? 0 : TIOCSER_TEMT;
}
static unsigned int max310x_get_mctrl(struct uart_port *port)
@@ -861,7 +882,7 @@
static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- struct max310x_one *one = container_of(port, struct max310x_one, port);
+ struct max310x_one *one = to_max310x_port(port);
schedule_work(&one->md_work);
}
@@ -934,17 +955,43 @@
/* Configure flow control */
max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]);
max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]);
- if (termios->c_cflag & CRTSCTS)
+
+ /* Disable transmitter before enabling AutoCTS or auto transmitter
+ * flow control
+ */
+ if (termios->c_cflag & CRTSCTS || termios->c_iflag & IXOFF) {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TXDIS_BIT,
+ MAX310X_MODE1_TXDIS_BIT);
+ }
+
+ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF);
+
+ if (termios->c_cflag & CRTSCTS) {
+ /* Enable AUTORTS and AUTOCTS */
+ port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT |
MAX310X_FLOWCTRL_AUTORTS_BIT;
+ }
if (termios->c_iflag & IXON)
flow |= MAX310X_FLOWCTRL_SWFLOW3_BIT |
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
- if (termios->c_iflag & IXOFF)
+ if (termios->c_iflag & IXOFF) {
+ port->status |= UPSTAT_AUTOXOFF;
flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT |
MAX310X_FLOWCTRL_SWFLOWEN_BIT;
+ }
max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow);
+ /* Enable transmitter after disabling AutoCTS and auto transmitter
+ * flow control
+ */
+ if (!(termios->c_cflag & CRTSCTS) && !(termios->c_iflag & IXOFF)) {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TXDIS_BIT,
+ 0);
+ }
+
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 0xffff,
@@ -960,37 +1007,36 @@
static void max310x_rs_proc(struct work_struct *ws)
{
struct max310x_one *one = container_of(ws, struct max310x_one, rs_work);
- unsigned int val;
+ unsigned int delay, mode1 = 0, mode2 = 0;
- val = (one->port.rs485.delay_rts_before_send << 4) |
+ delay = (one->port.rs485.delay_rts_before_send << 4) |
one->port.rs485.delay_rts_after_send;
- max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, val);
+ max310x_port_write(&one->port, MAX310X_HDPIXDELAY_REG, delay);
if (one->port.rs485.flags & SER_RS485_ENABLED) {
- max310x_port_update(&one->port, MAX310X_MODE1_REG,
- MAX310X_MODE1_TRNSCVCTRL_BIT,
- MAX310X_MODE1_TRNSCVCTRL_BIT);
- max310x_port_update(&one->port, MAX310X_MODE2_REG,
- MAX310X_MODE2_ECHOSUPR_BIT,
- MAX310X_MODE2_ECHOSUPR_BIT);
- } else {
- max310x_port_update(&one->port, MAX310X_MODE1_REG,
- MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
- max310x_port_update(&one->port, MAX310X_MODE2_REG,
- MAX310X_MODE2_ECHOSUPR_BIT, 0);
+ mode1 = MAX310X_MODE1_TRNSCVCTRL_BIT;
+
+ if (!(one->port.rs485.flags & SER_RS485_RX_DURING_TX))
+ mode2 = MAX310X_MODE2_ECHOSUPR_BIT;
}
+
+ max310x_port_update(&one->port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT, mode1);
+ max310x_port_update(&one->port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_ECHOSUPR_BIT, mode2);
}
static int max310x_rs485_config(struct uart_port *port,
struct serial_rs485 *rs485)
{
- struct max310x_one *one = container_of(port, struct max310x_one, port);
+ struct max310x_one *one = to_max310x_port(port);
if ((rs485->delay_rts_before_send > 0x0f) ||
(rs485->delay_rts_after_send > 0x0f))
return -ERANGE;
- rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_ENABLED;
+ rs485->flags &= SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX |
+ SER_RS485_ENABLED;
memset(rs485->padding, 0, sizeof(rs485->padding));
port->rs485 = *rs485;
@@ -1016,6 +1062,22 @@
max310x_port_update(port, MAX310X_MODE2_REG,
MAX310X_MODE2_FIFORST_BIT, 0);
+ /* Configure mode1/mode2 to have rs485/rs232 enabled at startup */
+ val = (clamp(port->rs485.delay_rts_before_send, 0U, 15U) << 4) |
+ clamp(port->rs485.delay_rts_after_send, 0U, 15U);
+ max310x_port_write(port, MAX310X_HDPIXDELAY_REG, val);
+
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT,
+ MAX310X_MODE1_TRNSCVCTRL_BIT);
+
+ if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
+ max310x_port_update(port, MAX310X_MODE2_REG,
+ MAX310X_MODE2_ECHOSUPR_BIT,
+ MAX310X_MODE2_ECHOSUPR_BIT);
+ }
+
/* Configure flow control levels */
/* Flow control halt level 96, resume level 48 */
max310x_port_write(port, MAX310X_FLOWLVL_REG,
@@ -1200,8 +1262,7 @@
return PTR_ERR(regmap);
/* Alloc port structure */
- s = devm_kzalloc(dev, sizeof(*s) +
- sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL);
+ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr), GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
@@ -1261,16 +1322,13 @@
MAX310X_BRGDIVLSB_REG + offs, &ret);
} while (ret != 0x01);
- regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs,
- MAX310X_MODE1_AUTOSLEEP_BIT,
- MAX310X_MODE1_AUTOSLEEP_BIT);
+ regmap_write(s->regmap, MAX310X_MODE1_REG + offs,
+ devtype->mode1);
}
uartclk = max310x_set_ref_clk(dev, s, freq, xtal);
dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
- mutex_init(&s->mutex);
-
for (i = 0; i < devtype->nr; i++) {
unsigned int line;
@@ -1297,16 +1355,16 @@
max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0);
/* Clear IRQ status register */
max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG);
- /* Enable IRQ pin */
- max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG,
- MAX310X_MODE1_IRQSEL_BIT,
- MAX310X_MODE1_IRQSEL_BIT);
/* Initialize queue for start TX */
- INIT_WORK(&s->p[i].tx_work, max310x_wq_proc);
+ INIT_WORK(&s->p[i].tx_work, max310x_tx_proc);
/* Initialize queue for changing LOOPBACK mode */
INIT_WORK(&s->p[i].md_work, max310x_md_proc);
/* Initialize queue for changing RS485 mode */
INIT_WORK(&s->p[i].rs_work, max310x_rs_proc);
+ /* Initialize SPI-transfer buffers */
+ s->p[i].wr_header = (s->p[i].port.iobase + MAX310X_THR_REG) |
+ MAX310X_WRITE_BIT;
+ s->p[i].rd_header = (s->p[i].port.iobase + MAX310X_RHR_REG);
/* Register port */
ret = uart_add_one_port(&max310x_uart, &s->p[i].port);
@@ -1354,8 +1412,6 @@
}
}
- mutex_destroy(&s->mutex);
-
out_clk:
clk_disable_unprepare(s->clk);
@@ -1376,7 +1432,6 @@
s->devtype->power(&s->p[i].port, 0);
}
- mutex_destroy(&s->mutex);
clk_disable_unprepare(s->clk);
return 0;
@@ -1419,6 +1474,8 @@
if (spi->dev.of_node) {
const struct of_device_id *of_id =
of_match_device(max310x_dt_ids, &spi->dev);
+ if (!of_id)
+ return -ENODEV;
devtype = (struct max310x_devtype *)of_id->data;
} else {
@@ -1470,10 +1527,10 @@
return ret;
#ifdef CONFIG_SPI_MASTER
- spi_register_driver(&max310x_spi_driver);
+ ret = spi_register_driver(&max310x_spi_driver);
#endif
- return 0;
+ return ret;
}
module_init(max310x_uart_init);
diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c
index ef89534..e5d3eba 100644
--- a/drivers/tty/serial/men_z135_uart.c
+++ b/drivers/tty/serial/men_z135_uart.c
@@ -353,7 +353,6 @@
memcpy_toio(port->membase + MEN_Z135_TX_RAM, &xmit->buf[xmit->tail], n);
xmit->tail = (xmit->tail + n) & (UART_XMIT_SIZE - 1);
- mmiowb();
iowrite32(n & 0x3ff, port->membase + MEN_Z135_TX_CTRL);
diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c
index 8a84259..fbc5bc0 100644
--- a/drivers/tty/serial/meson_uart.c
+++ b/drivers/tty/serial/meson_uart.c
@@ -72,7 +72,8 @@
#define AML_UART_BAUD_USE BIT(23)
#define AML_UART_BAUD_XTAL BIT(24)
-#define AML_UART_PORT_NUM 6
+#define AML_UART_PORT_NUM 12
+#define AML_UART_PORT_OFFSET 6
#define AML_UART_DEV_NAME "ttyAML"
@@ -654,10 +655,20 @@
struct resource *res_mem, *res_irq;
struct uart_port *port;
int ret = 0;
+ int id = -1;
if (pdev->dev.of_node)
pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (pdev->id < 0) {
+ for (id = AML_UART_PORT_OFFSET; id < AML_UART_PORT_NUM; id++) {
+ if (!meson_ports[id]) {
+ pdev->id = id;
+ break;
+ }
+ }
+ }
+
if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM)
return -EINVAL;
diff --git a/drivers/tty/serial/milbeaut_usio.c b/drivers/tty/serial/milbeaut_usio.c
new file mode 100644
index 0000000..949ab7e
--- /dev/null
+++ b/drivers/tty/serial/milbeaut_usio.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Socionext Inc.
+ */
+
+#if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define USIO_NAME "mlb-usio-uart"
+#define USIO_UART_DEV_NAME "ttyUSI"
+
+static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];
+
+#define RX 0
+#define TX 1
+static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];
+
+#define MLB_USIO_REG_SMR 0
+#define MLB_USIO_REG_SCR 1
+#define MLB_USIO_REG_ESCR 2
+#define MLB_USIO_REG_SSR 3
+#define MLB_USIO_REG_DR 4
+#define MLB_USIO_REG_BGR 6
+#define MLB_USIO_REG_FCR 12
+#define MLB_USIO_REG_FBYTE 14
+
+#define MLB_USIO_SMR_SOE BIT(0)
+#define MLB_USIO_SMR_SBL BIT(3)
+#define MLB_USIO_SCR_TXE BIT(0)
+#define MLB_USIO_SCR_RXE BIT(1)
+#define MLB_USIO_SCR_TBIE BIT(2)
+#define MLB_USIO_SCR_TIE BIT(3)
+#define MLB_USIO_SCR_RIE BIT(4)
+#define MLB_USIO_SCR_UPCL BIT(7)
+#define MLB_USIO_ESCR_L_8BIT 0
+#define MLB_USIO_ESCR_L_5BIT 1
+#define MLB_USIO_ESCR_L_6BIT 2
+#define MLB_USIO_ESCR_L_7BIT 3
+#define MLB_USIO_ESCR_P BIT(3)
+#define MLB_USIO_ESCR_PEN BIT(4)
+#define MLB_USIO_ESCR_FLWEN BIT(7)
+#define MLB_USIO_SSR_TBI BIT(0)
+#define MLB_USIO_SSR_TDRE BIT(1)
+#define MLB_USIO_SSR_RDRF BIT(2)
+#define MLB_USIO_SSR_ORE BIT(3)
+#define MLB_USIO_SSR_FRE BIT(4)
+#define MLB_USIO_SSR_PE BIT(5)
+#define MLB_USIO_SSR_REC BIT(7)
+#define MLB_USIO_SSR_BRK BIT(8)
+#define MLB_USIO_FCR_FE1 BIT(0)
+#define MLB_USIO_FCR_FE2 BIT(1)
+#define MLB_USIO_FCR_FCL1 BIT(2)
+#define MLB_USIO_FCR_FCL2 BIT(3)
+#define MLB_USIO_FCR_FSET BIT(4)
+#define MLB_USIO_FCR_FTIE BIT(9)
+#define MLB_USIO_FCR_FDRQ BIT(10)
+#define MLB_USIO_FCR_FRIIE BIT(11)
+
+static void mlb_usio_stop_tx(struct uart_port *port)
+{
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_tx_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ int count;
+
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) &
+ ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (port->x_char) {
+ writew(port->x_char, port->membase + MLB_USIO_REG_DR);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ mlb_usio_stop_tx(port);
+ return;
+ }
+
+ count = port->fifosize -
+ (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
+
+ do {
+ writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
+
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+
+ } while (--count > 0);
+
+ writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
+ port->membase + MLB_USIO_REG_FCR);
+
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit))
+ mlb_usio_stop_tx(port);
+}
+
+static void mlb_usio_start_tx(struct uart_port *port)
+{
+ u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);
+
+ writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);
+ if (!(fcr & MLB_USIO_FCR_FDRQ))
+ return;
+
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
+ port->membase + MLB_USIO_REG_SCR);
+
+ if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
+ mlb_usio_tx_chars(port);
+}
+
+static void mlb_usio_stop_rx(struct uart_port *port)
+{
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_enable_ms(struct uart_port *port)
+{
+ writeb(readb(port->membase + MLB_USIO_REG_SCR) |
+ MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,
+ port->membase + MLB_USIO_REG_SCR);
+}
+
+static void mlb_usio_rx_chars(struct uart_port *port)
+{
+ struct tty_port *ttyport = &port->state->port;
+ unsigned long flag = 0;
+ char ch = 0;
+ u8 status;
+ int max_count = 2;
+
+ while (max_count--) {
+ status = readb(port->membase + MLB_USIO_REG_SSR);
+
+ if (!(status & MLB_USIO_SSR_RDRF))
+ break;
+
+ if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |
+ MLB_USIO_SSR_PE))) {
+ ch = readw(port->membase + MLB_USIO_REG_DR);
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+ uart_insert_char(port, status, MLB_USIO_SSR_ORE,
+ ch, flag);
+ continue;
+ }
+ if (status & MLB_USIO_SSR_PE)
+ port->icount.parity++;
+ if (status & MLB_USIO_SSR_ORE)
+ port->icount.overrun++;
+ status &= port->read_status_mask;
+ if (status & MLB_USIO_SSR_BRK) {
+ flag = TTY_BREAK;
+ ch = 0;
+ } else
+ if (status & MLB_USIO_SSR_PE) {
+ flag = TTY_PARITY;
+ ch = 0;
+ } else
+ if (status & MLB_USIO_SSR_FRE) {
+ flag = TTY_FRAME;
+ ch = 0;
+ }
+ if (flag)
+ uart_insert_char(port, status, MLB_USIO_SSR_ORE,
+ ch, flag);
+
+ writeb(readb(port->membase + MLB_USIO_REG_SSR) |
+ MLB_USIO_SSR_REC,
+ port->membase + MLB_USIO_REG_SSR);
+
+ max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;
+ writew(readw(port->membase + MLB_USIO_REG_FCR) |
+ MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ }
+
+ tty_flip_buffer_push(ttyport);
+}
+
+static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+
+ spin_lock(&port->lock);
+ mlb_usio_rx_chars(port);
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+
+ spin_lock(&port->lock);
+ if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
+ mlb_usio_tx_chars(port);
+ spin_unlock(&port->lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int mlb_usio_tx_empty(struct uart_port *port)
+{
+ return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?
+ TIOCSER_TEMT : 0;
+}
+
+static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int mlb_usio_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+
+}
+
+static void mlb_usio_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int mlb_usio_startup(struct uart_port *port)
+{
+ const char *portname = to_platform_device(port->dev)->name;
+ unsigned long flags;
+ int ret, index = port->line;
+ unsigned char escr;
+
+ ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,
+ 0, portname, port);
+ if (ret)
+ return ret;
+ ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,
+ 0, portname, port);
+ if (ret) {
+ free_irq(mlb_usio_irq[index][RX], port);
+ return ret;
+ }
+
+ escr = readb(port->membase + MLB_USIO_REG_ESCR);
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
+ escr |= MLB_USIO_ESCR_FLWEN;
+ spin_lock_irqsave(&port->lock, flags);
+ writeb(0, port->membase + MLB_USIO_REG_SCR);
+ writeb(escr, port->membase + MLB_USIO_REG_ESCR);
+ writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(0, port->membase + MLB_USIO_REG_FBYTE);
+ writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
+
+ writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |
+ MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void mlb_usio_shutdown(struct uart_port *port)
+{
+ int index = port->line;
+
+ free_irq(mlb_usio_irq[index][RX], port);
+ free_irq(mlb_usio_irq[index][TX], port);
+}
+
+static void mlb_usio_set_termios(struct uart_port *port,
+ struct ktermios *termios, struct ktermios *old)
+{
+ unsigned int escr, smr = MLB_USIO_SMR_SOE;
+ unsigned long flags, baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ escr = MLB_USIO_ESCR_L_5BIT;
+ break;
+ case CS6:
+ escr = MLB_USIO_ESCR_L_6BIT;
+ break;
+ case CS7:
+ escr = MLB_USIO_ESCR_L_7BIT;
+ break;
+ case CS8:
+ default:
+ escr = MLB_USIO_ESCR_L_8BIT;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ smr |= MLB_USIO_SMR_SBL;
+
+ if (termios->c_cflag & PARENB) {
+ escr |= MLB_USIO_ESCR_PEN;
+ if (termios->c_cflag & PARODD)
+ escr |= MLB_USIO_ESCR_P;
+ }
+ /* Set hard flow control */
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||
+ (termios->c_cflag & CRTSCTS))
+ escr |= MLB_USIO_ESCR_FLWEN;
+
+ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
+ if (baud > 1)
+ quot = port->uartclk / baud - 1;
+ else
+ quot = 0;
+
+ spin_lock_irqsave(&port->lock, flags);
+ uart_update_timeout(port, termios->c_cflag, baud);
+ port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |
+ MLB_USIO_SSR_TDRE;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
+
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
+ if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))
+ port->ignore_status_mask |= MLB_USIO_SSR_ORE;
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= MLB_USIO_SSR_RDRF;
+
+ writeb(0, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
+ writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writeb(smr, port->membase + MLB_USIO_REG_SMR);
+ writeb(escr, port->membase + MLB_USIO_REG_ESCR);
+ writew(quot, port->membase + MLB_USIO_REG_BGR);
+ writew(0, port->membase + MLB_USIO_REG_FCR);
+ writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |
+ MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
+ port->membase + MLB_USIO_REG_FCR);
+ writew(0, port->membase + MLB_USIO_REG_FBYTE);
+ writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
+ writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |
+ MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *mlb_usio_type(struct uart_port *port)
+{
+ return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);
+}
+
+static void mlb_usio_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_MLB_USIO;
+}
+
+static const struct uart_ops mlb_usio_ops = {
+ .tx_empty = mlb_usio_tx_empty,
+ .set_mctrl = mlb_usio_set_mctrl,
+ .get_mctrl = mlb_usio_get_mctrl,
+ .stop_tx = mlb_usio_stop_tx,
+ .start_tx = mlb_usio_start_tx,
+ .stop_rx = mlb_usio_stop_rx,
+ .enable_ms = mlb_usio_enable_ms,
+ .break_ctl = mlb_usio_break_ctl,
+ .startup = mlb_usio_startup,
+ .shutdown = mlb_usio_shutdown,
+ .set_termios = mlb_usio_set_termios,
+ .type = mlb_usio_type,
+ .config_port = mlb_usio_config_port,
+};
+
+#ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
+
+static void mlb_usio_console_putchar(struct uart_port *port, int c)
+{
+ while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))
+ cpu_relax();
+
+ writew(c, port->membase + MLB_USIO_REG_DR);
+}
+
+static void mlb_usio_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct uart_port *port = &mlb_usio_ports[co->index];
+
+ uart_console_write(port, s, count, mlb_usio_console_putchar);
+}
+
+static int __init mlb_usio_console_setup(struct console *co, char *options)
+{
+ struct uart_port *port;
+ int baud = 115200;
+ int parity = 'n';
+ int flow = 'n';
+ int bits = 8;
+
+ if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)
+ return -ENODEV;
+
+ port = &mlb_usio_ports[co->index];
+ if (!port->membase)
+ return -ENODEV;
+
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
+ flow = 'r';
+
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+
+static struct uart_driver mlb_usio_uart_driver;
+static struct console mlb_usio_console = {
+ .name = USIO_UART_DEV_NAME,
+ .write = mlb_usio_console_write,
+ .device = uart_console_device,
+ .setup = mlb_usio_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &mlb_usio_uart_driver,
+};
+
+static int __init mlb_usio_console_init(void)
+{
+ register_console(&mlb_usio_console);
+ return 0;
+}
+console_initcall(mlb_usio_console_init);
+
+
+static void mlb_usio_early_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct earlycon_device *dev = co->data;
+
+ uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);
+}
+
+static int __init mlb_usio_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+ device->con->write = mlb_usio_early_console_write;
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",
+ mlb_usio_early_console_setup);
+
+#define USIO_CONSOLE (&mlb_usio_console)
+#else
+#define USIO_CONSOLE NULL
+#endif
+
+static struct uart_driver mlb_usio_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = USIO_NAME,
+ .dev_name = USIO_UART_DEV_NAME,
+ .cons = USIO_CONSOLE,
+ .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS,
+};
+
+static int mlb_usio_probe(struct platform_device *pdev)
+{
+ struct clk *clk = devm_clk_get(&pdev->dev, NULL);
+ struct uart_port *port;
+ struct resource *res;
+ int index = 0;
+ int ret;
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Missing clock\n");
+ return PTR_ERR(clk);
+ }
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);
+ return ret;
+ }
+ of_property_read_u32(pdev->dev.of_node, "index", &index);
+ port = &mlb_usio_ports[index];
+
+ port->private_data = (void *)clk;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Missing regs\n");
+ ret = -ENODEV;
+ goto failed;
+ }
+ port->membase = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+
+ ret = platform_get_irq_byname(pdev, "rx");
+ mlb_usio_irq[index][RX] = ret;
+
+ ret = platform_get_irq_byname(pdev, "tx");
+ mlb_usio_irq[index][TX] = ret;
+
+ port->irq = mlb_usio_irq[index][RX];
+ port->uartclk = clk_get_rate(clk);
+ port->fifosize = 128;
+ port->iotype = UPIO_MEM32;
+ port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;
+ port->line = index;
+ port->ops = &mlb_usio_ops;
+ port->dev = &pdev->dev;
+
+ ret = uart_add_one_port(&mlb_usio_uart_driver, port);
+ if (ret) {
+ dev_err(&pdev->dev, "Adding port failed: %d\n", ret);
+ goto failed;
+ }
+ return 0;
+
+failed:
+ clk_disable_unprepare(clk);
+
+ return ret;
+}
+
+static int mlb_usio_remove(struct platform_device *pdev)
+{
+ struct uart_port *port = &mlb_usio_ports[pdev->id];
+ struct clk *clk = port->private_data;
+
+ uart_remove_one_port(&mlb_usio_uart_driver, port);
+ clk_disable_unprepare(clk);
+
+ return 0;
+}
+
+static const struct of_device_id mlb_usio_dt_ids[] = {
+ { .compatible = "socionext,milbeaut-usio-uart" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);
+
+static struct platform_driver mlb_usio_driver = {
+ .probe = mlb_usio_probe,
+ .remove = mlb_usio_remove,
+ .driver = {
+ .name = USIO_NAME,
+ .of_match_table = mlb_usio_dt_ids,
+ },
+};
+
+static int __init mlb_usio_init(void)
+{
+ int ret = uart_register_driver(&mlb_usio_uart_driver);
+
+ if (ret) {
+ pr_err("%s: uart registration failed: %d\n", __func__, ret);
+ return ret;
+ }
+ ret = platform_driver_register(&mlb_usio_driver);
+ if (ret) {
+ uart_unregister_driver(&mlb_usio_uart_driver);
+ pr_err("%s: drv registration failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit mlb_usio_exit(void)
+{
+ platform_driver_unregister(&mlb_usio_driver);
+ uart_unregister_driver(&mlb_usio_uart_driver);
+}
+
+module_init(mlb_usio_init);
+module_exit(mlb_usio_exit);
+
+MODULE_AUTHOR("SOCIONEXT");
+MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/mps2-uart.c b/drivers/tty/serial/mps2-uart.c
index 9f8f637..587b42f 100644
--- a/drivers/tty/serial/mps2-uart.c
+++ b/drivers/tty/serial/mps2-uart.c
@@ -22,6 +22,7 @@
#include <linux/serial_core.h>
#include <linux/tty_flip.h>
#include <linux/types.h>
+#include <linux/idr.h>
#define SERIAL_NAME "ttyMPS"
#define DRIVER_NAME "mps2-uart"
@@ -65,11 +66,14 @@
#define MPS2_MAX_PORTS 3
+#define UART_PORT_COMBINED_IRQ BIT(0)
+
struct mps2_uart_port {
struct uart_port port;
struct clk *clk;
unsigned int tx_irq;
unsigned int rx_irq;
+ unsigned int flags;
};
static inline struct mps2_uart_port *to_mps2_port(struct uart_port *port)
@@ -264,6 +268,20 @@
return handled;
}
+static irqreturn_t mps2_uart_combinedirq(int irq, void *data)
+{
+ if (mps2_uart_rxirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ if (mps2_uart_txirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ if (mps2_uart_oerrirq(irq, data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
static int mps2_uart_startup(struct uart_port *port)
{
struct mps2_uart_port *mps_port = to_mps2_port(port);
@@ -274,26 +292,37 @@
mps2_uart_write8(port, control, UARTn_CTRL);
- ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
- MAKE_NAME(-rx), mps_port);
- if (ret) {
- dev_err(port->dev, "failed to register rxirq (%d)\n", ret);
- return ret;
- }
+ if (mps_port->flags & UART_PORT_COMBINED_IRQ) {
+ ret = request_irq(port->irq, mps2_uart_combinedirq, 0,
+ MAKE_NAME(-combined), mps_port);
- ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
- MAKE_NAME(-tx), mps_port);
- if (ret) {
- dev_err(port->dev, "failed to register txirq (%d)\n", ret);
- goto err_free_rxirq;
- }
+ if (ret) {
+ dev_err(port->dev, "failed to register combinedirq (%d)\n", ret);
+ return ret;
+ }
+ } else {
+ ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
+ MAKE_NAME(-overrun), mps_port);
- ret = request_irq(port->irq, mps2_uart_oerrirq, IRQF_SHARED,
- MAKE_NAME(-overrun), mps_port);
+ if (ret) {
+ dev_err(port->dev, "failed to register oerrirq (%d)\n", ret);
+ return ret;
+ }
- if (ret) {
- dev_err(port->dev, "failed to register oerrirq (%d)\n", ret);
- goto err_free_txirq;
+ ret = request_irq(mps_port->rx_irq, mps2_uart_rxirq, 0,
+ MAKE_NAME(-rx), mps_port);
+ if (ret) {
+ dev_err(port->dev, "failed to register rxirq (%d)\n", ret);
+ goto err_free_oerrirq;
+ }
+
+ ret = request_irq(mps_port->tx_irq, mps2_uart_txirq, 0,
+ MAKE_NAME(-tx), mps_port);
+ if (ret) {
+ dev_err(port->dev, "failed to register txirq (%d)\n", ret);
+ goto err_free_rxirq;
+ }
+
}
control |= UARTn_CTRL_RX_GRP | UARTn_CTRL_TX_GRP;
@@ -302,10 +331,10 @@
return 0;
-err_free_txirq:
- free_irq(mps_port->tx_irq, mps_port);
err_free_rxirq:
free_irq(mps_port->rx_irq, mps_port);
+err_free_oerrirq:
+ free_irq(port->irq, mps_port);
return ret;
}
@@ -319,8 +348,11 @@
mps2_uart_write8(port, control, UARTn_CTRL);
- free_irq(mps_port->rx_irq, mps_port);
- free_irq(mps_port->tx_irq, mps_port);
+ if (!(mps_port->flags & UART_PORT_COMBINED_IRQ)) {
+ free_irq(mps_port->rx_irq, mps_port);
+ free_irq(mps_port->tx_irq, mps_port);
+ }
+
free_irq(port->irq, mps_port);
}
@@ -397,7 +429,7 @@
.verify_port = mps2_uart_verify_port,
};
-static struct mps2_uart_port mps2_uart_ports[MPS2_MAX_PORTS];
+static DEFINE_IDR(ports_idr);
#ifdef CONFIG_SERIAL_MPS2_UART_CONSOLE
static void mps2_uart_console_putchar(struct uart_port *port, int ch)
@@ -410,7 +442,8 @@
static void mps2_uart_console_write(struct console *co, const char *s, unsigned int cnt)
{
- struct uart_port *port = &mps2_uart_ports[co->index].port;
+ struct mps2_uart_port *mps_port = idr_find(&ports_idr, co->index);
+ struct uart_port *port = &mps_port->port;
uart_console_write(port, s, cnt, mps2_uart_console_putchar);
}
@@ -426,7 +459,10 @@
if (co->index < 0 || co->index >= MPS2_MAX_PORTS)
return -ENODEV;
- mps_port = &mps2_uart_ports[co->index];
+ mps_port = idr_find(&ports_idr, co->index);
+
+ if (!mps_port)
+ return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -487,27 +523,36 @@
.cons = MPS2_SERIAL_CONSOLE,
};
-static struct mps2_uart_port *mps2_of_get_port(struct platform_device *pdev)
+static int mps2_of_get_port(struct platform_device *pdev,
+ struct mps2_uart_port *mps_port)
{
struct device_node *np = pdev->dev.of_node;
int id;
if (!np)
- return NULL;
+ return -ENODEV;
id = of_alias_get_id(np, "serial");
+
if (id < 0)
- id = 0;
+ id = idr_alloc_cyclic(&ports_idr, (void *)mps_port, 0, MPS2_MAX_PORTS, GFP_KERNEL);
+ else
+ id = idr_alloc(&ports_idr, (void *)mps_port, id, MPS2_MAX_PORTS, GFP_KERNEL);
- if (WARN_ON(id >= MPS2_MAX_PORTS))
- return NULL;
+ if (id < 0)
+ return id;
- mps2_uart_ports[id].port.line = id;
- return &mps2_uart_ports[id];
+ /* Only combined irq is presesnt */
+ if (platform_irq_count(pdev) == 1)
+ mps_port->flags |= UART_PORT_COMBINED_IRQ;
+
+ mps_port->port.line = id;
+
+ return 0;
}
-static int mps2_init_port(struct mps2_uart_port *mps_port,
- struct platform_device *pdev)
+static int mps2_init_port(struct platform_device *pdev,
+ struct mps2_uart_port *mps_port)
{
struct resource *res;
int ret;
@@ -519,11 +564,6 @@
mps_port->port.mapbase = res->start;
mps_port->port.mapsize = resource_size(res);
-
- mps_port->rx_irq = platform_get_irq(pdev, 0);
- mps_port->tx_irq = platform_get_irq(pdev, 1);
- mps_port->port.irq = platform_get_irq(pdev, 2);
-
mps_port->port.iotype = UPIO_MEM;
mps_port->port.flags = UPF_BOOT_AUTOCONF;
mps_port->port.fifosize = 1;
@@ -542,6 +582,15 @@
clk_disable_unprepare(mps_port->clk);
+
+ if (mps_port->flags & UART_PORT_COMBINED_IRQ) {
+ mps_port->port.irq = platform_get_irq(pdev, 0);
+ } else {
+ mps_port->rx_irq = platform_get_irq(pdev, 0);
+ mps_port->tx_irq = platform_get_irq(pdev, 1);
+ mps_port->port.irq = platform_get_irq(pdev, 2);
+ }
+
return ret;
}
@@ -550,11 +599,16 @@
struct mps2_uart_port *mps_port;
int ret;
- mps_port = mps2_of_get_port(pdev);
- if (!mps_port)
- return -ENODEV;
+ mps_port = devm_kzalloc(&pdev->dev, sizeof(struct mps2_uart_port), GFP_KERNEL);
- ret = mps2_init_port(mps_port, pdev);
+ if (!mps_port)
+ return -ENOMEM;
+
+ ret = mps2_of_get_port(pdev, mps_port);
+ if (ret)
+ return ret;
+
+ ret = mps2_init_port(pdev, mps_port);
if (ret)
return ret;
diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c
deleted file mode 100644
index 1f60d6f..0000000
--- a/drivers/tty/serial/mpsc.c
+++ /dev/null
@@ -1,2138 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240,
- * GT64260, MV64340, MV64360, GT96100, ... ).
- *
- * Author: Mark A. Greer <mgreer@mvista.com>
- *
- * Based on an old MPSC driver that was in the linuxppc tree. It appears to
- * have been created by Chris Zankel (formerly of MontaVista) but there
- * is no proper Copyright so I'm not sure. Apparently, parts were also
- * taken from PPCBoot (now U-Boot). Also based on drivers/serial/8250.c
- * by Russell King.
- *
- * 2004 (c) MontaVista, Software, Inc.
- */
-/*
- * The MPSC interface is much like a typical network controller's interface.
- * That is, you set up separate rings of descriptors for transmitting and
- * receiving data. There is also a pool of buffers with (one buffer per
- * descriptor) that incoming data are dma'd into or outgoing data are dma'd
- * out of.
- *
- * The MPSC requires two other controllers to be able to work. The Baud Rate
- * Generator (BRG) provides a clock at programmable frequencies which determines
- * the baud rate. The Serial DMA Controller (SDMA) takes incoming data from the
- * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the
- * MPSC. It is actually the SDMA interrupt that the driver uses to keep the
- * transmit and receive "engines" going (i.e., indicate data has been
- * transmitted or received).
- *
- * NOTES:
- *
- * 1) Some chips have an erratum where several regs cannot be
- * read. To work around that, we keep a local copy of those regs in
- * 'mpsc_port_info'.
- *
- * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr
- * accesses system mem with coherency enabled. For that reason, the driver
- * assumes that coherency for that ctlr has been disabled. This means
- * that when in a cache coherent system, the driver has to manually manage
- * the data cache on the areas that it touches because the dma_* macro are
- * basically no-ops.
- *
- * 3) There is an erratum (on PPC) where you can't use the instruction to do
- * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places
- * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed.
- *
- * 4) AFAICT, hardware flow control isn't supported by the controller --MAG.
- */
-
-
-#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/serial.h>
-#include <linux/serial_core.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/mv643xx.h>
-#include <linux/platform_device.h>
-#include <linux/gfp.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#define MPSC_NUM_CTLRS 2
-
-/*
- * Descriptors and buffers must be cache line aligned.
- * Buffers lengths must be multiple of cache line size.
- * Number of Tx & Rx descriptors must be powers of 2.
- */
-#define MPSC_RXR_ENTRIES 32
-#define MPSC_RXRE_SIZE dma_get_cache_alignment()
-#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE)
-#define MPSC_RXBE_SIZE dma_get_cache_alignment()
-#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE)
-
-#define MPSC_TXR_ENTRIES 32
-#define MPSC_TXRE_SIZE dma_get_cache_alignment()
-#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE)
-#define MPSC_TXBE_SIZE dma_get_cache_alignment()
-#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE)
-
-#define MPSC_DMA_ALLOC_SIZE (MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \
- + MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */)
-
-/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */
-struct mpsc_rx_desc {
- u16 bufsize;
- u16 bytecnt;
- u32 cmdstat;
- u32 link;
- u32 buf_ptr;
-} __attribute((packed));
-
-struct mpsc_tx_desc {
- u16 bytecnt;
- u16 shadow;
- u32 cmdstat;
- u32 link;
- u32 buf_ptr;
-} __attribute((packed));
-
-/*
- * Some regs that have the erratum that you can't read them are are shared
- * between the two MPSC controllers. This struct contains those shared regs.
- */
-struct mpsc_shared_regs {
- phys_addr_t mpsc_routing_base_p;
- phys_addr_t sdma_intr_base_p;
-
- void __iomem *mpsc_routing_base;
- void __iomem *sdma_intr_base;
-
- u32 MPSC_MRR_m;
- u32 MPSC_RCRR_m;
- u32 MPSC_TCRR_m;
- u32 SDMA_INTR_CAUSE_m;
- u32 SDMA_INTR_MASK_m;
-};
-
-/* The main driver data structure */
-struct mpsc_port_info {
- struct uart_port port; /* Overlay uart_port structure */
-
- /* Internal driver state for this ctlr */
- u8 ready;
- u8 rcv_data;
-
- /* Info passed in from platform */
- u8 mirror_regs; /* Need to mirror regs? */
- u8 cache_mgmt; /* Need manual cache mgmt? */
- u8 brg_can_tune; /* BRG has baud tuning? */
- u32 brg_clk_src;
- u16 mpsc_max_idle;
- int default_baud;
- int default_bits;
- int default_parity;
- int default_flow;
-
- /* Physical addresses of various blocks of registers (from platform) */
- phys_addr_t mpsc_base_p;
- phys_addr_t sdma_base_p;
- phys_addr_t brg_base_p;
-
- /* Virtual addresses of various blocks of registers (from platform) */
- void __iomem *mpsc_base;
- void __iomem *sdma_base;
- void __iomem *brg_base;
-
- /* Descriptor ring and buffer allocations */
- void *dma_region;
- dma_addr_t dma_region_p;
-
- dma_addr_t rxr; /* Rx descriptor ring */
- dma_addr_t rxr_p; /* Phys addr of rxr */
- u8 *rxb; /* Rx Ring I/O buf */
- u8 *rxb_p; /* Phys addr of rxb */
- u32 rxr_posn; /* First desc w/ Rx data */
-
- dma_addr_t txr; /* Tx descriptor ring */
- dma_addr_t txr_p; /* Phys addr of txr */
- u8 *txb; /* Tx Ring I/O buf */
- u8 *txb_p; /* Phys addr of txb */
- int txr_head; /* Where new data goes */
- int txr_tail; /* Where sent data comes off */
- spinlock_t tx_lock; /* transmit lock */
-
- /* Mirrored values of regs we can't read (if 'mirror_regs' set) */
- u32 MPSC_MPCR_m;
- u32 MPSC_CHR_1_m;
- u32 MPSC_CHR_2_m;
- u32 MPSC_CHR_10_m;
- u32 BRG_BCR_m;
- struct mpsc_shared_regs *shared_regs;
-};
-
-/* Hooks to platform-specific code */
-int mpsc_platform_register_driver(void);
-void mpsc_platform_unregister_driver(void);
-
-/* Hooks back in to mpsc common to be called by platform-specific code */
-struct mpsc_port_info *mpsc_device_probe(int index);
-struct mpsc_port_info *mpsc_device_remove(int index);
-
-/* Main MPSC Configuration Register Offsets */
-#define MPSC_MMCRL 0x0000
-#define MPSC_MMCRH 0x0004
-#define MPSC_MPCR 0x0008
-#define MPSC_CHR_1 0x000c
-#define MPSC_CHR_2 0x0010
-#define MPSC_CHR_3 0x0014
-#define MPSC_CHR_4 0x0018
-#define MPSC_CHR_5 0x001c
-#define MPSC_CHR_6 0x0020
-#define MPSC_CHR_7 0x0024
-#define MPSC_CHR_8 0x0028
-#define MPSC_CHR_9 0x002c
-#define MPSC_CHR_10 0x0030
-#define MPSC_CHR_11 0x0034
-
-#define MPSC_MPCR_FRZ (1 << 9)
-#define MPSC_MPCR_CL_5 0
-#define MPSC_MPCR_CL_6 1
-#define MPSC_MPCR_CL_7 2
-#define MPSC_MPCR_CL_8 3
-#define MPSC_MPCR_SBL_1 0
-#define MPSC_MPCR_SBL_2 1
-
-#define MPSC_CHR_2_TEV (1<<1)
-#define MPSC_CHR_2_TA (1<<7)
-#define MPSC_CHR_2_TTCS (1<<9)
-#define MPSC_CHR_2_REV (1<<17)
-#define MPSC_CHR_2_RA (1<<23)
-#define MPSC_CHR_2_CRD (1<<25)
-#define MPSC_CHR_2_EH (1<<31)
-#define MPSC_CHR_2_PAR_ODD 0
-#define MPSC_CHR_2_PAR_SPACE 1
-#define MPSC_CHR_2_PAR_EVEN 2
-#define MPSC_CHR_2_PAR_MARK 3
-
-/* MPSC Signal Routing */
-#define MPSC_MRR 0x0000
-#define MPSC_RCRR 0x0004
-#define MPSC_TCRR 0x0008
-
-/* Serial DMA Controller Interface Registers */
-#define SDMA_SDC 0x0000
-#define SDMA_SDCM 0x0008
-#define SDMA_RX_DESC 0x0800
-#define SDMA_RX_BUF_PTR 0x0808
-#define SDMA_SCRDP 0x0810
-#define SDMA_TX_DESC 0x0c00
-#define SDMA_SCTDP 0x0c10
-#define SDMA_SFTDP 0x0c14
-
-#define SDMA_DESC_CMDSTAT_PE (1<<0)
-#define SDMA_DESC_CMDSTAT_CDL (1<<1)
-#define SDMA_DESC_CMDSTAT_FR (1<<3)
-#define SDMA_DESC_CMDSTAT_OR (1<<6)
-#define SDMA_DESC_CMDSTAT_BR (1<<9)
-#define SDMA_DESC_CMDSTAT_MI (1<<10)
-#define SDMA_DESC_CMDSTAT_A (1<<11)
-#define SDMA_DESC_CMDSTAT_AM (1<<12)
-#define SDMA_DESC_CMDSTAT_CT (1<<13)
-#define SDMA_DESC_CMDSTAT_C (1<<14)
-#define SDMA_DESC_CMDSTAT_ES (1<<15)
-#define SDMA_DESC_CMDSTAT_L (1<<16)
-#define SDMA_DESC_CMDSTAT_F (1<<17)
-#define SDMA_DESC_CMDSTAT_P (1<<18)
-#define SDMA_DESC_CMDSTAT_EI (1<<23)
-#define SDMA_DESC_CMDSTAT_O (1<<31)
-
-#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O \
- | SDMA_DESC_CMDSTAT_EI)
-
-#define SDMA_SDC_RFT (1<<0)
-#define SDMA_SDC_SFM (1<<1)
-#define SDMA_SDC_BLMR (1<<6)
-#define SDMA_SDC_BLMT (1<<7)
-#define SDMA_SDC_POVR (1<<8)
-#define SDMA_SDC_RIFB (1<<9)
-
-#define SDMA_SDCM_ERD (1<<7)
-#define SDMA_SDCM_AR (1<<15)
-#define SDMA_SDCM_STD (1<<16)
-#define SDMA_SDCM_TXD (1<<23)
-#define SDMA_SDCM_AT (1<<31)
-
-#define SDMA_0_CAUSE_RXBUF (1<<0)
-#define SDMA_0_CAUSE_RXERR (1<<1)
-#define SDMA_0_CAUSE_TXBUF (1<<2)
-#define SDMA_0_CAUSE_TXEND (1<<3)
-#define SDMA_1_CAUSE_RXBUF (1<<8)
-#define SDMA_1_CAUSE_RXERR (1<<9)
-#define SDMA_1_CAUSE_TXBUF (1<<10)
-#define SDMA_1_CAUSE_TXEND (1<<11)
-
-#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \
- | SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)
-#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \
- | SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)
-
-/* SDMA Interrupt registers */
-#define SDMA_INTR_CAUSE 0x0000
-#define SDMA_INTR_MASK 0x0080
-
-/* Baud Rate Generator Interface Registers */
-#define BRG_BCR 0x0000
-#define BRG_BTR 0x0004
-
-/*
- * Define how this driver is known to the outside (we've been assigned a
- * range on the "Low-density serial ports" major).
- */
-#define MPSC_MAJOR 204
-#define MPSC_MINOR_START 44
-#define MPSC_DRIVER_NAME "MPSC"
-#define MPSC_DEV_NAME "ttyMM"
-#define MPSC_VERSION "1.00"
-
-static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS];
-static struct mpsc_shared_regs mpsc_shared_regs;
-static struct uart_driver mpsc_reg;
-
-static void mpsc_start_rx(struct mpsc_port_info *pi);
-static void mpsc_free_ring_mem(struct mpsc_port_info *pi);
-static void mpsc_release_port(struct uart_port *port);
-/*
- ******************************************************************************
- *
- * Baud Rate Generator Routines (BRG)
- *
- ******************************************************************************
- */
-static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src)
-{
- u32 v;
-
- v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
- v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18);
-
- if (pi->brg_can_tune)
- v &= ~(1 << 25);
-
- if (pi->mirror_regs)
- pi->BRG_BCR_m = v;
- writel(v, pi->brg_base + BRG_BCR);
-
- writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000,
- pi->brg_base + BRG_BTR);
-}
-
-static void mpsc_brg_enable(struct mpsc_port_info *pi)
-{
- u32 v;
-
- v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
- v |= (1 << 16);
-
- if (pi->mirror_regs)
- pi->BRG_BCR_m = v;
- writel(v, pi->brg_base + BRG_BCR);
-}
-
-static void mpsc_brg_disable(struct mpsc_port_info *pi)
-{
- u32 v;
-
- v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
- v &= ~(1 << 16);
-
- if (pi->mirror_regs)
- pi->BRG_BCR_m = v;
- writel(v, pi->brg_base + BRG_BCR);
-}
-
-/*
- * To set the baud, we adjust the CDV field in the BRG_BCR reg.
- * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1.
- * However, the input clock is divided by 16 in the MPSC b/c of how
- * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our
- * calculation by 16 to account for that. So the real calculation
- * that accounts for the way the mpsc is set up is:
- * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1.
- */
-static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud)
-{
- u32 cdv = (pi->port.uartclk / (baud << 5)) - 1;
- u32 v;
-
- mpsc_brg_disable(pi);
- v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
- v = (v & 0xffff0000) | (cdv & 0xffff);
-
- if (pi->mirror_regs)
- pi->BRG_BCR_m = v;
- writel(v, pi->brg_base + BRG_BCR);
- mpsc_brg_enable(pi);
-}
-
-/*
- ******************************************************************************
- *
- * Serial DMA Routines (SDMA)
- *
- ******************************************************************************
- */
-
-static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size)
-{
- u32 v;
-
- pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n",
- pi->port.line, burst_size);
-
- burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */
-
- if (burst_size < 2)
- v = 0x0; /* 1 64-bit word */
- else if (burst_size < 4)
- v = 0x1; /* 2 64-bit words */
- else if (burst_size < 8)
- v = 0x2; /* 4 64-bit words */
- else
- v = 0x3; /* 8 64-bit words */
-
- writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12),
- pi->sdma_base + SDMA_SDC);
-}
-
-static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size)
-{
- pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line,
- burst_size);
-
- writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f,
- pi->sdma_base + SDMA_SDC);
- mpsc_sdma_burstsize(pi, burst_size);
-}
-
-static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask)
-{
- u32 old, v;
-
- pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask);
-
- old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m :
- readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
-
- mask &= 0xf;
- if (pi->port.line)
- mask <<= 8;
- v &= ~mask;
-
- if (pi->mirror_regs)
- pi->shared_regs->SDMA_INTR_MASK_m = v;
- writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
-
- if (pi->port.line)
- old >>= 8;
- return old & 0xf;
-}
-
-static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask)
-{
- u32 v;
-
- pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask);
-
- v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m
- : readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
-
- mask &= 0xf;
- if (pi->port.line)
- mask <<= 8;
- v |= mask;
-
- if (pi->mirror_regs)
- pi->shared_regs->SDMA_INTR_MASK_m = v;
- writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
-}
-
-static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line);
-
- if (pi->mirror_regs)
- pi->shared_regs->SDMA_INTR_CAUSE_m = 0;
- writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE
- + pi->port.line);
-}
-
-static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi,
- struct mpsc_rx_desc *rxre_p)
-{
- pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n",
- pi->port.line, (u32)rxre_p);
-
- writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP);
-}
-
-static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi,
- struct mpsc_tx_desc *txre_p)
-{
- writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP);
- writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP);
-}
-
-static void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val)
-{
- u32 v;
-
- v = readl(pi->sdma_base + SDMA_SDCM);
- if (val)
- v |= val;
- else
- v = 0;
- wmb();
- writel(v, pi->sdma_base + SDMA_SDCM);
- wmb();
-}
-
-static uint mpsc_sdma_tx_active(struct mpsc_port_info *pi)
-{
- return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD;
-}
-
-static void mpsc_sdma_start_tx(struct mpsc_port_info *pi)
-{
- struct mpsc_tx_desc *txre, *txre_p;
-
- /* If tx isn't running & there's a desc ready to go, start it */
- if (!mpsc_sdma_tx_active(pi)) {
- txre = (struct mpsc_tx_desc *)(pi->txr
- + (pi->txr_tail * MPSC_TXRE_SIZE));
- dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
- DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)txre,
- (ulong)txre + MPSC_TXRE_SIZE);
-#endif
-
- if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) {
- txre_p = (struct mpsc_tx_desc *)
- (pi->txr_p + (pi->txr_tail * MPSC_TXRE_SIZE));
-
- mpsc_sdma_set_tx_ring(pi, txre_p);
- mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD);
- }
- }
-}
-
-static void mpsc_sdma_stop(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line);
-
- /* Abort any SDMA transfers */
- mpsc_sdma_cmd(pi, 0);
- mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT);
-
- /* Clear the SDMA current and first TX and RX pointers */
- mpsc_sdma_set_tx_ring(pi, NULL);
- mpsc_sdma_set_rx_ring(pi, NULL);
-
- /* Disable interrupts */
- mpsc_sdma_intr_mask(pi, 0xf);
- mpsc_sdma_intr_ack(pi);
-}
-
-/*
- ******************************************************************************
- *
- * Multi-Protocol Serial Controller Routines (MPSC)
- *
- ******************************************************************************
- */
-
-static void mpsc_hw_init(struct mpsc_port_info *pi)
-{
- u32 v;
-
- pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line);
-
- /* Set up clock routing */
- if (pi->mirror_regs) {
- v = pi->shared_regs->MPSC_MRR_m;
- v &= ~0x1c7;
- pi->shared_regs->MPSC_MRR_m = v;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
-
- v = pi->shared_regs->MPSC_RCRR_m;
- v = (v & ~0xf0f) | 0x100;
- pi->shared_regs->MPSC_RCRR_m = v;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
-
- v = pi->shared_regs->MPSC_TCRR_m;
- v = (v & ~0xf0f) | 0x100;
- pi->shared_regs->MPSC_TCRR_m = v;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
- } else {
- v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR);
- v &= ~0x1c7;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
-
- v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
- v = (v & ~0xf0f) | 0x100;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
-
- v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
- v = (v & ~0xf0f) | 0x100;
- writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
- }
-
- /* Put MPSC in UART mode & enabel Tx/Rx egines */
- writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL);
-
- /* No preamble, 16x divider, low-latency, */
- writel(0x04400400, pi->mpsc_base + MPSC_MMCRH);
- mpsc_set_baudrate(pi, pi->default_baud);
-
- if (pi->mirror_regs) {
- pi->MPSC_CHR_1_m = 0;
- pi->MPSC_CHR_2_m = 0;
- }
- writel(0, pi->mpsc_base + MPSC_CHR_1);
- writel(0, pi->mpsc_base + MPSC_CHR_2);
- writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3);
- writel(0, pi->mpsc_base + MPSC_CHR_4);
- writel(0, pi->mpsc_base + MPSC_CHR_5);
- writel(0, pi->mpsc_base + MPSC_CHR_6);
- writel(0, pi->mpsc_base + MPSC_CHR_7);
- writel(0, pi->mpsc_base + MPSC_CHR_8);
- writel(0, pi->mpsc_base + MPSC_CHR_9);
- writel(0, pi->mpsc_base + MPSC_CHR_10);
-}
-
-static void mpsc_enter_hunt(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line);
-
- if (pi->mirror_regs) {
- writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH,
- pi->mpsc_base + MPSC_CHR_2);
- /* Erratum prevents reading CHR_2 so just delay for a while */
- udelay(100);
- } else {
- writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH,
- pi->mpsc_base + MPSC_CHR_2);
-
- while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH)
- udelay(10);
- }
-}
-
-static void mpsc_freeze(struct mpsc_port_info *pi)
-{
- u32 v;
-
- pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line);
-
- v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
- readl(pi->mpsc_base + MPSC_MPCR);
- v |= MPSC_MPCR_FRZ;
-
- if (pi->mirror_regs)
- pi->MPSC_MPCR_m = v;
- writel(v, pi->mpsc_base + MPSC_MPCR);
-}
-
-static void mpsc_unfreeze(struct mpsc_port_info *pi)
-{
- u32 v;
-
- v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
- readl(pi->mpsc_base + MPSC_MPCR);
- v &= ~MPSC_MPCR_FRZ;
-
- if (pi->mirror_regs)
- pi->MPSC_MPCR_m = v;
- writel(v, pi->mpsc_base + MPSC_MPCR);
-
- pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line);
-}
-
-static void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len)
-{
- u32 v;
-
- pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len);
-
- v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
- readl(pi->mpsc_base + MPSC_MPCR);
- v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12);
-
- if (pi->mirror_regs)
- pi->MPSC_MPCR_m = v;
- writel(v, pi->mpsc_base + MPSC_MPCR);
-}
-
-static void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len)
-{
- u32 v;
-
- pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n",
- pi->port.line, len);
-
- v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
- readl(pi->mpsc_base + MPSC_MPCR);
-
- v = (v & ~(1 << 14)) | ((len & 0x1) << 14);
-
- if (pi->mirror_regs)
- pi->MPSC_MPCR_m = v;
- writel(v, pi->mpsc_base + MPSC_MPCR);
-}
-
-static void mpsc_set_parity(struct mpsc_port_info *pi, u32 p)
-{
- u32 v;
-
- pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p);
-
- v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m :
- readl(pi->mpsc_base + MPSC_CHR_2);
-
- p &= 0x3;
- v = (v & ~0xc000c) | (p << 18) | (p << 2);
-
- if (pi->mirror_regs)
- pi->MPSC_CHR_2_m = v;
- writel(v, pi->mpsc_base + MPSC_CHR_2);
-}
-
-/*
- ******************************************************************************
- *
- * Driver Init Routines
- *
- ******************************************************************************
- */
-
-static void mpsc_init_hw(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line);
-
- mpsc_brg_init(pi, pi->brg_clk_src);
- mpsc_brg_enable(pi);
- mpsc_sdma_init(pi, dma_get_cache_alignment()); /* burst a cacheline */
- mpsc_sdma_stop(pi);
- mpsc_hw_init(pi);
-}
-
-static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi)
-{
- int rc = 0;
-
- pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n",
- pi->port.line);
-
- if (!pi->dma_region) {
- if (!dma_set_mask(pi->port.dev, 0xffffffff)) {
- printk(KERN_ERR "MPSC: Inadequate DMA support\n");
- rc = -ENXIO;
- } else if ((pi->dma_region = dma_alloc_attrs(pi->port.dev,
- MPSC_DMA_ALLOC_SIZE,
- &pi->dma_region_p, GFP_KERNEL,
- DMA_ATTR_NON_CONSISTENT))
- == NULL) {
- printk(KERN_ERR "MPSC: Can't alloc Desc region\n");
- rc = -ENOMEM;
- }
- }
-
- return rc;
-}
-
-static void mpsc_free_ring_mem(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line);
-
- if (pi->dma_region) {
- dma_free_attrs(pi->port.dev, MPSC_DMA_ALLOC_SIZE,
- pi->dma_region, pi->dma_region_p,
- DMA_ATTR_NON_CONSISTENT);
- pi->dma_region = NULL;
- pi->dma_region_p = (dma_addr_t)NULL;
- }
-}
-
-static void mpsc_init_rings(struct mpsc_port_info *pi)
-{
- struct mpsc_rx_desc *rxre;
- struct mpsc_tx_desc *txre;
- dma_addr_t dp, dp_p;
- u8 *bp, *bp_p;
- int i;
-
- pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line);
-
- BUG_ON(pi->dma_region == NULL);
-
- memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE);
-
- /*
- * Descriptors & buffers are multiples of cacheline size and must be
- * cacheline aligned.
- */
- dp = ALIGN((u32)pi->dma_region, dma_get_cache_alignment());
- dp_p = ALIGN((u32)pi->dma_region_p, dma_get_cache_alignment());
-
- /*
- * Partition dma region into rx ring descriptor, rx buffers,
- * tx ring descriptors, and tx buffers.
- */
- pi->rxr = dp;
- pi->rxr_p = dp_p;
- dp += MPSC_RXR_SIZE;
- dp_p += MPSC_RXR_SIZE;
-
- pi->rxb = (u8 *)dp;
- pi->rxb_p = (u8 *)dp_p;
- dp += MPSC_RXB_SIZE;
- dp_p += MPSC_RXB_SIZE;
-
- pi->rxr_posn = 0;
-
- pi->txr = dp;
- pi->txr_p = dp_p;
- dp += MPSC_TXR_SIZE;
- dp_p += MPSC_TXR_SIZE;
-
- pi->txb = (u8 *)dp;
- pi->txb_p = (u8 *)dp_p;
-
- pi->txr_head = 0;
- pi->txr_tail = 0;
-
- /* Init rx ring descriptors */
- dp = pi->rxr;
- dp_p = pi->rxr_p;
- bp = pi->rxb;
- bp_p = pi->rxb_p;
-
- for (i = 0; i < MPSC_RXR_ENTRIES; i++) {
- rxre = (struct mpsc_rx_desc *)dp;
-
- rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE);
- rxre->bytecnt = cpu_to_be16(0);
- rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O
- | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F
- | SDMA_DESC_CMDSTAT_L);
- rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE);
- rxre->buf_ptr = cpu_to_be32(bp_p);
-
- dp += MPSC_RXRE_SIZE;
- dp_p += MPSC_RXRE_SIZE;
- bp += MPSC_RXBE_SIZE;
- bp_p += MPSC_RXBE_SIZE;
- }
- rxre->link = cpu_to_be32(pi->rxr_p); /* Wrap last back to first */
-
- /* Init tx ring descriptors */
- dp = pi->txr;
- dp_p = pi->txr_p;
- bp = pi->txb;
- bp_p = pi->txb_p;
-
- for (i = 0; i < MPSC_TXR_ENTRIES; i++) {
- txre = (struct mpsc_tx_desc *)dp;
-
- txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE);
- txre->buf_ptr = cpu_to_be32(bp_p);
-
- dp += MPSC_TXRE_SIZE;
- dp_p += MPSC_TXRE_SIZE;
- bp += MPSC_TXBE_SIZE;
- bp_p += MPSC_TXBE_SIZE;
- }
- txre->link = cpu_to_be32(pi->txr_p); /* Wrap last back to first */
-
- dma_cache_sync(pi->port.dev, (void *)pi->dma_region,
- MPSC_DMA_ALLOC_SIZE, DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)pi->dma_region,
- (ulong)pi->dma_region
- + MPSC_DMA_ALLOC_SIZE);
-#endif
-
- return;
-}
-
-static void mpsc_uninit_rings(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line);
-
- BUG_ON(pi->dma_region == NULL);
-
- pi->rxr = 0;
- pi->rxr_p = 0;
- pi->rxb = NULL;
- pi->rxb_p = NULL;
- pi->rxr_posn = 0;
-
- pi->txr = 0;
- pi->txr_p = 0;
- pi->txb = NULL;
- pi->txb_p = NULL;
- pi->txr_head = 0;
- pi->txr_tail = 0;
-}
-
-static int mpsc_make_ready(struct mpsc_port_info *pi)
-{
- int rc;
-
- pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line);
-
- if (!pi->ready) {
- mpsc_init_hw(pi);
- rc = mpsc_alloc_ring_mem(pi);
- if (rc)
- return rc;
- mpsc_init_rings(pi);
- pi->ready = 1;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int serial_polled;
-#endif
-
-/*
- ******************************************************************************
- *
- * Interrupt Handling Routines
- *
- ******************************************************************************
- */
-
-static int mpsc_rx_intr(struct mpsc_port_info *pi, unsigned long *flags)
-{
- struct mpsc_rx_desc *rxre;
- struct tty_port *port = &pi->port.state->port;
- u32 cmdstat, bytes_in, i;
- int rc = 0;
- u8 *bp;
- char flag = TTY_NORMAL;
-
- pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
-
- rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE));
-
- dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
- DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
-
- /*
- * Loop through Rx descriptors handling ones that have been completed.
- */
- while (!((cmdstat = be32_to_cpu(rxre->cmdstat))
- & SDMA_DESC_CMDSTAT_O)) {
- bytes_in = be16_to_cpu(rxre->bytecnt);
-#ifdef CONFIG_CONSOLE_POLL
- if (unlikely(serial_polled)) {
- serial_polled = 0;
- return 0;
- }
-#endif
- /* Following use of tty struct directly is deprecated */
- if (tty_buffer_request_room(port, bytes_in) < bytes_in) {
- if (port->low_latency) {
- spin_unlock_irqrestore(&pi->port.lock, *flags);
- tty_flip_buffer_push(port);
- spin_lock_irqsave(&pi->port.lock, *flags);
- }
- /*
- * If this failed then we will throw away the bytes
- * but must do so to clear interrupts.
- */
- }
-
- bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
- dma_cache_sync(pi->port.dev, (void *)bp, MPSC_RXBE_SIZE,
- DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)bp,
- (ulong)bp + MPSC_RXBE_SIZE);
-#endif
-
- /*
- * Other than for parity error, the manual provides little
- * info on what data will be in a frame flagged by any of
- * these errors. For parity error, it is the last byte in
- * the buffer that had the error. As for the rest, I guess
- * we'll assume there is no data in the buffer.
- * If there is...it gets lost.
- */
- if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR
- | SDMA_DESC_CMDSTAT_FR
- | SDMA_DESC_CMDSTAT_OR))) {
-
- pi->port.icount.rx++;
-
- if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */
- pi->port.icount.brk++;
-
- if (uart_handle_break(&pi->port))
- goto next_frame;
- } else if (cmdstat & SDMA_DESC_CMDSTAT_FR) {
- pi->port.icount.frame++;
- } else if (cmdstat & SDMA_DESC_CMDSTAT_OR) {
- pi->port.icount.overrun++;
- }
-
- cmdstat &= pi->port.read_status_mask;
-
- if (cmdstat & SDMA_DESC_CMDSTAT_BR)
- flag = TTY_BREAK;
- else if (cmdstat & SDMA_DESC_CMDSTAT_FR)
- flag = TTY_FRAME;
- else if (cmdstat & SDMA_DESC_CMDSTAT_OR)
- flag = TTY_OVERRUN;
- else if (cmdstat & SDMA_DESC_CMDSTAT_PE)
- flag = TTY_PARITY;
- }
-
- if (uart_handle_sysrq_char(&pi->port, *bp)) {
- bp++;
- bytes_in--;
-#ifdef CONFIG_CONSOLE_POLL
- if (unlikely(serial_polled)) {
- serial_polled = 0;
- return 0;
- }
-#endif
- goto next_frame;
- }
-
- if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR
- | SDMA_DESC_CMDSTAT_FR
- | SDMA_DESC_CMDSTAT_OR)))
- && !(cmdstat & pi->port.ignore_status_mask)) {
- tty_insert_flip_char(port, *bp, flag);
- } else {
- for (i=0; i<bytes_in; i++)
- tty_insert_flip_char(port, *bp++, TTY_NORMAL);
-
- pi->port.icount.rx += bytes_in;
- }
-
-next_frame:
- rxre->bytecnt = cpu_to_be16(0);
- wmb();
- rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O
- | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F
- | SDMA_DESC_CMDSTAT_L);
- wmb();
- dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
- DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
-
- /* Advance to next descriptor */
- pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1);
- rxre = (struct mpsc_rx_desc *)
- (pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE));
- dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
- DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
- rc = 1;
- }
-
- /* Restart rx engine, if its stopped */
- if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
- mpsc_start_rx(pi);
-
- spin_unlock_irqrestore(&pi->port.lock, *flags);
- tty_flip_buffer_push(port);
- spin_lock_irqsave(&pi->port.lock, *flags);
- return rc;
-}
-
-static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr)
-{
- struct mpsc_tx_desc *txre;
-
- txre = (struct mpsc_tx_desc *)(pi->txr
- + (pi->txr_head * MPSC_TXRE_SIZE));
-
- txre->bytecnt = cpu_to_be16(count);
- txre->shadow = txre->bytecnt;
- wmb(); /* ensure cmdstat is last field updated */
- txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F
- | SDMA_DESC_CMDSTAT_L
- | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0));
- wmb();
- dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
- DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)txre,
- (ulong)txre + MPSC_TXRE_SIZE);
-#endif
-}
-
-static void mpsc_copy_tx_data(struct mpsc_port_info *pi)
-{
- struct circ_buf *xmit = &pi->port.state->xmit;
- u8 *bp;
- u32 i;
-
- /* Make sure the desc ring isn't full */
- while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES)
- < (MPSC_TXR_ENTRIES - 1)) {
- if (pi->port.x_char) {
- /*
- * Ideally, we should use the TCS field in
- * CHR_1 to put the x_char out immediately but
- * errata prevents us from being able to read
- * CHR_2 to know that its safe to write to
- * CHR_1. Instead, just put it in-band with
- * all the other Tx data.
- */
- bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
- *bp = pi->port.x_char;
- pi->port.x_char = 0;
- i = 1;
- } else if (!uart_circ_empty(xmit)
- && !uart_tx_stopped(&pi->port)) {
- i = min((u32)MPSC_TXBE_SIZE,
- (u32)uart_circ_chars_pending(xmit));
- i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail,
- UART_XMIT_SIZE));
- bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
- memcpy(bp, &xmit->buf[xmit->tail], i);
- xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&pi->port);
- } else { /* All tx data copied into ring bufs */
- return;
- }
-
- dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE,
- DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)bp,
- (ulong)bp + MPSC_TXBE_SIZE);
-#endif
- mpsc_setup_tx_desc(pi, i, 1);
-
- /* Advance to next descriptor */
- pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
- }
-}
-
-static int mpsc_tx_intr(struct mpsc_port_info *pi)
-{
- struct mpsc_tx_desc *txre;
- int rc = 0;
- unsigned long iflags;
-
- spin_lock_irqsave(&pi->tx_lock, iflags);
-
- if (!mpsc_sdma_tx_active(pi)) {
- txre = (struct mpsc_tx_desc *)(pi->txr
- + (pi->txr_tail * MPSC_TXRE_SIZE));
-
- dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
- DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)txre,
- (ulong)txre + MPSC_TXRE_SIZE);
-#endif
-
- while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) {
- rc = 1;
- pi->port.icount.tx += be16_to_cpu(txre->bytecnt);
- pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1);
-
- /* If no more data to tx, fall out of loop */
- if (pi->txr_head == pi->txr_tail)
- break;
-
- txre = (struct mpsc_tx_desc *)(pi->txr
- + (pi->txr_tail * MPSC_TXRE_SIZE));
- dma_cache_sync(pi->port.dev, (void *)txre,
- MPSC_TXRE_SIZE, DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)txre,
- (ulong)txre + MPSC_TXRE_SIZE);
-#endif
- }
-
- mpsc_copy_tx_data(pi);
- mpsc_sdma_start_tx(pi); /* start next desc if ready */
- }
-
- spin_unlock_irqrestore(&pi->tx_lock, iflags);
- return rc;
-}
-
-/*
- * This is the driver's interrupt handler. To avoid a race, we first clear
- * the interrupt, then handle any completed Rx/Tx descriptors. When done
- * handling those descriptors, we restart the Rx/Tx engines if they're stopped.
- */
-static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id)
-{
- struct mpsc_port_info *pi = dev_id;
- ulong iflags;
- int rc = IRQ_NONE;
-
- pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line);
-
- spin_lock_irqsave(&pi->port.lock, iflags);
- mpsc_sdma_intr_ack(pi);
- if (mpsc_rx_intr(pi, &iflags))
- rc = IRQ_HANDLED;
- if (mpsc_tx_intr(pi))
- rc = IRQ_HANDLED;
- spin_unlock_irqrestore(&pi->port.lock, iflags);
-
- pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line);
- return rc;
-}
-
-/*
- ******************************************************************************
- *
- * serial_core.c Interface routines
- *
- ******************************************************************************
- */
-static uint mpsc_tx_empty(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- ulong iflags;
- uint rc;
-
- spin_lock_irqsave(&pi->port.lock, iflags);
- rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT;
- spin_unlock_irqrestore(&pi->port.lock, iflags);
-
- return rc;
-}
-
-static void mpsc_set_mctrl(struct uart_port *port, uint mctrl)
-{
- /* Have no way to set modem control lines AFAICT */
-}
-
-static uint mpsc_get_mctrl(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- u32 mflags, status;
-
- status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m
- : readl(pi->mpsc_base + MPSC_CHR_10);
-
- mflags = 0;
- if (status & 0x1)
- mflags |= TIOCM_CTS;
- if (status & 0x2)
- mflags |= TIOCM_CAR;
-
- return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */
-}
-
-static void mpsc_stop_tx(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
-
- pr_debug("mpsc_stop_tx[%d]\n", port->line);
-
- mpsc_freeze(pi);
-}
-
-static void mpsc_start_tx(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- unsigned long iflags;
-
- spin_lock_irqsave(&pi->tx_lock, iflags);
-
- mpsc_unfreeze(pi);
- mpsc_copy_tx_data(pi);
- mpsc_sdma_start_tx(pi);
-
- spin_unlock_irqrestore(&pi->tx_lock, iflags);
-
- pr_debug("mpsc_start_tx[%d]\n", port->line);
-}
-
-static void mpsc_start_rx(struct mpsc_port_info *pi)
-{
- pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line);
-
- if (pi->rcv_data) {
- mpsc_enter_hunt(pi);
- mpsc_sdma_cmd(pi, SDMA_SDCM_ERD);
- }
-}
-
-static void mpsc_stop_rx(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
-
- pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line);
-
- if (pi->mirror_regs) {
- writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA,
- pi->mpsc_base + MPSC_CHR_2);
- /* Erratum prevents reading CHR_2 so just delay for a while */
- udelay(100);
- } else {
- writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA,
- pi->mpsc_base + MPSC_CHR_2);
-
- while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA)
- udelay(10);
- }
-
- mpsc_sdma_cmd(pi, SDMA_SDCM_AR);
-}
-
-static void mpsc_break_ctl(struct uart_port *port, int ctl)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- ulong flags;
- u32 v;
-
- v = ctl ? 0x00ff0000 : 0;
-
- spin_lock_irqsave(&pi->port.lock, flags);
- if (pi->mirror_regs)
- pi->MPSC_CHR_1_m = v;
- writel(v, pi->mpsc_base + MPSC_CHR_1);
- spin_unlock_irqrestore(&pi->port.lock, flags);
-}
-
-static int mpsc_startup(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- u32 flag = 0;
- int rc;
-
- pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n",
- port->line, pi->port.irq);
-
- if ((rc = mpsc_make_ready(pi)) == 0) {
- /* Setup IRQ handler */
- mpsc_sdma_intr_ack(pi);
-
- /* If irq's are shared, need to set flag */
- if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq)
- flag = IRQF_SHARED;
-
- if (request_irq(pi->port.irq, mpsc_sdma_intr, flag,
- "mpsc-sdma", pi))
- printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n",
- pi->port.irq);
-
- mpsc_sdma_intr_unmask(pi, 0xf);
- mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p
- + (pi->rxr_posn * MPSC_RXRE_SIZE)));
- }
-
- return rc;
-}
-
-static void mpsc_shutdown(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
-
- pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line);
-
- mpsc_sdma_stop(pi);
- free_irq(pi->port.irq, pi);
-}
-
-static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- u32 baud;
- ulong flags;
- u32 chr_bits, stop_bits, par;
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- chr_bits = MPSC_MPCR_CL_5;
- break;
- case CS6:
- chr_bits = MPSC_MPCR_CL_6;
- break;
- case CS7:
- chr_bits = MPSC_MPCR_CL_7;
- break;
- case CS8:
- default:
- chr_bits = MPSC_MPCR_CL_8;
- break;
- }
-
- if (termios->c_cflag & CSTOPB)
- stop_bits = MPSC_MPCR_SBL_2;
- else
- stop_bits = MPSC_MPCR_SBL_1;
-
- par = MPSC_CHR_2_PAR_EVEN;
- if (termios->c_cflag & PARENB)
- if (termios->c_cflag & PARODD)
- par = MPSC_CHR_2_PAR_ODD;
-#ifdef CMSPAR
- if (termios->c_cflag & CMSPAR) {
- if (termios->c_cflag & PARODD)
- par = MPSC_CHR_2_PAR_MARK;
- else
- par = MPSC_CHR_2_PAR_SPACE;
- }
-#endif
-
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
-
- spin_lock_irqsave(&pi->port.lock, flags);
-
- uart_update_timeout(port, termios->c_cflag, baud);
-
- mpsc_set_char_length(pi, chr_bits);
- mpsc_set_stop_bit_length(pi, stop_bits);
- mpsc_set_parity(pi, par);
- mpsc_set_baudrate(pi, baud);
-
- /* Characters/events to read */
- pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR;
-
- if (termios->c_iflag & INPCK)
- pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE
- | SDMA_DESC_CMDSTAT_FR;
-
- if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR;
-
- /* Characters/events to ignore */
- pi->port.ignore_status_mask = 0;
-
- if (termios->c_iflag & IGNPAR)
- pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE
- | SDMA_DESC_CMDSTAT_FR;
-
- if (termios->c_iflag & IGNBRK) {
- pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR;
-
- if (termios->c_iflag & IGNPAR)
- pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR;
- }
-
- if ((termios->c_cflag & CREAD)) {
- if (!pi->rcv_data) {
- pi->rcv_data = 1;
- mpsc_start_rx(pi);
- }
- } else if (pi->rcv_data) {
- mpsc_stop_rx(port);
- pi->rcv_data = 0;
- }
-
- spin_unlock_irqrestore(&pi->port.lock, flags);
-}
-
-static const char *mpsc_type(struct uart_port *port)
-{
- pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME);
- return MPSC_DRIVER_NAME;
-}
-
-static int mpsc_request_port(struct uart_port *port)
-{
- /* Should make chip/platform specific call */
- return 0;
-}
-
-static void mpsc_release_port(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
-
- if (pi->ready) {
- mpsc_uninit_rings(pi);
- mpsc_free_ring_mem(pi);
- pi->ready = 0;
- }
-}
-
-static void mpsc_config_port(struct uart_port *port, int flags)
-{
-}
-
-static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- int rc = 0;
-
- pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line);
-
- if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC)
- rc = -EINVAL;
- else if (pi->port.irq != ser->irq)
- rc = -EINVAL;
- else if (ser->io_type != SERIAL_IO_MEM)
- rc = -EINVAL;
- else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */
- rc = -EINVAL;
- else if ((void *)pi->port.mapbase != ser->iomem_base)
- rc = -EINVAL;
- else if (pi->port.iobase != ser->port)
- rc = -EINVAL;
- else if (ser->hub6 != 0)
- rc = -EINVAL;
-
- return rc;
-}
-#ifdef CONFIG_CONSOLE_POLL
-/* Serial polling routines for writing and reading from the uart while
- * in an interrupt or debug context.
- */
-
-static char poll_buf[2048];
-static int poll_ptr;
-static int poll_cnt;
-static void mpsc_put_poll_char(struct uart_port *port,
- unsigned char c);
-
-static int mpsc_get_poll_char(struct uart_port *port)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- struct mpsc_rx_desc *rxre;
- u32 cmdstat, bytes_in, i;
- u8 *bp;
-
- if (!serial_polled)
- serial_polled = 1;
-
- pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
-
- if (poll_cnt) {
- poll_cnt--;
- return poll_buf[poll_ptr++];
- }
- poll_ptr = 0;
- poll_cnt = 0;
-
- while (poll_cnt == 0) {
- rxre = (struct mpsc_rx_desc *)(pi->rxr +
- (pi->rxr_posn*MPSC_RXRE_SIZE));
- dma_cache_sync(pi->port.dev, (void *)rxre,
- MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
- /*
- * Loop through Rx descriptors handling ones that have
- * been completed.
- */
- while (poll_cnt == 0 &&
- !((cmdstat = be32_to_cpu(rxre->cmdstat)) &
- SDMA_DESC_CMDSTAT_O)){
- bytes_in = be16_to_cpu(rxre->bytecnt);
- bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
- dma_cache_sync(pi->port.dev, (void *) bp,
- MPSC_RXBE_SIZE, DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)bp,
- (ulong)bp + MPSC_RXBE_SIZE);
-#endif
- if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR |
- SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) &&
- !(cmdstat & pi->port.ignore_status_mask)) {
- poll_buf[poll_cnt] = *bp;
- poll_cnt++;
- } else {
- for (i = 0; i < bytes_in; i++) {
- poll_buf[poll_cnt] = *bp++;
- poll_cnt++;
- }
- pi->port.icount.rx += bytes_in;
- }
- rxre->bytecnt = cpu_to_be16(0);
- wmb();
- rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
- SDMA_DESC_CMDSTAT_EI |
- SDMA_DESC_CMDSTAT_F |
- SDMA_DESC_CMDSTAT_L);
- wmb();
- dma_cache_sync(pi->port.dev, (void *)rxre,
- MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
-
- /* Advance to next descriptor */
- pi->rxr_posn = (pi->rxr_posn + 1) &
- (MPSC_RXR_ENTRIES - 1);
- rxre = (struct mpsc_rx_desc *)(pi->rxr +
- (pi->rxr_posn * MPSC_RXRE_SIZE));
- dma_cache_sync(pi->port.dev, (void *)rxre,
- MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- invalidate_dcache_range((ulong)rxre,
- (ulong)rxre + MPSC_RXRE_SIZE);
-#endif
- }
-
- /* Restart rx engine, if its stopped */
- if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
- mpsc_start_rx(pi);
- }
- if (poll_cnt) {
- poll_cnt--;
- return poll_buf[poll_ptr++];
- }
-
- return 0;
-}
-
-
-static void mpsc_put_poll_char(struct uart_port *port,
- unsigned char c)
-{
- struct mpsc_port_info *pi =
- container_of(port, struct mpsc_port_info, port);
- u32 data;
-
- data = readl(pi->mpsc_base + MPSC_MPCR);
- writeb(c, pi->mpsc_base + MPSC_CHR_1);
- mb();
- data = readl(pi->mpsc_base + MPSC_CHR_2);
- data |= MPSC_CHR_2_TTCS;
- writel(data, pi->mpsc_base + MPSC_CHR_2);
- mb();
-
- while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS);
-}
-#endif
-
-static const struct uart_ops mpsc_pops = {
- .tx_empty = mpsc_tx_empty,
- .set_mctrl = mpsc_set_mctrl,
- .get_mctrl = mpsc_get_mctrl,
- .stop_tx = mpsc_stop_tx,
- .start_tx = mpsc_start_tx,
- .stop_rx = mpsc_stop_rx,
- .break_ctl = mpsc_break_ctl,
- .startup = mpsc_startup,
- .shutdown = mpsc_shutdown,
- .set_termios = mpsc_set_termios,
- .type = mpsc_type,
- .release_port = mpsc_release_port,
- .request_port = mpsc_request_port,
- .config_port = mpsc_config_port,
- .verify_port = mpsc_verify_port,
-#ifdef CONFIG_CONSOLE_POLL
- .poll_get_char = mpsc_get_poll_char,
- .poll_put_char = mpsc_put_poll_char,
-#endif
-};
-
-/*
- ******************************************************************************
- *
- * Console Interface Routines
- *
- ******************************************************************************
- */
-
-#ifdef CONFIG_SERIAL_MPSC_CONSOLE
-static void mpsc_console_write(struct console *co, const char *s, uint count)
-{
- struct mpsc_port_info *pi = &mpsc_ports[co->index];
- u8 *bp, *dp, add_cr = 0;
- int i;
- unsigned long iflags;
-
- spin_lock_irqsave(&pi->tx_lock, iflags);
-
- while (pi->txr_head != pi->txr_tail) {
- while (mpsc_sdma_tx_active(pi))
- udelay(100);
- mpsc_sdma_intr_ack(pi);
- mpsc_tx_intr(pi);
- }
-
- while (mpsc_sdma_tx_active(pi))
- udelay(100);
-
- while (count > 0) {
- bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
-
- for (i = 0; i < MPSC_TXBE_SIZE; i++) {
- if (count == 0)
- break;
-
- if (add_cr) {
- *(dp++) = '\r';
- add_cr = 0;
- } else {
- *(dp++) = *s;
-
- if (*(s++) == '\n') { /* add '\r' after '\n' */
- add_cr = 1;
- count++;
- }
- }
-
- count--;
- }
-
- dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE,
- DMA_BIDIRECTIONAL);
-#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
- if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
- flush_dcache_range((ulong)bp,
- (ulong)bp + MPSC_TXBE_SIZE);
-#endif
- mpsc_setup_tx_desc(pi, i, 0);
- pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
- mpsc_sdma_start_tx(pi);
-
- while (mpsc_sdma_tx_active(pi))
- udelay(100);
-
- pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1);
- }
-
- spin_unlock_irqrestore(&pi->tx_lock, iflags);
-}
-
-static int __init mpsc_console_setup(struct console *co, char *options)
-{
- struct mpsc_port_info *pi;
- int baud, bits, parity, flow;
-
- pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options);
-
- if (co->index >= MPSC_NUM_CTLRS)
- co->index = 0;
-
- pi = &mpsc_ports[co->index];
-
- baud = pi->default_baud;
- bits = pi->default_bits;
- parity = pi->default_parity;
- flow = pi->default_flow;
-
- if (!pi->port.ops)
- return -ENODEV;
-
- spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
-
- return uart_set_options(&pi->port, co, baud, parity, bits, flow);
-}
-
-static struct console mpsc_console = {
- .name = MPSC_DEV_NAME,
- .write = mpsc_console_write,
- .device = uart_console_device,
- .setup = mpsc_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &mpsc_reg,
-};
-
-static int __init mpsc_late_console_init(void)
-{
- pr_debug("mpsc_late_console_init: Enter\n");
-
- if (!(mpsc_console.flags & CON_ENABLED))
- register_console(&mpsc_console);
- return 0;
-}
-
-late_initcall(mpsc_late_console_init);
-
-#define MPSC_CONSOLE &mpsc_console
-#else
-#define MPSC_CONSOLE NULL
-#endif
-/*
- ******************************************************************************
- *
- * Dummy Platform Driver to extract & map shared register regions
- *
- ******************************************************************************
- */
-static void mpsc_resource_err(char *s)
-{
- printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s);
-}
-
-static int mpsc_shared_map_regs(struct platform_device *pd)
-{
- struct resource *r;
-
- if ((r = platform_get_resource(pd, IORESOURCE_MEM,
- MPSC_ROUTING_BASE_ORDER))
- && request_mem_region(r->start,
- MPSC_ROUTING_REG_BLOCK_SIZE,
- "mpsc_routing_regs")) {
- mpsc_shared_regs.mpsc_routing_base = ioremap(r->start,
- MPSC_ROUTING_REG_BLOCK_SIZE);
- mpsc_shared_regs.mpsc_routing_base_p = r->start;
- } else {
- mpsc_resource_err("MPSC routing base");
- return -ENOMEM;
- }
-
- if ((r = platform_get_resource(pd, IORESOURCE_MEM,
- MPSC_SDMA_INTR_BASE_ORDER))
- && request_mem_region(r->start,
- MPSC_SDMA_INTR_REG_BLOCK_SIZE,
- "sdma_intr_regs")) {
- mpsc_shared_regs.sdma_intr_base = ioremap(r->start,
- MPSC_SDMA_INTR_REG_BLOCK_SIZE);
- mpsc_shared_regs.sdma_intr_base_p = r->start;
- } else {
- iounmap(mpsc_shared_regs.mpsc_routing_base);
- release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
- MPSC_ROUTING_REG_BLOCK_SIZE);
- mpsc_resource_err("SDMA intr base");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void mpsc_shared_unmap_regs(void)
-{
- if (mpsc_shared_regs.mpsc_routing_base) {
- iounmap(mpsc_shared_regs.mpsc_routing_base);
- release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
- MPSC_ROUTING_REG_BLOCK_SIZE);
- }
- if (mpsc_shared_regs.sdma_intr_base) {
- iounmap(mpsc_shared_regs.sdma_intr_base);
- release_mem_region(mpsc_shared_regs.sdma_intr_base_p,
- MPSC_SDMA_INTR_REG_BLOCK_SIZE);
- }
-
- mpsc_shared_regs.mpsc_routing_base = NULL;
- mpsc_shared_regs.sdma_intr_base = NULL;
-
- mpsc_shared_regs.mpsc_routing_base_p = 0;
- mpsc_shared_regs.sdma_intr_base_p = 0;
-}
-
-static int mpsc_shared_drv_probe(struct platform_device *dev)
-{
- struct mpsc_shared_pdata *pdata;
- int rc;
-
- if (dev->id != 0)
- return -ENODEV;
-
- rc = mpsc_shared_map_regs(dev);
- if (rc)
- return rc;
-
- pdata = dev_get_platdata(&dev->dev);
-
- mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
- mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
- mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val;
- mpsc_shared_regs.SDMA_INTR_CAUSE_m = pdata->intr_cause_val;
- mpsc_shared_regs.SDMA_INTR_MASK_m = pdata->intr_mask_val;
-
- return 0;
-}
-
-static int mpsc_shared_drv_remove(struct platform_device *dev)
-{
- if (dev->id != 0)
- return -ENODEV;
-
- mpsc_shared_unmap_regs();
- mpsc_shared_regs.MPSC_MRR_m = 0;
- mpsc_shared_regs.MPSC_RCRR_m = 0;
- mpsc_shared_regs.MPSC_TCRR_m = 0;
- mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0;
- mpsc_shared_regs.SDMA_INTR_MASK_m = 0;
-
- return 0;
-}
-
-static struct platform_driver mpsc_shared_driver = {
- .probe = mpsc_shared_drv_probe,
- .remove = mpsc_shared_drv_remove,
- .driver = {
- .name = MPSC_SHARED_NAME,
- },
-};
-
-/*
- ******************************************************************************
- *
- * Driver Interface Routines
- *
- ******************************************************************************
- */
-static struct uart_driver mpsc_reg = {
- .owner = THIS_MODULE,
- .driver_name = MPSC_DRIVER_NAME,
- .dev_name = MPSC_DEV_NAME,
- .major = MPSC_MAJOR,
- .minor = MPSC_MINOR_START,
- .nr = MPSC_NUM_CTLRS,
- .cons = MPSC_CONSOLE,
-};
-
-static int mpsc_drv_map_regs(struct mpsc_port_info *pi,
- struct platform_device *pd)
-{
- struct resource *r;
-
- if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER))
- && request_mem_region(r->start, MPSC_REG_BLOCK_SIZE,
- "mpsc_regs")) {
- pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE);
- pi->mpsc_base_p = r->start;
- } else {
- mpsc_resource_err("MPSC base");
- goto err;
- }
-
- if ((r = platform_get_resource(pd, IORESOURCE_MEM,
- MPSC_SDMA_BASE_ORDER))
- && request_mem_region(r->start,
- MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) {
- pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE);
- pi->sdma_base_p = r->start;
- } else {
- mpsc_resource_err("SDMA base");
- goto err;
- }
-
- if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER))
- && request_mem_region(r->start,
- MPSC_BRG_REG_BLOCK_SIZE, "brg_regs")) {
- pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE);
- pi->brg_base_p = r->start;
- } else {
- mpsc_resource_err("BRG base");
- goto err;
- }
- return 0;
-
-err:
- if (pi->sdma_base) {
- iounmap(pi->sdma_base);
- pi->sdma_base = NULL;
- }
- if (pi->mpsc_base) {
- iounmap(pi->mpsc_base);
- pi->mpsc_base = NULL;
- }
- return -ENOMEM;
-}
-
-static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi)
-{
- if (pi->mpsc_base) {
- iounmap(pi->mpsc_base);
- release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE);
- }
- if (pi->sdma_base) {
- iounmap(pi->sdma_base);
- release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE);
- }
- if (pi->brg_base) {
- iounmap(pi->brg_base);
- release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE);
- }
-
- pi->mpsc_base = NULL;
- pi->sdma_base = NULL;
- pi->brg_base = NULL;
-
- pi->mpsc_base_p = 0;
- pi->sdma_base_p = 0;
- pi->brg_base_p = 0;
-}
-
-static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
- struct platform_device *pd, int num)
-{
- struct mpsc_pdata *pdata;
-
- pdata = dev_get_platdata(&pd->dev);
-
- pi->port.uartclk = pdata->brg_clk_freq;
- pi->port.iotype = UPIO_MEM;
- pi->port.line = num;
- pi->port.type = PORT_MPSC;
- pi->port.fifosize = MPSC_TXBE_SIZE;
- pi->port.membase = pi->mpsc_base;
- pi->port.mapbase = (ulong)pi->mpsc_base;
- pi->port.ops = &mpsc_pops;
-
- pi->mirror_regs = pdata->mirror_regs;
- pi->cache_mgmt = pdata->cache_mgmt;
- pi->brg_can_tune = pdata->brg_can_tune;
- pi->brg_clk_src = pdata->brg_clk_src;
- pi->mpsc_max_idle = pdata->max_idle;
- pi->default_baud = pdata->default_baud;
- pi->default_bits = pdata->default_bits;
- pi->default_parity = pdata->default_parity;
- pi->default_flow = pdata->default_flow;
-
- /* Initial values of mirrored regs */
- pi->MPSC_CHR_1_m = pdata->chr_1_val;
- pi->MPSC_CHR_2_m = pdata->chr_2_val;
- pi->MPSC_CHR_10_m = pdata->chr_10_val;
- pi->MPSC_MPCR_m = pdata->mpcr_val;
- pi->BRG_BCR_m = pdata->bcr_val;
-
- pi->shared_regs = &mpsc_shared_regs;
-
- pi->port.irq = platform_get_irq(pd, 0);
-}
-
-static int mpsc_drv_probe(struct platform_device *dev)
-{
- struct mpsc_port_info *pi;
- int rc;
-
- dev_dbg(&dev->dev, "mpsc_drv_probe: Adding MPSC %d\n", dev->id);
-
- if (dev->id >= MPSC_NUM_CTLRS)
- return -ENODEV;
-
- pi = &mpsc_ports[dev->id];
-
- rc = mpsc_drv_map_regs(pi, dev);
- if (rc)
- return rc;
-
- mpsc_drv_get_platform_data(pi, dev, dev->id);
- pi->port.dev = &dev->dev;
-
- rc = mpsc_make_ready(pi);
- if (rc)
- goto err_unmap;
-
- spin_lock_init(&pi->tx_lock);
- rc = uart_add_one_port(&mpsc_reg, &pi->port);
- if (rc)
- goto err_relport;
-
- return 0;
-err_relport:
- mpsc_release_port(&pi->port);
-err_unmap:
- mpsc_drv_unmap_regs(pi);
- return rc;
-}
-
-static struct platform_driver mpsc_driver = {
- .probe = mpsc_drv_probe,
- .driver = {
- .name = MPSC_CTLR_NAME,
- .suppress_bind_attrs = true,
- },
-};
-
-static int __init mpsc_drv_init(void)
-{
- int rc;
-
- printk(KERN_INFO "Serial: MPSC driver\n");
-
- memset(mpsc_ports, 0, sizeof(mpsc_ports));
- memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs));
-
- rc = uart_register_driver(&mpsc_reg);
- if (rc)
- return rc;
-
- rc = platform_driver_register(&mpsc_shared_driver);
- if (rc)
- goto err_unreg_uart;
-
- rc = platform_driver_register(&mpsc_driver);
- if (rc)
- goto err_unreg_plat;
-
- return 0;
-err_unreg_plat:
- platform_driver_unregister(&mpsc_shared_driver);
-err_unreg_uart:
- uart_unregister_driver(&mpsc_reg);
- return rc;
-}
-device_initcall(mpsc_drv_init);
-
-/*
-MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
-MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver");
-MODULE_LICENSE("GPL");
-*/
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 736b74f..3657a24 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -383,10 +383,14 @@
static inline void msm_wait_for_xmitr(struct uart_port *port)
{
+ unsigned int timeout = 500000;
+
while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
break;
udelay(1);
+ if (!timeout--)
+ break;
}
msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR);
}
@@ -860,6 +864,7 @@
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct msm_dma *dma = &msm_port->tx_dma;
unsigned int pio_count, dma_count, dma_min;
+ char buf[4] = { 0 };
void __iomem *tf;
int err = 0;
@@ -869,10 +874,12 @@
else
tf = port->membase + UART_TF;
+ buf[0] = port->x_char;
+
if (msm_port->is_uartdm)
msm_reset_dm_count(port, 1);
- iowrite8_rep(tf, &port->x_char, 1);
+ iowrite32_rep(tf, buf, 1);
port->icount.tx++;
port->x_char = 0;
return;
@@ -1634,7 +1641,7 @@
__msm_console_write(port, s, count, msm_port->is_uartdm);
}
-static int __init msm_console_setup(struct console *co, char *options)
+static int msm_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 115200;
diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c
index 170e446..c12a125 100644
--- a/drivers/tty/serial/mvebu-uart.c
+++ b/drivers/tty/serial/mvebu-uart.c
@@ -72,6 +72,8 @@
#define BRDV_BAUD_MASK 0x3FF
#define UART_OSAMP 0x14
+#define OSAMP_DEFAULT_DIVISOR 16
+#define OSAMP_DIVISORS_MASK 0x3F3F3F3F
#define MVEBU_NR_UARTS 2
@@ -444,25 +446,34 @@
static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud)
{
struct mvebu_uart *mvuart = to_mvuart(port);
- unsigned int baud_rate_div;
- u32 brdv;
+ unsigned int d_divisor, m_divisor;
+ u32 brdv, osamp;
if (IS_ERR(mvuart->clk))
return -PTR_ERR(mvuart->clk);
/*
- * The UART clock is divided by the value of the divisor to generate
- * UCLK_OUT clock, which is 16 times faster than the baudrate.
- * This prescaler can achieve all standard baudrates until 230400.
- * Higher baudrates could be achieved for the extended UART by using the
- * programmable oversampling stack (also called fractional divisor).
+ * The baudrate is derived from the UART clock thanks to two divisors:
+ * > D ("baud generator"): can divide the clock from 2 to 2^10 - 1.
+ * > M ("fractional divisor"): allows a better accuracy for
+ * baudrates higher than 230400.
+ *
+ * As the derivation of M is rather complicated, the code sticks to its
+ * default value (x16) when all the prescalers are zeroed, and only
+ * makes use of D to configure the desired baudrate.
*/
- baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16);
+ m_divisor = OSAMP_DEFAULT_DIVISOR;
+ d_divisor = DIV_ROUND_UP(port->uartclk, baud * m_divisor);
+
brdv = readl(port->membase + UART_BRDV);
brdv &= ~BRDV_BAUD_MASK;
- brdv |= baud_rate_div;
+ brdv |= d_divisor;
writel(brdv, port->membase + UART_BRDV);
+ osamp = readl(port->membase + UART_OSAMP);
+ osamp &= ~OSAMP_DIVISORS_MASK;
+ writel(osamp, port->membase + UART_OSAMP);
+
return 0;
}
@@ -799,6 +810,9 @@
return -EINVAL;
}
+ if (!match)
+ return -ENODEV;
+
/* Assume that all UART ports have a DT alias or none has */
id = of_alias_get_id(pdev->dev.of_node, "serial");
if (!pdev->dev.of_node || id < 0)
@@ -870,10 +884,8 @@
if (platform_irq_count(pdev) == 1) {
/* Old bindings: no name on the single unamed UART0 IRQ */
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "unable to get UART IRQ\n");
+ if (irq < 0)
return irq;
- }
mvuart->irq[UART_IRQ_SUM] = irq;
} else {
@@ -883,18 +895,14 @@
* uart-sum of UART0 port.
*/
irq = platform_get_irq_byname(pdev, "uart-rx");
- if (irq < 0) {
- dev_err(&pdev->dev, "unable to get 'uart-rx' IRQ\n");
+ if (irq < 0)
return irq;
- }
mvuart->irq[UART_RX_IRQ] = irq;
irq = platform_get_irq_byname(pdev, "uart-tx");
- if (irq < 0) {
- dev_err(&pdev->dev, "unable to get 'uart-tx' IRQ\n");
+ if (irq < 0)
return irq;
- }
mvuart->irq[UART_TX_IRQ] = irq;
}
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 76aa289..e345259 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -969,10 +969,8 @@
}
-#define RTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \
- UART_GPIO_RTS))
-#define CTS_AT_AUART() IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(s->gpios, \
- UART_GPIO_CTS))
+#define RTS_AT_AUART() !mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_RTS)
+#define CTS_AT_AUART() !mctrl_gpio_to_gpiod(s->gpios, UART_GPIO_CTS)
static void mxs_auart_settermios(struct uart_port *u,
struct ktermios *termios,
struct ktermios *old)
@@ -1634,8 +1632,9 @@
/*
* If something went wrong, rollback.
+ * Be careful: i may be unsigned.
*/
- while (err && (--i >= 0))
+ while (err && (i-- > 0))
if (irq[i] >= 0)
free_irq(irq[i], s);
@@ -1685,6 +1684,10 @@
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
+ if (!s->port.membase) {
+ ret = -ENOMEM;
+ goto out_disable_clks;
+ }
s->port.ops = &mxs_auart_ops;
s->port.iotype = UPIO_MEM;
s->port.fifosize = MXS_AUART_FIFO_SIZE;
diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c
deleted file mode 100644
index b355686..0000000
--- a/drivers/tty/serial/netx-serial.c
+++ /dev/null
@@ -1,733 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
- */
-
-#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/platform_device.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial_core.h>
-#include <linux/serial.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <mach/hardware.h>
-#include <mach/netx-regs.h>
-
-/* We've been assigned a range on the "Low-density serial ports" major */
-#define SERIAL_NX_MAJOR 204
-#define MINOR_START 170
-
-enum uart_regs {
- UART_DR = 0x00,
- UART_SR = 0x04,
- UART_LINE_CR = 0x08,
- UART_BAUDDIV_MSB = 0x0c,
- UART_BAUDDIV_LSB = 0x10,
- UART_CR = 0x14,
- UART_FR = 0x18,
- UART_IIR = 0x1c,
- UART_ILPR = 0x20,
- UART_RTS_CR = 0x24,
- UART_RTS_LEAD = 0x28,
- UART_RTS_TRAIL = 0x2c,
- UART_DRV_ENABLE = 0x30,
- UART_BRM_CR = 0x34,
- UART_RXFIFO_IRQLEVEL = 0x38,
- UART_TXFIFO_IRQLEVEL = 0x3c,
-};
-
-#define SR_FE (1<<0)
-#define SR_PE (1<<1)
-#define SR_BE (1<<2)
-#define SR_OE (1<<3)
-
-#define LINE_CR_BRK (1<<0)
-#define LINE_CR_PEN (1<<1)
-#define LINE_CR_EPS (1<<2)
-#define LINE_CR_STP2 (1<<3)
-#define LINE_CR_FEN (1<<4)
-#define LINE_CR_5BIT (0<<5)
-#define LINE_CR_6BIT (1<<5)
-#define LINE_CR_7BIT (2<<5)
-#define LINE_CR_8BIT (3<<5)
-#define LINE_CR_BITS_MASK (3<<5)
-
-#define CR_UART_EN (1<<0)
-#define CR_SIREN (1<<1)
-#define CR_SIRLP (1<<2)
-#define CR_MSIE (1<<3)
-#define CR_RIE (1<<4)
-#define CR_TIE (1<<5)
-#define CR_RTIE (1<<6)
-#define CR_LBE (1<<7)
-
-#define FR_CTS (1<<0)
-#define FR_DSR (1<<1)
-#define FR_DCD (1<<2)
-#define FR_BUSY (1<<3)
-#define FR_RXFE (1<<4)
-#define FR_TXFF (1<<5)
-#define FR_RXFF (1<<6)
-#define FR_TXFE (1<<7)
-
-#define IIR_MIS (1<<0)
-#define IIR_RIS (1<<1)
-#define IIR_TIS (1<<2)
-#define IIR_RTIS (1<<3)
-#define IIR_MASK 0xf
-
-#define RTS_CR_AUTO (1<<0)
-#define RTS_CR_RTS (1<<1)
-#define RTS_CR_COUNT (1<<2)
-#define RTS_CR_MOD2 (1<<3)
-#define RTS_CR_RTS_POL (1<<4)
-#define RTS_CR_CTS_CTR (1<<5)
-#define RTS_CR_CTS_POL (1<<6)
-#define RTS_CR_STICK (1<<7)
-
-#define UART_PORT_SIZE 0x40
-#define DRIVER_NAME "netx-uart"
-
-struct netx_port {
- struct uart_port port;
-};
-
-static void netx_stop_tx(struct uart_port *port)
-{
- unsigned int val;
- val = readl(port->membase + UART_CR);
- writel(val & ~CR_TIE, port->membase + UART_CR);
-}
-
-static void netx_stop_rx(struct uart_port *port)
-{
- unsigned int val;
- val = readl(port->membase + UART_CR);
- writel(val & ~CR_RIE, port->membase + UART_CR);
-}
-
-static void netx_enable_ms(struct uart_port *port)
-{
- unsigned int val;
- val = readl(port->membase + UART_CR);
- writel(val | CR_MSIE, port->membase + UART_CR);
-}
-
-static inline void netx_transmit_buffer(struct uart_port *port)
-{
- struct circ_buf *xmit = &port->state->xmit;
-
- if (port->x_char) {
- writel(port->x_char, port->membase + UART_DR);
- port->icount.tx++;
- port->x_char = 0;
- return;
- }
-
- if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
- netx_stop_tx(port);
- return;
- }
-
- do {
- /* send xmit->buf[xmit->tail]
- * out the port here */
- writel(xmit->buf[xmit->tail], port->membase + UART_DR);
- xmit->tail = (xmit->tail + 1) &
- (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- if (uart_circ_empty(xmit))
- break;
- } while (!(readl(port->membase + UART_FR) & FR_TXFF));
-
- if (uart_circ_empty(xmit))
- netx_stop_tx(port);
-}
-
-static void netx_start_tx(struct uart_port *port)
-{
- writel(
- readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR);
-
- if (!(readl(port->membase + UART_FR) & FR_TXFF))
- netx_transmit_buffer(port);
-}
-
-static unsigned int netx_tx_empty(struct uart_port *port)
-{
- return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT;
-}
-
-static void netx_txint(struct uart_port *port)
-{
- struct circ_buf *xmit = &port->state->xmit;
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
- netx_stop_tx(port);
- return;
- }
-
- netx_transmit_buffer(port);
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-}
-
-static void netx_rxint(struct uart_port *port, unsigned long *flags)
-{
- unsigned char rx, flg, status;
-
- while (!(readl(port->membase + UART_FR) & FR_RXFE)) {
- rx = readl(port->membase + UART_DR);
- flg = TTY_NORMAL;
- port->icount.rx++;
- status = readl(port->membase + UART_SR);
- if (status & SR_BE) {
- writel(0, port->membase + UART_SR);
- if (uart_handle_break(port))
- continue;
- }
-
- if (unlikely(status & (SR_FE | SR_PE | SR_OE))) {
-
- if (status & SR_PE)
- port->icount.parity++;
- else if (status & SR_FE)
- port->icount.frame++;
- if (status & SR_OE)
- port->icount.overrun++;
-
- status &= port->read_status_mask;
-
- if (status & SR_BE)
- flg = TTY_BREAK;
- else if (status & SR_PE)
- flg = TTY_PARITY;
- else if (status & SR_FE)
- flg = TTY_FRAME;
- }
-
- if (uart_handle_sysrq_char(port, rx))
- continue;
-
- uart_insert_char(port, status, SR_OE, rx, flg);
- }
-
- spin_unlock_irqrestore(&port->lock, *flags);
- tty_flip_buffer_push(&port->state->port);
- spin_lock_irqsave(&port->lock, *flags);
-}
-
-static irqreturn_t netx_int(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- unsigned long flags;
- unsigned char status;
-
- spin_lock_irqsave(&port->lock,flags);
-
- status = readl(port->membase + UART_IIR) & IIR_MASK;
- while (status) {
- if (status & IIR_RIS)
- netx_rxint(port, &flags);
- if (status & IIR_TIS)
- netx_txint(port);
- if (status & IIR_MIS) {
- if (readl(port->membase + UART_FR) & FR_CTS)
- uart_handle_cts_change(port, 1);
- else
- uart_handle_cts_change(port, 0);
- }
- writel(0, port->membase + UART_IIR);
- status = readl(port->membase + UART_IIR) & IIR_MASK;
- }
-
- spin_unlock_irqrestore(&port->lock,flags);
- return IRQ_HANDLED;
-}
-
-static unsigned int netx_get_mctrl(struct uart_port *port)
-{
- unsigned int ret = TIOCM_DSR | TIOCM_CAR;
-
- if (readl(port->membase + UART_FR) & FR_CTS)
- ret |= TIOCM_CTS;
-
- return ret;
-}
-
-static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
- unsigned int val;
-
- /* FIXME: Locking needed ? */
- if (mctrl & TIOCM_RTS) {
- val = readl(port->membase + UART_RTS_CR);
- writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR);
- }
-}
-
-static void netx_break_ctl(struct uart_port *port, int break_state)
-{
- unsigned int line_cr;
- spin_lock_irq(&port->lock);
-
- line_cr = readl(port->membase + UART_LINE_CR);
- if (break_state != 0)
- line_cr |= LINE_CR_BRK;
- else
- line_cr &= ~LINE_CR_BRK;
- writel(line_cr, port->membase + UART_LINE_CR);
-
- spin_unlock_irq(&port->lock);
-}
-
-static int netx_startup(struct uart_port *port)
-{
- int ret;
-
- ret = request_irq(port->irq, netx_int, 0,
- DRIVER_NAME, port);
- if (ret) {
- dev_err(port->dev, "unable to grab irq%d\n",port->irq);
- goto exit;
- }
-
- writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN,
- port->membase + UART_LINE_CR);
-
- writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN,
- port->membase + UART_CR);
-
-exit:
- return ret;
-}
-
-static void netx_shutdown(struct uart_port *port)
-{
- writel(0, port->membase + UART_CR) ;
-
- free_irq(port->irq, port);
-}
-
-static void
-netx_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
- unsigned int baud, quot;
- unsigned char old_cr;
- unsigned char line_cr = LINE_CR_FEN;
- unsigned char rts_cr = 0;
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- line_cr |= LINE_CR_5BIT;
- break;
- case CS6:
- line_cr |= LINE_CR_6BIT;
- break;
- case CS7:
- line_cr |= LINE_CR_7BIT;
- break;
- case CS8:
- line_cr |= LINE_CR_8BIT;
- break;
- }
-
- if (termios->c_cflag & CSTOPB)
- line_cr |= LINE_CR_STP2;
-
- if (termios->c_cflag & PARENB) {
- line_cr |= LINE_CR_PEN;
- if (!(termios->c_cflag & PARODD))
- line_cr |= LINE_CR_EPS;
- }
-
- if (termios->c_cflag & CRTSCTS)
- rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL;
-
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
- quot = baud * 4096;
- quot /= 1000;
- quot *= 256;
- quot /= 100000;
-
- spin_lock_irq(&port->lock);
-
- uart_update_timeout(port, termios->c_cflag, baud);
-
- old_cr = readl(port->membase + UART_CR);
-
- /* disable interrupts */
- writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE),
- port->membase + UART_CR);
-
- /* drain transmitter */
- while (readl(port->membase + UART_FR) & FR_BUSY);
-
- /* disable UART */
- writel(old_cr & ~CR_UART_EN, port->membase + UART_CR);
-
- /* modem status interrupts */
- old_cr &= ~CR_MSIE;
- if (UART_ENABLE_MS(port, termios->c_cflag))
- old_cr |= CR_MSIE;
-
- writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB);
- writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB);
- writel(line_cr, port->membase + UART_LINE_CR);
-
- writel(rts_cr, port->membase + UART_RTS_CR);
-
- /*
- * Characters to ignore
- */
- port->ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= SR_PE;
- if (termios->c_iflag & IGNBRK) {
- port->ignore_status_mask |= SR_BE;
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns too (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= SR_PE;
- }
-
- port->read_status_mask = 0;
- if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- port->read_status_mask |= SR_BE;
- if (termios->c_iflag & INPCK)
- port->read_status_mask |= SR_PE | SR_FE;
-
- writel(old_cr, port->membase + UART_CR);
-
- spin_unlock_irq(&port->lock);
-}
-
-static const char *netx_type(struct uart_port *port)
-{
- return port->type == PORT_NETX ? "NETX" : NULL;
-}
-
-static void netx_release_port(struct uart_port *port)
-{
- release_mem_region(port->mapbase, UART_PORT_SIZE);
-}
-
-static int netx_request_port(struct uart_port *port)
-{
- return request_mem_region(port->mapbase, UART_PORT_SIZE,
- DRIVER_NAME) != NULL ? 0 : -EBUSY;
-}
-
-static void netx_config_port(struct uart_port *port, int flags)
-{
- if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0)
- port->type = PORT_NETX;
-}
-
-static int
-netx_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- int ret = 0;
-
- if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX)
- ret = -EINVAL;
-
- return ret;
-}
-
-static struct uart_ops netx_pops = {
- .tx_empty = netx_tx_empty,
- .set_mctrl = netx_set_mctrl,
- .get_mctrl = netx_get_mctrl,
- .stop_tx = netx_stop_tx,
- .start_tx = netx_start_tx,
- .stop_rx = netx_stop_rx,
- .enable_ms = netx_enable_ms,
- .break_ctl = netx_break_ctl,
- .startup = netx_startup,
- .shutdown = netx_shutdown,
- .set_termios = netx_set_termios,
- .type = netx_type,
- .release_port = netx_release_port,
- .request_port = netx_request_port,
- .config_port = netx_config_port,
- .verify_port = netx_verify_port,
-};
-
-static struct netx_port netx_ports[] = {
- {
- .port = {
- .type = PORT_NETX,
- .iotype = UPIO_MEM,
- .membase = (char __iomem *)io_p2v(NETX_PA_UART0),
- .mapbase = NETX_PA_UART0,
- .irq = NETX_IRQ_UART0,
- .uartclk = 100000000,
- .fifosize = 16,
- .flags = UPF_BOOT_AUTOCONF,
- .ops = &netx_pops,
- .line = 0,
- },
- }, {
- .port = {
- .type = PORT_NETX,
- .iotype = UPIO_MEM,
- .membase = (char __iomem *)io_p2v(NETX_PA_UART1),
- .mapbase = NETX_PA_UART1,
- .irq = NETX_IRQ_UART1,
- .uartclk = 100000000,
- .fifosize = 16,
- .flags = UPF_BOOT_AUTOCONF,
- .ops = &netx_pops,
- .line = 1,
- },
- }, {
- .port = {
- .type = PORT_NETX,
- .iotype = UPIO_MEM,
- .membase = (char __iomem *)io_p2v(NETX_PA_UART2),
- .mapbase = NETX_PA_UART2,
- .irq = NETX_IRQ_UART2,
- .uartclk = 100000000,
- .fifosize = 16,
- .flags = UPF_BOOT_AUTOCONF,
- .ops = &netx_pops,
- .line = 2,
- },
- }
-};
-
-#ifdef CONFIG_SERIAL_NETX_CONSOLE
-
-static void netx_console_putchar(struct uart_port *port, int ch)
-{
- while (readl(port->membase + UART_FR) & FR_BUSY);
- writel(ch, port->membase + UART_DR);
-}
-
-static void
-netx_console_write(struct console *co, const char *s, unsigned int count)
-{
- struct uart_port *port = &netx_ports[co->index].port;
- unsigned char cr_save;
-
- cr_save = readl(port->membase + UART_CR);
- writel(cr_save | CR_UART_EN, port->membase + UART_CR);
-
- uart_console_write(port, s, count, netx_console_putchar);
-
- while (readl(port->membase + UART_FR) & FR_BUSY);
- writel(cr_save, port->membase + UART_CR);
-}
-
-static void __init
-netx_console_get_options(struct uart_port *port, int *baud,
- int *parity, int *bits, int *flow)
-{
- unsigned char line_cr;
-
- *baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) |
- readl(port->membase + UART_BAUDDIV_LSB);
- *baud *= 1000;
- *baud /= 4096;
- *baud *= 1000;
- *baud /= 256;
- *baud *= 100;
-
- line_cr = readl(port->membase + UART_LINE_CR);
- *parity = 'n';
- if (line_cr & LINE_CR_PEN) {
- if (line_cr & LINE_CR_EPS)
- *parity = 'e';
- else
- *parity = 'o';
- }
-
- switch (line_cr & LINE_CR_BITS_MASK) {
- case LINE_CR_8BIT:
- *bits = 8;
- break;
- case LINE_CR_7BIT:
- *bits = 7;
- break;
- case LINE_CR_6BIT:
- *bits = 6;
- break;
- case LINE_CR_5BIT:
- *bits = 5;
- break;
- }
-
- if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO)
- *flow = 'r';
-}
-
-static int __init
-netx_console_setup(struct console *co, char *options)
-{
- struct netx_port *sport;
- int baud = 9600;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- /*
- * Check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- if (co->index == -1 || co->index >= ARRAY_SIZE(netx_ports))
- co->index = 0;
- sport = &netx_ports[co->index];
-
- if (options) {
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- } else {
- /* if the UART is enabled, assume it has been correctly setup
- * by the bootloader and get the options
- */
- if (readl(sport->port.membase + UART_CR) & CR_UART_EN) {
- netx_console_get_options(&sport->port, &baud,
- &parity, &bits, &flow);
- }
-
- }
-
- return uart_set_options(&sport->port, co, baud, parity, bits, flow);
-}
-
-static struct uart_driver netx_reg;
-static struct console netx_console = {
- .name = "ttyNX",
- .write = netx_console_write,
- .device = uart_console_device,
- .setup = netx_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &netx_reg,
-};
-
-static int __init netx_console_init(void)
-{
- register_console(&netx_console);
- return 0;
-}
-console_initcall(netx_console_init);
-
-#define NETX_CONSOLE &netx_console
-#else
-#define NETX_CONSOLE NULL
-#endif
-
-static struct uart_driver netx_reg = {
- .owner = THIS_MODULE,
- .driver_name = DRIVER_NAME,
- .dev_name = "ttyNX",
- .major = SERIAL_NX_MAJOR,
- .minor = MINOR_START,
- .nr = ARRAY_SIZE(netx_ports),
- .cons = NETX_CONSOLE,
-};
-
-static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct netx_port *sport = platform_get_drvdata(pdev);
-
- if (sport)
- uart_suspend_port(&netx_reg, &sport->port);
-
- return 0;
-}
-
-static int serial_netx_resume(struct platform_device *pdev)
-{
- struct netx_port *sport = platform_get_drvdata(pdev);
-
- if (sport)
- uart_resume_port(&netx_reg, &sport->port);
-
- return 0;
-}
-
-static int serial_netx_probe(struct platform_device *pdev)
-{
- struct uart_port *port = &netx_ports[pdev->id].port;
-
- dev_info(&pdev->dev, "initialising\n");
-
- port->dev = &pdev->dev;
-
- writel(1, port->membase + UART_RXFIFO_IRQLEVEL);
- uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port);
- platform_set_drvdata(pdev, &netx_ports[pdev->id]);
-
- return 0;
-}
-
-static int serial_netx_remove(struct platform_device *pdev)
-{
- struct netx_port *sport = platform_get_drvdata(pdev);
-
- if (sport)
- uart_remove_one_port(&netx_reg, &sport->port);
-
- return 0;
-}
-
-static struct platform_driver serial_netx_driver = {
- .probe = serial_netx_probe,
- .remove = serial_netx_remove,
-
- .suspend = serial_netx_suspend,
- .resume = serial_netx_resume,
-
- .driver = {
- .name = DRIVER_NAME,
- },
-};
-
-static int __init netx_serial_init(void)
-{
- int ret;
-
- printk(KERN_INFO "Serial: NetX driver\n");
-
- ret = uart_register_driver(&netx_reg);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&serial_netx_driver);
- if (ret != 0)
- uart_unregister_driver(&netx_reg);
-
- return 0;
-}
-
-static void __exit netx_serial_exit(void)
-{
- platform_driver_unregister(&serial_netx_driver);
- uart_unregister_driver(&netx_reg);
-}
-
-module_init(netx_serial_init);
-module_exit(netx_serial_exit);
-
-MODULE_AUTHOR("Sascha Hauer");
-MODULE_DESCRIPTION("NetX serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c
index 29a6dc6..d2d8b34 100644
--- a/drivers/tty/serial/owl-uart.c
+++ b/drivers/tty/serial/owl-uart.c
@@ -662,10 +662,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "could not get irq\n");
+ if (irq < 0)
return irq;
- }
if (owl_uart_ports[pdev->id]) {
dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
@@ -742,7 +740,7 @@
return ret;
}
-static void __init owl_uart_exit(void)
+static void __exit owl_uart_exit(void)
{
platform_driver_unregister(&owl_uart_platform_driver);
uart_unregister_driver(&owl_uart_driver);
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index cb85002..6157213 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -192,8 +192,6 @@
#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP)
#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE)
-#define PCI_VENDOR_ID_ROHM 0x10DB
-
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
#define DEFAULT_UARTCLK 1843200 /* 1.8432 MHz */
@@ -933,7 +931,6 @@
struct scatterlist *sg;
int nent;
int fifo_size;
- int tx_empty;
struct dma_async_tx_descriptor *desc;
int num;
int i;
@@ -958,11 +955,9 @@
}
fifo_size = max(priv->fifo_size, 1);
- tx_empty = 1;
if (pop_tx_x(priv, xmit->buf)) {
pch_uart_hal_write(priv, xmit->buf, 1);
port->icount.tx++;
- tx_empty = 0;
fifo_size--;
}
diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c
index fd80d99..0bdf168 100644
--- a/drivers/tty/serial/pic32_uart.c
+++ b/drivers/tty/serial/pic32_uart.c
@@ -919,6 +919,7 @@
.driver = {
.name = PIC32_DEV_NAME,
.of_match_table = of_match_ptr(pic32_serial_dt_ids),
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_PIC32),
},
};
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index 3d21790..bcb5bf7 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -219,7 +219,7 @@
static bool pmz_receive_chars(struct uart_pmac_port *uap)
{
struct tty_port *port;
- unsigned char ch, r1, drop, error, flag;
+ unsigned char ch, r1, drop, flag;
int loops = 0;
/* Sanity check, make sure the old bug is no longer happening */
@@ -231,7 +231,6 @@
port = &uap->port.state->port;
while (1) {
- error = 0;
drop = 0;
r1 = read_zsreg(uap, R1);
@@ -273,7 +272,6 @@
uap->port.icount.rx++;
if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) {
- error = 1;
if (r1 & BRK_ABRT) {
pmz_debug("pmz: got break !\n");
r1 &= ~(PAR_ERR | CRC_ERR);
@@ -1566,9 +1564,9 @@
* to work around bugs in ancient Apple device-trees
*/
if (macio_request_resources(uap->dev, "pmac_zilog"))
- printk(KERN_WARNING "%s: Failed to request resource"
+ printk(KERN_WARNING "%pOFn: Failed to request resource"
", port still active\n",
- uap->node->name);
+ uap->node);
else
uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
@@ -1650,9 +1648,9 @@
*/
node_a = node_b = NULL;
for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) {
- if (strncmp(np->name, "ch-a", 4) == 0)
+ if (of_node_name_prefix(np, "ch-a"))
node_a = of_node_get(np);
- else if (strncmp(np->name, "ch-b", 4) == 0)
+ else if (of_node_name_prefix(np, "ch-b"))
node_b = of_node_get(np);
}
if (!node_a && !node_b) {
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 1515074..14c6306 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+#if defined(CONFIG_SERIAL_QCOM_GENI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/io.h>
@@ -85,13 +89,13 @@
#define DEF_FIFO_DEPTH_WORDS 16
#define DEF_TX_WM 2
#define DEF_FIFO_WIDTH_BITS 32
-#define UART_CONSOLE_RX_WM 2
+#define UART_RX_WM 2
#define MAX_LOOPBACK_CFG 3
#ifdef CONFIG_CONSOLE_POLL
-#define RX_BYTES_PW 1
+#define CONSOLE_RX_BYTES_PW 1
#else
-#define RX_BYTES_PW 4
+#define CONSOLE_RX_BYTES_PW 4
#endif
struct qcom_geni_serial_port {
@@ -101,10 +105,6 @@
u32 tx_fifo_depth;
u32 tx_fifo_width;
u32 rx_fifo_depth;
- u32 tx_wm;
- u32 rx_wm;
- u32 rx_rfr;
- enum geni_se_xfer_mode xfer_mode;
bool setup;
int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
unsigned int baud;
@@ -113,6 +113,8 @@
u32 *rx_fifo;
u32 loopback;
bool brk;
+
+ unsigned int tx_remaining;
};
static const struct uart_ops qcom_geni_console_pops;
@@ -162,8 +164,7 @@
static ssize_t loopback_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
return snprintf(buf, sizeof(u32), "%d\n", port->loopback);
}
@@ -172,8 +173,7 @@
struct device_attribute *attr, const char *buf,
size_t size)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
+ struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
u32 loopback;
if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) {
@@ -198,10 +198,8 @@
{
struct platform_device *pdev = to_platform_device(uport->dev);
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- struct resource *res;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- uport->membase = devm_ioremap_resource(&pdev->dev, res);
+ uport->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(uport->membase))
return PTR_ERR(uport->membase);
port->se.base = uport->membase;
@@ -221,10 +219,10 @@
unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
u32 geni_ios;
- if (uart_console(uport) || !uart_cts_enabled(uport)) {
+ if (uart_console(uport)) {
mctrl |= TIOCM_CTS;
} else {
- geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS);
+ geni_ios = readl(uport->membase + SE_GENI_IOS);
if (!(geni_ios & IO2_DATA_IN))
mctrl |= TIOCM_CTS;
}
@@ -237,12 +235,12 @@
{
u32 uart_manual_rfr = 0;
- if (uart_console(uport) || !uart_cts_enabled(uport))
+ if (uart_console(uport))
return;
if (!(mctrl & TIOCM_RTS))
uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY;
- writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR);
+ writel(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR);
}
static const char *qcom_geni_serial_get_type(struct uart_port *uport)
@@ -271,9 +269,6 @@
unsigned int fifo_bits;
unsigned long timeout_us = 20000;
- /* Ensure polling is not re-ordered before the prior writes/reads */
- mb();
-
if (uport->private_data) {
port = to_dev_port(uport, uport);
baud = port->baud;
@@ -293,7 +288,7 @@
*/
timeout_us = DIV_ROUND_UP(timeout_us, 10) * 10;
while (timeout_us) {
- reg = readl_relaxed(uport->membase + offset);
+ reg = readl(uport->membase + offset);
if ((bool)(reg & field) == set)
return true;
udelay(10);
@@ -306,7 +301,7 @@
{
u32 m_cmd;
- writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
+ writel(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
m_cmd = UART_START_TX << M_OPCODE_SHFT;
writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
}
@@ -319,13 +314,13 @@
done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_DONE_EN, true);
if (!done) {
- writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
+ writel(M_GENI_CMD_ABORT, uport->membase +
SE_GENI_M_CMD_CTRL_REG);
irq_clear |= M_CMD_ABORT_EN;
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
}
- writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
+ writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
static void qcom_geni_serial_abort_rx(struct uart_port *uport)
@@ -335,8 +330,8 @@
writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_ABORT, false);
- writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
- writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
+ writel(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ writel(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
}
#ifdef CONFIG_CONSOLE_POLL
@@ -345,19 +340,13 @@
u32 rx_fifo;
u32 status;
- status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
- writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+ status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
+ writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
- status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
- writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
+ writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
- /*
- * Ensure the writes to clear interrupts is not re-ordered after
- * reading the data.
- */
- mb();
-
- status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
if (!(status & RX_FIFO_WC_MSK))
return NO_POLL_CHAR;
@@ -368,15 +357,12 @@
static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
unsigned char c)
{
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
-
- writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
qcom_geni_serial_setup_tx(uport, 1);
WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_TX_FIFO_WATERMARK_EN, true));
- writel_relaxed(c, uport->membase + SE_GENI_TX_FIFOn);
- writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
- SE_GENI_M_IRQ_CLEAR);
+ writel(c, uport->membase + SE_GENI_TX_FIFOn);
+ writel(M_TX_FIFO_WATERMARK_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
qcom_geni_serial_poll_tx_done(uport);
}
#endif
@@ -384,7 +370,7 @@
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
{
- writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
+ writel(ch, uport->membase + SE_GENI_TX_FIFOn);
}
static void
@@ -403,7 +389,7 @@
bytes_to_send++;
}
- writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
qcom_geni_serial_setup_tx(uport, bytes_to_send);
for (i = 0; i < count; ) {
size_t chars_to_write = 0;
@@ -421,7 +407,7 @@
chars_to_write = min_t(size_t, count - i, avail / 2);
uart_console_write(uport, s + i, chars_to_write,
qcom_geni_serial_wr_char);
- writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
+ writel(M_TX_FIFO_WATERMARK_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
i += chars_to_write;
}
@@ -435,6 +421,8 @@
struct qcom_geni_serial_port *port;
bool locked = true;
unsigned long flags;
+ u32 geni_status;
+ u32 irq_en;
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
@@ -448,6 +436,8 @@
else
spin_lock_irqsave(&uport->lock, flags);
+ geni_status = readl(uport->membase + SE_GENI_STATUS);
+
/* Cancel the current write to log the fault */
if (!locked) {
geni_se_cancel_m_cmd(&port->se);
@@ -456,14 +446,29 @@
geni_se_abort_m_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
- writel_relaxed(M_CMD_ABORT_EN, uport->membase +
+ writel(M_CMD_ABORT_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
}
- writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
- SE_GENI_M_IRQ_CLEAR);
+ writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
+ } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) {
+ /*
+ * It seems we can't interrupt existing transfers if all data
+ * has been sent, in which case we need to look for done first.
+ */
+ qcom_geni_serial_poll_tx_done(uport);
+
+ if (uart_circ_chars_pending(&uport->state->xmit)) {
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ writel(irq_en | M_TX_FIFO_WATERMARK_EN,
+ uport->membase + SE_GENI_M_IRQ_EN);
+ }
}
__qcom_geni_serial_console_write(uport, s, count);
+
+ if (port->tx_remaining)
+ qcom_geni_serial_setup_tx(uport, port->tx_remaining);
+
if (locked)
spin_unlock_irqrestore(&uport->lock, flags);
}
@@ -495,7 +500,8 @@
continue;
}
- sysrq = uart_handle_sysrq_char(uport, buf[c]);
+ sysrq = uart_prepare_sysrq_char(uport, buf[c]);
+
if (!sysrq)
tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
}
@@ -541,29 +547,20 @@
static void qcom_geni_serial_start_tx(struct uart_port *uport)
{
u32 irq_en;
- struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 status;
- if (port->xfer_mode == GENI_SE_FIFO) {
- /*
- * readl ensures reading & writing of IRQ_EN register
- * is not re-ordered before checking the status of the
- * Serial Engine.
- */
- status = readl(uport->membase + SE_GENI_STATUS);
- if (status & M_GENI_CMD_ACTIVE)
- return;
+ status = readl(uport->membase + SE_GENI_STATUS);
+ if (status & M_GENI_CMD_ACTIVE)
+ return;
- if (!qcom_geni_serial_tx_empty(uport))
- return;
+ if (!qcom_geni_serial_tx_empty(uport))
+ return;
- irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
- writel_relaxed(port->tx_wm, uport->membase +
- SE_GENI_TX_WATERMARK_REG);
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- }
+ writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ writel(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
static void qcom_geni_serial_stop_tx(struct uart_port *uport)
@@ -572,35 +569,24 @@
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en &= ~M_CMD_DONE_EN;
- if (port->xfer_mode == GENI_SE_FIFO) {
- irq_en &= ~M_TX_FIFO_WATERMARK_EN;
- writel_relaxed(0, uport->membase +
- SE_GENI_TX_WATERMARK_REG);
- }
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ 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))
return;
- /*
- * Ensure cancel command write is not re-ordered before checking
- * the status of the Primary Sequencer.
- */
- mb();
-
geni_se_cancel_m_cmd(&port->se);
if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_CANCEL_EN, true)) {
geni_se_abort_m_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
- writel_relaxed(M_CMD_ABORT_EN, uport->membase +
- SE_GENI_M_IRQ_CLEAR);
+ writel(M_CMD_ABORT_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
- writel_relaxed(M_CMD_CANCEL_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_rx(struct uart_port *uport)
@@ -609,27 +595,19 @@
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ status = readl(uport->membase + SE_GENI_STATUS);
if (status & S_GENI_CMD_ACTIVE)
qcom_geni_serial_stop_rx(uport);
- /*
- * Ensure setup command write is not re-ordered before checking
- * the status of the Secondary Sequencer.
- */
- mb();
-
geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
- if (port->xfer_mode == GENI_SE_FIFO) {
- irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
- irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
- writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+ 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_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_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(struct uart_port *uport)
@@ -639,32 +617,24 @@
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 irq_clear = S_CMD_DONE_EN;
- if (port->xfer_mode == GENI_SE_FIFO) {
- irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
- irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
- writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
+ 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_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_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);
- status = readl_relaxed(uport->membase + SE_GENI_STATUS);
+ status = readl(uport->membase + SE_GENI_STATUS);
/* Possible stop rx is called multiple times. */
if (!(status & S_GENI_CMD_ACTIVE))
return;
- /*
- * Ensure cancel command write is not re-ordered before checking
- * the status of the Secondary Sequencer.
- */
- mb();
-
geni_se_cancel_s_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);
- status = readl_relaxed(uport->membase + SE_GENI_STATUS);
- writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ status = readl(uport->membase + SE_GENI_STATUS);
+ writel(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
if (status & S_GENI_CMD_ACTIVE)
qcom_geni_serial_abort_rx(uport);
}
@@ -678,7 +648,7 @@
u32 total_bytes;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ 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) >>
@@ -694,40 +664,51 @@
port->handle_rx(uport, total_bytes, drop);
}
-static void qcom_geni_serial_handle_tx(struct uart_port *uport)
+static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done,
+ bool active)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
struct 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;
- u32 irq_en;
- chunk = uart_circ_chars_pending(xmit);
- status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
- /* Both FIFO and framework buffer are drained */
- if (!chunk && !status) {
+ status = readl(uport->membase + SE_GENI_TX_FIFO_STATUS);
+
+ /* Complete the current tx command before taking newly added data */
+ if (active)
+ pending = port->tx_remaining;
+ else
+ pending = uart_circ_chars_pending(xmit);
+
+ /* All data has been transmitted and acknowledged as received */
+ if (!pending && !status && done) {
qcom_geni_serial_stop_tx(uport);
goto out_write_wakeup;
}
- if (!uart_console(uport)) {
- irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- irq_en &= ~(M_TX_FIFO_WATERMARK_EN);
- writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG);
- writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
- }
+ avail = port->tx_fifo_depth - (status & TX_FIFO_WC);
+ avail *= port->tx_bytes_pw;
- avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = xmit->tail;
- chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail);
+ chunk = min(avail, pending);
if (!chunk)
goto out_write_wakeup;
- qcom_geni_serial_setup_tx(uport, chunk);
+ if (!port->tx_remaining) {
+ qcom_geni_serial_setup_tx(uport, pending);
+ port->tx_remaining = pending;
+
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
+ writel(irq_en | M_TX_FIFO_WATERMARK_EN,
+ uport->membase + SE_GENI_M_IRQ_EN);
+ }
remaining = chunk;
for (i = 0; i < chunk; ) {
@@ -737,32 +718,50 @@
memset(buf, 0, ARRAY_SIZE(buf));
tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw);
- for (c = 0; c < tx_bytes ; c++)
- buf[c] = xmit->buf[tail + c];
+
+ for (c = 0; c < tx_bytes ; c++) {
+ buf[c] = xmit->buf[tail++];
+ tail &= UART_XMIT_SIZE - 1;
+ }
iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1);
i += tx_bytes;
- tail += tx_bytes;
uport->icount.tx += tx_bytes;
remaining -= tx_bytes;
+ port->tx_remaining -= tx_bytes;
}
- xmit->tail = tail & (UART_XMIT_SIZE - 1);
- if (uart_console(uport))
- qcom_geni_serial_poll_tx_done(uport);
+ xmit->tail = tail;
+
+ /*
+ * The tx fifo watermark is level triggered and latched. Though we had
+ * cleared it in qcom_geni_serial_isr it will have already reasserted
+ * so we must clear it again here after our writes.
+ */
+ writel(M_TX_FIFO_WATERMARK_EN,
+ uport->membase + SE_GENI_M_IRQ_CLEAR);
+
out_write_wakeup:
+ if (!port->tx_remaining) {
+ irq_en = readl(uport->membase + SE_GENI_M_IRQ_EN);
+ if (irq_en & M_TX_FIFO_WATERMARK_EN)
+ writel(irq_en & ~M_TX_FIFO_WATERMARK_EN,
+ uport->membase + SE_GENI_M_IRQ_EN);
+ }
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport);
}
static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
{
- unsigned int m_irq_status;
- unsigned int s_irq_status;
+ u32 m_irq_en;
+ u32 m_irq_status;
+ u32 s_irq_status;
+ u32 geni_status;
struct uart_port *uport = dev;
unsigned long flags;
- unsigned int m_irq_en;
bool drop_rx = false;
struct tty_port *tport = &uport->state->port;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
@@ -771,11 +770,12 @@
return IRQ_NONE;
spin_lock_irqsave(&uport->lock, flags);
- m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
- s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
- m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
- writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
- writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ m_irq_status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
+ s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
+ geni_status = readl(uport->membase + SE_GENI_STATUS);
+ 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);
if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
goto out_unlock;
@@ -785,9 +785,9 @@
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
}
- if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
- m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
- qcom_geni_serial_handle_tx(uport);
+ 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)
@@ -804,7 +804,8 @@
qcom_geni_serial_handle_rx(uport, drop_rx);
out_unlock:
- spin_unlock_irqrestore(&uport->lock, flags);
+ uart_unlock_and_check_sysrq(uport, flags);
+
return IRQ_HANDLED;
}
@@ -820,17 +821,6 @@
(port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
}
-static void set_rfr_wm(struct qcom_geni_serial_port *port)
-{
- /*
- * Set RFR (Flow off) to FIFO_DEPTH - 2.
- * RX WM level at 10% RX_FIFO_DEPTH.
- * TX WM level at 10% TX_FIFO_DEPTH.
- */
- port->rx_rfr = port->rx_fifo_depth - 2;
- port->rx_wm = UART_CONSOLE_RX_WM;
- port->tx_wm = DEF_TX_WM;
-}
static void qcom_geni_serial_shutdown(struct uart_port *uport)
{
@@ -850,23 +840,40 @@
static int qcom_geni_serial_port_setup(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
- unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+ u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
+ u32 proto;
- set_rfr_wm(port);
- writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
+ if (uart_console(uport)) {
+ port->tx_bytes_pw = 1;
+ port->rx_bytes_pw = CONSOLE_RX_BYTES_PW;
+ } else {
+ port->tx_bytes_pw = 4;
+ port->rx_bytes_pw = 4;
+ }
+
+ proto = geni_se_read_proto(&port->se);
+ if (proto != GENI_SE_UART) {
+ dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
+ return -ENXIO;
+ }
+
+ qcom_geni_serial_stop_rx(uport);
+
+ get_tx_fifo_size(port);
+
+ writel(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
/*
* Make an unconditional cancel on the main sequencer to reset
* it else we could end up in data loss scenarios.
*/
- port->xfer_mode = GENI_SE_FIFO;
if (uart_console(uport))
qcom_geni_serial_poll_tx_done(uport);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
false, true, false);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
false, false, true);
- geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
- geni_se_select_mode(&port->se, port->xfer_mode);
+ geni_se_init(&port->se, UART_RX_WM, port->rx_fifo_depth - 2);
+ geni_se_select_mode(&port->se, GENI_SE_FIFO);
if (!uart_console(uport)) {
port->rx_fifo = devm_kcalloc(uport->dev,
port->rx_fifo_depth, sizeof(u32), GFP_KERNEL);
@@ -874,30 +881,19 @@
return -ENOMEM;
}
port->setup = true;
+
return 0;
}
static int qcom_geni_serial_startup(struct uart_port *uport)
{
int ret;
- u32 proto;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
scnprintf(port->name, sizeof(port->name),
"qcom_serial_%s%d",
(uart_console(uport) ? "console" : "uart"), uport->line);
- if (!uart_console(uport)) {
- port->tx_bytes_pw = 4;
- port->rx_bytes_pw = RX_BYTES_PW;
- }
- proto = geni_se_read_proto(&port->se);
- if (proto != GENI_SE_UART) {
- dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto);
- return -ENXIO;
- }
-
- get_tx_fifo_size(port);
if (!port->setup) {
ret = qcom_geni_serial_port_setup(uport);
if (ret)
@@ -922,12 +918,13 @@
return 0;
}
-static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
+static unsigned long get_clk_div_rate(unsigned int baud,
+ unsigned int sampling_rate, unsigned int *clk_div)
{
unsigned long ser_clk;
unsigned long desired_clk;
- desired_clk = baud * UART_OVERSAMPLING;
+ desired_clk = baud * sampling_rate;
ser_clk = get_clk_cfg(desired_clk);
if (!ser_clk) {
pr_err("%s: Can't find matching DFS entry for baud %d\n",
@@ -943,22 +940,30 @@
struct ktermios *termios, struct ktermios *old)
{
unsigned int baud;
- unsigned int bits_per_char;
- unsigned int tx_trans_cfg;
- unsigned int tx_parity_cfg;
- unsigned int rx_trans_cfg;
- unsigned int rx_parity_cfg;
- unsigned int stop_bit_len;
+ u32 bits_per_char;
+ u32 tx_trans_cfg;
+ u32 tx_parity_cfg;
+ u32 rx_trans_cfg;
+ u32 rx_parity_cfg;
+ u32 stop_bit_len;
unsigned int clk_div;
- unsigned long ser_clk_cfg;
+ u32 ser_clk_cfg;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
unsigned long clk_rate;
+ u32 ver, sampling_rate;
qcom_geni_serial_stop_rx(uport);
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
port->baud = baud;
- clk_rate = get_clk_div_rate(baud, &clk_div);
+
+ sampling_rate = UART_OVERSAMPLING;
+ /* Sampling rate is halved for IP versions >= 2.5 */
+ ver = geni_se_get_qup_hw_version(&port->se);
+ if (GENI_SE_VERSION_MAJOR(ver) >= 2 && GENI_SE_VERSION_MINOR(ver) >= 5)
+ sampling_rate /= 2;
+
+ clk_rate = get_clk_div_rate(baud, sampling_rate, &clk_div);
if (!clk_rate)
goto out_restart_rx;
@@ -968,10 +973,10 @@
ser_clk_cfg |= clk_div << CLK_DIV_SHFT;
/* parity */
- tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
- tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
- rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
- rx_parity_cfg = readl_relaxed(uport->membase + SE_UART_RX_PARITY_CFG);
+ tx_trans_cfg = readl(uport->membase + SE_UART_TX_TRANS_CFG);
+ tx_parity_cfg = readl(uport->membase + SE_UART_TX_PARITY_CFG);
+ rx_trans_cfg = readl(uport->membase + SE_UART_RX_TRANS_CFG);
+ rx_parity_cfg = readl(uport->membase + SE_UART_RX_PARITY_CFG);
if (termios->c_cflag & PARENB) {
tx_trans_cfg |= UART_TX_PAR_EN;
rx_trans_cfg |= UART_RX_PAR_EN;
@@ -1027,17 +1032,17 @@
uart_update_timeout(uport, termios->c_cflag, baud);
if (!uart_console(uport))
- writel_relaxed(port->loopback,
+ writel(port->loopback,
uport->membase + SE_UART_LOOPBACK_CFG);
- writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
- writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
- writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
- writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
- writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
- writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
- writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
- writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
- writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
+ writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
+ writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
+ writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
+ writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
+ writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
+ writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
+ writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
+ writel(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
+ writel(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
out_restart_rx:
qcom_geni_serial_start_rx(uport);
}
@@ -1052,10 +1057,11 @@
{
struct uart_port *uport;
struct qcom_geni_serial_port *port;
- int baud;
+ int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int ret;
if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
return -ENXIO;
@@ -1071,21 +1077,10 @@
if (unlikely(!uport->membase))
return -ENXIO;
- if (geni_se_resources_on(&port->se)) {
- dev_err(port->se.dev, "Error turning on resources\n");
- return -ENXIO;
- }
-
- if (unlikely(geni_se_read_proto(&port->se) != GENI_SE_UART)) {
- geni_se_resources_off(&port->se);
- return -ENXIO;
- }
-
if (!port->setup) {
- port->tx_bytes_pw = 1;
- port->rx_bytes_pw = RX_BYTES_PW;
- qcom_geni_serial_stop_rx(uport);
- qcom_geni_serial_port_setup(uport);
+ ret = qcom_geni_serial_port_setup(uport);
+ if (ret)
+ return ret;
}
if (options)
@@ -1138,13 +1133,13 @@
geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2);
geni_se_select_mode(&se, GENI_SE_FIFO);
- writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
- writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
- writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
- writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
- writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
- writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
- writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
+ writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
+ writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
+ writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
+ writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
+ writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
+ writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
+ writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
dev->con->write = qcom_geni_serial_earlycon_write;
dev->con->setup = NULL;
@@ -1203,11 +1198,12 @@
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
+ /* If we've never been called, treat it as off */
+ if (old_state == UART_PM_STATE_UNDEFINED)
+ old_state = UART_PM_STATE_OFF;
+
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
geni_se_resources_on(&port->se);
- else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON &&
- old_state == UART_PM_STATE_UNDEFINED))
- geni_se_resources_on(&port->se);
else if (new_state == UART_PM_STATE_OFF &&
old_state == UART_PM_STATE_ON)
geni_se_resources_off(&port->se);
@@ -1263,14 +1259,12 @@
if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart"))
console = true;
- if (pdev->dev.of_node) {
- if (console) {
- drv = &qcom_geni_console_driver;
- line = of_alias_get_id(pdev->dev.of_node, "serial");
- } else {
- drv = &qcom_geni_uart_driver;
- line = of_alias_get_id(pdev->dev.of_node, "hsuart");
- }
+ if (console) {
+ drv = &qcom_geni_console_driver;
+ line = of_alias_get_id(pdev->dev.of_node, "serial");
+ } else {
+ drv = &qcom_geni_uart_driver;
+ line = of_alias_get_id(pdev->dev.of_node, "hsuart");
}
port = get_port_from_line(line, console);
@@ -1304,10 +1298,8 @@
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Failed to get IRQ %d\n", irq);
+ if (irq < 0)
return irq;
- }
uport->irq = irq;
uport->private_data = drv;
@@ -1327,49 +1319,25 @@
return 0;
}
-static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
+static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
- if (uart_console(uport)) {
- uart_suspend_port(uport->private_data, uport);
- } else {
- struct uart_state *state = uport->state;
- /*
- * If the port is open, deny system suspend.
- */
- if (state->pm_state == UART_PM_STATE_ON)
- return -EBUSY;
- }
-
- return 0;
+ return uart_suspend_port(uport->private_data, uport);
}
-static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
+static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
- if (uart_console(uport) &&
- console_suspend_enabled && uport->suspended) {
- uart_resume_port(uport->private_data, uport);
- /*
- * uart_suspend_port() invokes port shutdown which in turn
- * frees the irq. uart_resume_port invokes port startup which
- * performs request_irq. The request_irq auto-enables the IRQ.
- * In addition, resume_noirq implicitly enables the IRQ and
- * leads to an unbalanced IRQ enable warning. Disable the IRQ
- * before returning so that the warning is suppressed.
- */
- disable_irq(uport->irq);
- }
- return 0;
+ return uart_resume_port(uport->private_data, uport);
}
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq,
- qcom_geni_serial_sys_resume_noirq)
+ SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend,
+ qcom_geni_serial_sys_resume)
};
static const struct of_device_id qcom_geni_serial_match_table[] = {
diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c
new file mode 100644
index 0000000..ff9a27d
--- /dev/null
+++ b/drivers/tty/serial/rda-uart.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * RDA8810PL serial device driver
+ *
+ * Copyright RDA Microelectronics Company Limited
+ * Copyright (c) 2017 Andreas Färber
+ * Copyright (c) 2018 Manivannan Sadhasivam
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define RDA_UART_PORT_NUM 3
+#define RDA_UART_DEV_NAME "ttyRDA"
+
+#define RDA_UART_CTRL 0x00
+#define RDA_UART_STATUS 0x04
+#define RDA_UART_RXTX_BUFFER 0x08
+#define RDA_UART_IRQ_MASK 0x0c
+#define RDA_UART_IRQ_CAUSE 0x10
+#define RDA_UART_IRQ_TRIGGERS 0x14
+#define RDA_UART_CMD_SET 0x18
+#define RDA_UART_CMD_CLR 0x1c
+
+/* UART_CTRL Bits */
+#define RDA_UART_ENABLE BIT(0)
+#define RDA_UART_DBITS_8 BIT(1)
+#define RDA_UART_TX_SBITS_2 BIT(2)
+#define RDA_UART_PARITY_EN BIT(3)
+#define RDA_UART_PARITY(x) (((x) & 0x3) << 4)
+#define RDA_UART_PARITY_ODD RDA_UART_PARITY(0)
+#define RDA_UART_PARITY_EVEN RDA_UART_PARITY(1)
+#define RDA_UART_PARITY_SPACE RDA_UART_PARITY(2)
+#define RDA_UART_PARITY_MARK RDA_UART_PARITY(3)
+#define RDA_UART_DIV_MODE BIT(20)
+#define RDA_UART_IRDA_EN BIT(21)
+#define RDA_UART_DMA_EN BIT(22)
+#define RDA_UART_FLOW_CNT_EN BIT(23)
+#define RDA_UART_LOOP_BACK_EN BIT(24)
+#define RDA_UART_RX_LOCK_ERR BIT(25)
+#define RDA_UART_RX_BREAK_LEN(x) (((x) & 0xf) << 28)
+
+/* UART_STATUS Bits */
+#define RDA_UART_RX_FIFO(x) (((x) & 0x7f) << 0)
+#define RDA_UART_RX_FIFO_MASK (0x7f << 0)
+#define RDA_UART_TX_FIFO(x) (((x) & 0x1f) << 8)
+#define RDA_UART_TX_FIFO_MASK (0x1f << 8)
+#define RDA_UART_TX_ACTIVE BIT(14)
+#define RDA_UART_RX_ACTIVE BIT(15)
+#define RDA_UART_RX_OVERFLOW_ERR BIT(16)
+#define RDA_UART_TX_OVERFLOW_ERR BIT(17)
+#define RDA_UART_RX_PARITY_ERR BIT(18)
+#define RDA_UART_RX_FRAMING_ERR BIT(19)
+#define RDA_UART_RX_BREAK_INT BIT(20)
+#define RDA_UART_DCTS BIT(24)
+#define RDA_UART_CTS BIT(25)
+#define RDA_UART_DTR BIT(28)
+#define RDA_UART_CLK_ENABLED BIT(31)
+
+/* UART_RXTX_BUFFER Bits */
+#define RDA_UART_RX_DATA(x) (((x) & 0xff) << 0)
+#define RDA_UART_TX_DATA(x) (((x) & 0xff) << 0)
+
+/* UART_IRQ_MASK Bits */
+#define RDA_UART_TX_MODEM_STATUS BIT(0)
+#define RDA_UART_RX_DATA_AVAILABLE BIT(1)
+#define RDA_UART_TX_DATA_NEEDED BIT(2)
+#define RDA_UART_RX_TIMEOUT BIT(3)
+#define RDA_UART_RX_LINE_ERR BIT(4)
+#define RDA_UART_TX_DMA_DONE BIT(5)
+#define RDA_UART_RX_DMA_DONE BIT(6)
+#define RDA_UART_RX_DMA_TIMEOUT BIT(7)
+#define RDA_UART_DTR_RISE BIT(8)
+#define RDA_UART_DTR_FALL BIT(9)
+
+/* UART_IRQ_CAUSE Bits */
+#define RDA_UART_TX_MODEM_STATUS_U BIT(16)
+#define RDA_UART_RX_DATA_AVAILABLE_U BIT(17)
+#define RDA_UART_TX_DATA_NEEDED_U BIT(18)
+#define RDA_UART_RX_TIMEOUT_U BIT(19)
+#define RDA_UART_RX_LINE_ERR_U BIT(20)
+#define RDA_UART_TX_DMA_DONE_U BIT(21)
+#define RDA_UART_RX_DMA_DONE_U BIT(22)
+#define RDA_UART_RX_DMA_TIMEOUT_U BIT(23)
+#define RDA_UART_DTR_RISE_U BIT(24)
+#define RDA_UART_DTR_FALL_U BIT(25)
+
+/* UART_TRIGGERS Bits */
+#define RDA_UART_RX_TRIGGER(x) (((x) & 0x1f) << 0)
+#define RDA_UART_TX_TRIGGER(x) (((x) & 0xf) << 8)
+#define RDA_UART_AFC_LEVEL(x) (((x) & 0x1f) << 16)
+
+/* UART_CMD_SET Bits */
+#define RDA_UART_RI BIT(0)
+#define RDA_UART_DCD BIT(1)
+#define RDA_UART_DSR BIT(2)
+#define RDA_UART_TX_BREAK_CONTROL BIT(3)
+#define RDA_UART_TX_FINISH_N_WAIT BIT(4)
+#define RDA_UART_RTS BIT(5)
+#define RDA_UART_RX_FIFO_RESET BIT(6)
+#define RDA_UART_TX_FIFO_RESET BIT(7)
+
+#define RDA_UART_TX_FIFO_SIZE 16
+
+static struct uart_driver rda_uart_driver;
+
+struct rda_uart_port {
+ struct uart_port port;
+ struct clk *clk;
+};
+
+#define to_rda_uart_port(port) container_of(port, struct rda_uart_port, port)
+
+static struct rda_uart_port *rda_uart_ports[RDA_UART_PORT_NUM];
+
+static inline void rda_uart_write(struct uart_port *port, u32 val,
+ unsigned int off)
+{
+ writel(val, port->membase + off);
+}
+
+static inline u32 rda_uart_read(struct uart_port *port, unsigned int off)
+{
+ return readl(port->membase + off);
+}
+
+static unsigned int rda_uart_tx_empty(struct uart_port *port)
+{
+ unsigned long flags;
+ unsigned int ret;
+ u32 val;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ val = rda_uart_read(port, RDA_UART_STATUS);
+ ret = (val & RDA_UART_TX_FIFO_MASK) ? TIOCSER_TEMT : 0;
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return ret;
+}
+
+static unsigned int rda_uart_get_mctrl(struct uart_port *port)
+{
+ unsigned int mctrl = 0;
+ u32 cmd_set, status;
+
+ cmd_set = rda_uart_read(port, RDA_UART_CMD_SET);
+ status = rda_uart_read(port, RDA_UART_STATUS);
+ if (cmd_set & RDA_UART_RTS)
+ mctrl |= TIOCM_RTS;
+ if (!(status & RDA_UART_CTS))
+ mctrl |= TIOCM_CTS;
+
+ return mctrl;
+}
+
+static void rda_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ u32 val;
+
+ if (mctrl & TIOCM_RTS) {
+ val = rda_uart_read(port, RDA_UART_CMD_SET);
+ rda_uart_write(port, (val | RDA_UART_RTS), RDA_UART_CMD_SET);
+ } else {
+ /* Clear RTS to stop to receive. */
+ val = rda_uart_read(port, RDA_UART_CMD_CLR);
+ rda_uart_write(port, (val | RDA_UART_RTS), RDA_UART_CMD_CLR);
+ }
+
+ val = rda_uart_read(port, RDA_UART_CTRL);
+
+ if (mctrl & TIOCM_LOOP)
+ val |= RDA_UART_LOOP_BACK_EN;
+ else
+ val &= ~RDA_UART_LOOP_BACK_EN;
+
+ rda_uart_write(port, val, RDA_UART_CTRL);
+}
+
+static void rda_uart_stop_tx(struct uart_port *port)
+{
+ u32 val;
+
+ val = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ val &= ~RDA_UART_TX_DATA_NEEDED;
+ rda_uart_write(port, val, RDA_UART_IRQ_MASK);
+
+ val = rda_uart_read(port, RDA_UART_CMD_SET);
+ val |= RDA_UART_TX_FIFO_RESET;
+ rda_uart_write(port, val, RDA_UART_CMD_SET);
+}
+
+static void rda_uart_stop_rx(struct uart_port *port)
+{
+ u32 val;
+
+ val = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ val &= ~(RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT);
+ rda_uart_write(port, val, RDA_UART_IRQ_MASK);
+
+ /* Read Rx buffer before reset to avoid Rx timeout interrupt */
+ val = rda_uart_read(port, RDA_UART_RXTX_BUFFER);
+
+ val = rda_uart_read(port, RDA_UART_CMD_SET);
+ val |= RDA_UART_RX_FIFO_RESET;
+ rda_uart_write(port, val, RDA_UART_CMD_SET);
+}
+
+static void rda_uart_start_tx(struct uart_port *port)
+{
+ u32 val;
+
+ if (uart_tx_stopped(port)) {
+ rda_uart_stop_tx(port);
+ return;
+ }
+
+ val = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ val |= RDA_UART_TX_DATA_NEEDED;
+ rda_uart_write(port, val, RDA_UART_IRQ_MASK);
+}
+
+static void rda_uart_change_baudrate(struct rda_uart_port *rda_port,
+ unsigned long baud)
+{
+ clk_set_rate(rda_port->clk, baud * 8);
+}
+
+static void rda_uart_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct rda_uart_port *rda_port = to_rda_uart_port(port);
+ unsigned long flags;
+ unsigned int ctrl, cmd_set, cmd_clr, triggers;
+ unsigned int baud;
+ u32 irq_mask;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ baud = uart_get_baud_rate(port, termios, old, 9600, port->uartclk / 4);
+ rda_uart_change_baudrate(rda_port, baud);
+
+ ctrl = rda_uart_read(port, RDA_UART_CTRL);
+ cmd_set = rda_uart_read(port, RDA_UART_CMD_SET);
+ cmd_clr = rda_uart_read(port, RDA_UART_CMD_CLR);
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ case CS6:
+ dev_warn(port->dev, "bit size not supported, using 7 bits\n");
+ /* Fall through */
+ case CS7:
+ ctrl &= ~RDA_UART_DBITS_8;
+ break;
+ default:
+ ctrl |= RDA_UART_DBITS_8;
+ break;
+ }
+
+ /* stop bits */
+ if (termios->c_cflag & CSTOPB)
+ ctrl |= RDA_UART_TX_SBITS_2;
+ else
+ ctrl &= ~RDA_UART_TX_SBITS_2;
+
+ /* parity check */
+ if (termios->c_cflag & PARENB) {
+ ctrl |= RDA_UART_PARITY_EN;
+
+ /* Mark or Space parity */
+ if (termios->c_cflag & CMSPAR) {
+ if (termios->c_cflag & PARODD)
+ ctrl |= RDA_UART_PARITY_MARK;
+ else
+ ctrl |= RDA_UART_PARITY_SPACE;
+ } else if (termios->c_cflag & PARODD) {
+ ctrl |= RDA_UART_PARITY_ODD;
+ } else {
+ ctrl |= RDA_UART_PARITY_EVEN;
+ }
+ } else {
+ ctrl &= ~RDA_UART_PARITY_EN;
+ }
+
+ /* Hardware handshake (RTS/CTS) */
+ if (termios->c_cflag & CRTSCTS) {
+ ctrl |= RDA_UART_FLOW_CNT_EN;
+ cmd_set |= RDA_UART_RTS;
+ } else {
+ ctrl &= ~RDA_UART_FLOW_CNT_EN;
+ cmd_clr |= RDA_UART_RTS;
+ }
+
+ ctrl |= RDA_UART_ENABLE;
+ ctrl &= ~RDA_UART_DMA_EN;
+
+ triggers = (RDA_UART_AFC_LEVEL(20) | RDA_UART_RX_TRIGGER(16));
+ irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ rda_uart_write(port, 0, RDA_UART_IRQ_MASK);
+
+ rda_uart_write(port, triggers, RDA_UART_IRQ_TRIGGERS);
+ rda_uart_write(port, ctrl, RDA_UART_CTRL);
+ rda_uart_write(port, cmd_set, RDA_UART_CMD_SET);
+ rda_uart_write(port, cmd_clr, RDA_UART_CMD_CLR);
+
+ rda_uart_write(port, irq_mask, RDA_UART_IRQ_MASK);
+
+ /* Don't rewrite B0 */
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
+ /* update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void rda_uart_send_chars(struct uart_port *port)
+{
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned int ch;
+ u32 val;
+
+ if (uart_tx_stopped(port))
+ return;
+
+ if (port->x_char) {
+ while (!(rda_uart_read(port, RDA_UART_STATUS) &
+ RDA_UART_TX_FIFO_MASK))
+ cpu_relax();
+
+ rda_uart_write(port, port->x_char, RDA_UART_RXTX_BUFFER);
+ port->icount.tx++;
+ port->x_char = 0;
+ }
+
+ while (rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK) {
+ if (uart_circ_empty(xmit))
+ break;
+
+ ch = xmit->buf[xmit->tail];
+ rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER);
+ xmit->tail = (xmit->tail + 1) & (SERIAL_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (!uart_circ_empty(xmit)) {
+ /* Re-enable Tx FIFO interrupt */
+ val = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ val |= RDA_UART_TX_DATA_NEEDED;
+ rda_uart_write(port, val, RDA_UART_IRQ_MASK);
+ }
+}
+
+static void rda_uart_receive_chars(struct uart_port *port)
+{
+ u32 status, val;
+
+ status = rda_uart_read(port, RDA_UART_STATUS);
+ while ((status & RDA_UART_RX_FIFO_MASK)) {
+ char flag = TTY_NORMAL;
+
+ if (status & RDA_UART_RX_PARITY_ERR) {
+ port->icount.parity++;
+ flag = TTY_PARITY;
+ }
+
+ if (status & RDA_UART_RX_FRAMING_ERR) {
+ port->icount.frame++;
+ flag = TTY_FRAME;
+ }
+
+ if (status & RDA_UART_RX_OVERFLOW_ERR) {
+ port->icount.overrun++;
+ flag = TTY_OVERRUN;
+ }
+
+ val = rda_uart_read(port, RDA_UART_RXTX_BUFFER);
+ val &= 0xff;
+
+ port->icount.rx++;
+ tty_insert_flip_char(&port->state->port, val, flag);
+
+ status = rda_uart_read(port, RDA_UART_STATUS);
+ }
+
+ spin_unlock(&port->lock);
+ tty_flip_buffer_push(&port->state->port);
+ spin_lock(&port->lock);
+}
+
+static irqreturn_t rda_interrupt(int irq, void *dev_id)
+{
+ struct uart_port *port = dev_id;
+ unsigned long flags;
+ u32 val, irq_mask;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ /* Clear IRQ cause */
+ val = rda_uart_read(port, RDA_UART_IRQ_CAUSE);
+ rda_uart_write(port, val, RDA_UART_IRQ_CAUSE);
+
+ if (val & (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT))
+ rda_uart_receive_chars(port);
+
+ if (val & (RDA_UART_TX_DATA_NEEDED)) {
+ irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ irq_mask &= ~RDA_UART_TX_DATA_NEEDED;
+ rda_uart_write(port, irq_mask, RDA_UART_IRQ_MASK);
+
+ rda_uart_send_chars(port);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int rda_uart_startup(struct uart_port *port)
+{
+ unsigned long flags;
+ int ret;
+ u32 val;
+
+ spin_lock_irqsave(&port->lock, flags);
+ rda_uart_write(port, 0, RDA_UART_IRQ_MASK);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ ret = request_irq(port->irq, rda_interrupt, IRQF_NO_SUSPEND,
+ "rda-uart", port);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ val = rda_uart_read(port, RDA_UART_CTRL);
+ val |= RDA_UART_ENABLE;
+ rda_uart_write(port, val, RDA_UART_CTRL);
+
+ /* enable rx interrupt */
+ val = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ val |= (RDA_UART_RX_DATA_AVAILABLE | RDA_UART_RX_TIMEOUT);
+ rda_uart_write(port, val, RDA_UART_IRQ_MASK);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return 0;
+}
+
+static void rda_uart_shutdown(struct uart_port *port)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ rda_uart_stop_tx(port);
+ rda_uart_stop_rx(port);
+
+ val = rda_uart_read(port, RDA_UART_CTRL);
+ val &= ~RDA_UART_ENABLE;
+ rda_uart_write(port, val, RDA_UART_CTRL);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *rda_uart_type(struct uart_port *port)
+{
+ return (port->type == PORT_RDA) ? "rda-uart" : NULL;
+}
+
+static int rda_uart_request_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!devm_request_mem_region(port->dev, port->mapbase,
+ resource_size(res), dev_name(port->dev)))
+ return -EBUSY;
+
+ if (port->flags & UPF_IOREMAP) {
+ port->membase = devm_ioremap_nocache(port->dev, port->mapbase,
+ resource_size(res));
+ if (!port->membase)
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void rda_uart_config_port(struct uart_port *port, int flags)
+{
+ unsigned long irq_flags;
+
+ if (flags & UART_CONFIG_TYPE) {
+ port->type = PORT_RDA;
+ rda_uart_request_port(port);
+ }
+
+ spin_lock_irqsave(&port->lock, irq_flags);
+
+ /* Clear mask, so no surprise interrupts. */
+ rda_uart_write(port, 0, RDA_UART_IRQ_MASK);
+
+ /* Clear status register */
+ rda_uart_write(port, 0, RDA_UART_STATUS);
+
+ spin_unlock_irqrestore(&port->lock, irq_flags);
+}
+
+static void rda_uart_release_port(struct uart_port *port)
+{
+ struct platform_device *pdev = to_platform_device(port->dev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return;
+
+ if (port->flags & UPF_IOREMAP) {
+ devm_release_mem_region(port->dev, port->mapbase,
+ resource_size(res));
+ devm_iounmap(port->dev, port->membase);
+ port->membase = NULL;
+ }
+}
+
+static int rda_uart_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ if (port->type != PORT_RDA)
+ return -EINVAL;
+
+ if (port->irq != ser->irq)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct uart_ops rda_uart_ops = {
+ .tx_empty = rda_uart_tx_empty,
+ .get_mctrl = rda_uart_get_mctrl,
+ .set_mctrl = rda_uart_set_mctrl,
+ .start_tx = rda_uart_start_tx,
+ .stop_tx = rda_uart_stop_tx,
+ .stop_rx = rda_uart_stop_rx,
+ .startup = rda_uart_startup,
+ .shutdown = rda_uart_shutdown,
+ .set_termios = rda_uart_set_termios,
+ .type = rda_uart_type,
+ .request_port = rda_uart_request_port,
+ .release_port = rda_uart_release_port,
+ .config_port = rda_uart_config_port,
+ .verify_port = rda_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_RDA_CONSOLE
+
+static void rda_console_putchar(struct uart_port *port, int ch)
+{
+ if (!port->membase)
+ return;
+
+ while (!(rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK))
+ cpu_relax();
+
+ rda_uart_write(port, ch, RDA_UART_RXTX_BUFFER);
+}
+
+static void rda_uart_port_write(struct uart_port *port, const char *s,
+ u_int count)
+{
+ u32 old_irq_mask;
+ unsigned long flags;
+ int locked;
+
+ local_irq_save(flags);
+
+ if (port->sysrq) {
+ locked = 0;
+ } else if (oops_in_progress) {
+ locked = spin_trylock(&port->lock);
+ } else {
+ spin_lock(&port->lock);
+ locked = 1;
+ }
+
+ old_irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK);
+ rda_uart_write(port, 0, RDA_UART_IRQ_MASK);
+
+ uart_console_write(port, s, count, rda_console_putchar);
+
+ /* wait until all contents have been sent out */
+ while (!(rda_uart_read(port, RDA_UART_STATUS) & RDA_UART_TX_FIFO_MASK))
+ cpu_relax();
+
+ rda_uart_write(port, old_irq_mask, RDA_UART_IRQ_MASK);
+
+ if (locked)
+ spin_unlock(&port->lock);
+
+ local_irq_restore(flags);
+}
+
+static void rda_uart_console_write(struct console *co, const char *s,
+ u_int count)
+{
+ struct rda_uart_port *rda_port;
+
+ rda_port = rda_uart_ports[co->index];
+ if (!rda_port)
+ return;
+
+ rda_uart_port_write(&rda_port->port, s, count);
+}
+
+static int rda_uart_console_setup(struct console *co, char *options)
+{
+ struct rda_uart_port *rda_port;
+ int baud = 921600;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= RDA_UART_PORT_NUM)
+ return -EINVAL;
+
+ rda_port = rda_uart_ports[co->index];
+ if (!rda_port || !rda_port->port.membase)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&rda_port->port, co, baud, parity, bits, flow);
+}
+
+static struct console rda_uart_console = {
+ .name = RDA_UART_DEV_NAME,
+ .write = rda_uart_console_write,
+ .device = uart_console_device,
+ .setup = rda_uart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &rda_uart_driver,
+};
+
+static int __init rda_uart_console_init(void)
+{
+ register_console(&rda_uart_console);
+
+ return 0;
+}
+console_initcall(rda_uart_console_init);
+
+static void rda_uart_early_console_write(struct console *co,
+ const char *s,
+ u_int count)
+{
+ struct earlycon_device *dev = co->data;
+
+ rda_uart_port_write(&dev->port, s, count);
+}
+
+static int __init
+rda_uart_early_console_setup(struct earlycon_device *device, const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = rda_uart_early_console_write;
+
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(rda, "rda,8810pl-uart",
+ rda_uart_early_console_setup);
+
+#define RDA_UART_CONSOLE (&rda_uart_console)
+#else
+#define RDA_UART_CONSOLE NULL
+#endif /* CONFIG_SERIAL_RDA_CONSOLE */
+
+static struct uart_driver rda_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "rda-uart",
+ .dev_name = RDA_UART_DEV_NAME,
+ .nr = RDA_UART_PORT_NUM,
+ .cons = RDA_UART_CONSOLE,
+};
+
+static const struct of_device_id rda_uart_dt_matches[] = {
+ { .compatible = "rda,8810pl-uart" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rda_uart_dt_matches);
+
+static int rda_uart_probe(struct platform_device *pdev)
+{
+ struct resource *res_mem;
+ struct rda_uart_port *rda_port;
+ int ret, irq;
+
+ if (pdev->dev.of_node)
+ pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
+
+ if (pdev->id < 0 || pdev->id >= RDA_UART_PORT_NUM) {
+ dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+ return -EINVAL;
+ }
+
+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res_mem) {
+ dev_err(&pdev->dev, "could not get mem\n");
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ if (rda_uart_ports[pdev->id]) {
+ dev_err(&pdev->dev, "port %d already allocated\n", pdev->id);
+ return -EBUSY;
+ }
+
+ rda_port = devm_kzalloc(&pdev->dev, sizeof(*rda_port), GFP_KERNEL);
+ if (!rda_port)
+ return -ENOMEM;
+
+ rda_port->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rda_port->clk)) {
+ dev_err(&pdev->dev, "could not get clk\n");
+ return PTR_ERR(rda_port->clk);
+ }
+
+ rda_port->port.dev = &pdev->dev;
+ rda_port->port.regshift = 0;
+ rda_port->port.line = pdev->id;
+ rda_port->port.type = PORT_RDA;
+ rda_port->port.iotype = UPIO_MEM;
+ rda_port->port.mapbase = res_mem->start;
+ rda_port->port.irq = irq;
+ rda_port->port.uartclk = clk_get_rate(rda_port->clk);
+ if (rda_port->port.uartclk == 0) {
+ dev_err(&pdev->dev, "clock rate is zero\n");
+ return -EINVAL;
+ }
+ rda_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+ UPF_LOW_LATENCY;
+ rda_port->port.x_char = 0;
+ rda_port->port.fifosize = RDA_UART_TX_FIFO_SIZE;
+ rda_port->port.ops = &rda_uart_ops;
+
+ rda_uart_ports[pdev->id] = rda_port;
+ platform_set_drvdata(pdev, rda_port);
+
+ ret = uart_add_one_port(&rda_uart_driver, &rda_port->port);
+ if (ret)
+ rda_uart_ports[pdev->id] = NULL;
+
+ return ret;
+}
+
+static int rda_uart_remove(struct platform_device *pdev)
+{
+ struct rda_uart_port *rda_port = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&rda_uart_driver, &rda_port->port);
+ rda_uart_ports[pdev->id] = NULL;
+
+ return 0;
+}
+
+static struct platform_driver rda_uart_platform_driver = {
+ .probe = rda_uart_probe,
+ .remove = rda_uart_remove,
+ .driver = {
+ .name = "rda-uart",
+ .of_match_table = rda_uart_dt_matches,
+ },
+};
+
+static int __init rda_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&rda_uart_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&rda_uart_platform_driver);
+ if (ret)
+ uart_unregister_driver(&rda_uart_driver);
+
+ return ret;
+}
+
+static void __exit rda_uart_exit(void)
+{
+ platform_driver_unregister(&rda_uart_platform_driver);
+ uart_unregister_driver(&rda_uart_driver);
+}
+
+module_init(rda_uart_init);
+module_exit(rda_uart_exit);
+
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("RDA8810PL serial device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index a399772..8e61812 100644
--- a/drivers/tty/serial/sa1100.c
+++ b/drivers/tty/serial/sa1100.c
@@ -28,6 +28,8 @@
#include <mach/hardware.h>
#include <mach/irqs.h>
+#include "serial_mctrl_gpio.h"
+
/* We've been assigned a range on the "Low-density serial ports" major */
#define SERIAL_SA1100_MAJOR 204
#define MINOR_START 5
@@ -77,6 +79,7 @@
struct uart_port port;
struct timer_list timer;
unsigned int old_status;
+ struct mctrl_gpios *gpios;
};
/*
@@ -174,6 +177,8 @@
container_of(port, struct sa1100_port, port);
mod_timer(&sport->timer, jiffies);
+
+ mctrl_gpio_enable_ms(sport->gpios);
}
static void
@@ -322,11 +327,21 @@
static unsigned int sa1100_get_mctrl(struct uart_port *port)
{
- return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+ struct sa1100_port *sport =
+ container_of(port, struct sa1100_port, port);
+ int ret = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+ mctrl_gpio_get(sport->gpios, &ret);
+
+ return ret;
}
static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
+ struct sa1100_port *sport =
+ container_of(port, struct sa1100_port, port);
+
+ mctrl_gpio_set(sport->gpios, mctrl);
}
/*
@@ -842,6 +857,31 @@
return 0;
}
+static int sa1100_serial_add_one_port(struct sa1100_port *sport, struct platform_device *dev)
+{
+ sport->port.dev = &dev->dev;
+
+ // mctrl_gpio_init() requires that the GPIO driver supports interrupts,
+ // but we need to support GPIO drivers for hardware that has no such
+ // interrupts. Use mctrl_gpio_init_noauto() instead.
+ sport->gpios = mctrl_gpio_init_noauto(sport->port.dev, 0);
+ if (IS_ERR(sport->gpios)) {
+ int err = PTR_ERR(sport->gpios);
+
+ dev_err(sport->port.dev, "failed to get mctrl gpios: %d\n",
+ err);
+
+ if (err == -EPROBE_DEFER)
+ return err;
+
+ sport->gpios = NULL;
+ }
+
+ platform_set_drvdata(dev, sport);
+
+ return uart_add_one_port(&sa1100_reg, &sport->port);
+}
+
static int sa1100_serial_probe(struct platform_device *dev)
{
struct resource *res = dev->resource;
@@ -856,9 +896,7 @@
if (sa1100_ports[i].port.mapbase != res->start)
continue;
- sa1100_ports[i].port.dev = &dev->dev;
- uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
- platform_set_drvdata(dev, &sa1100_ports[i]);
+ sa1100_serial_add_one_port(&sa1100_ports[i], dev);
break;
}
}
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 2f8fa18..83fd516 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -1287,7 +1287,7 @@
* Ask the core to calculate the divisor for us.
*/
- baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
+ baud = uart_get_baud_rate(port, termios, old, 0, 3000000);
quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
@@ -1365,11 +1365,14 @@
wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
+ port->status &= ~UPSTAT_AUTOCTS;
+
umcon = rd_regl(port, S3C2410_UMCON);
if (termios->c_cflag & CRTSCTS) {
umcon |= S3C2410_UMCOM_AFC;
/* Disable RTS when RX FIFO contains 63 bytes */
umcon &= ~S3C2412_UMCON_AFC_8;
+ port->status = UPSTAT_AUTOCTS;
} else {
umcon &= ~S3C2410_UMCOM_AFC;
}
@@ -1691,6 +1694,42 @@
}
#endif
+static int s3c24xx_serial_enable_baudclk(struct s3c24xx_uart_port *ourport)
+{
+ struct device *dev = ourport->port.dev;
+ struct s3c24xx_uart_info *info = ourport->info;
+ char clk_name[MAX_CLK_NAME_LENGTH];
+ unsigned int clk_sel;
+ struct clk *clk;
+ int clk_num;
+ int ret;
+
+ clk_sel = ourport->cfg->clk_sel ? : info->def_clk_sel;
+ for (clk_num = 0; clk_num < info->num_clks; clk_num++) {
+ if (!(clk_sel & (1 << clk_num)))
+ continue;
+
+ sprintf(clk_name, "clk_uart_baud%d", clk_num);
+ clk = clk_get(dev, clk_name);
+ if (IS_ERR(clk))
+ continue;
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ clk_put(clk);
+ continue;
+ }
+
+ ourport->baudclk = clk;
+ ourport->baudclk_rate = clk_get_rate(clk);
+ s3c24xx_serial_setsource(&ourport->port, clk_num);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
@@ -1785,6 +1824,10 @@
goto err;
}
+ ret = s3c24xx_serial_enable_baudclk(ourport);
+ if (ret)
+ pr_warn("uart: failed to enable baudclk\n");
+
/* Keep all interrupts masked and cleared */
if (s3c24xx_serial_has_interrupt_mask(port)) {
wr_regl(port, S3C64XX_UINTM, 0xf);
@@ -1898,6 +1941,8 @@
* and keeps the clock enabled in this case.
*/
clk_disable_unprepare(ourport->clk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
ret = s3c24xx_serial_cpufreq_register(ourport);
if (ret < 0)
@@ -1941,7 +1986,11 @@
if (port) {
clk_prepare_enable(ourport->clk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_prepare_enable(ourport->baudclk);
s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
clk_disable_unprepare(ourport->clk);
uart_resume_port(&s3c24xx_uart_drv, port);
@@ -1964,7 +2013,11 @@
if (rx_enabled(port))
uintm &= ~S3C64XX_UINTM_RXD_MSK;
clk_prepare_enable(ourport->clk);
+ if (!IS_ERR(ourport->baudclk))
+ clk_prepare_enable(ourport->baudclk);
wr_regl(port, S3C64XX_UINTM, uintm);
+ if (!IS_ERR(ourport->baudclk))
+ clk_disable_unprepare(ourport->baudclk);
clk_disable_unprepare(ourport->clk);
}
}
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 47b4115..7d3ae31 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -14,9 +14,9 @@
#include <linux/device.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
@@ -328,6 +328,7 @@
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work irq_work;
+ struct mutex efr_lock;
struct sc16is7xx_one p[0];
};
@@ -499,6 +500,21 @@
div /= 4;
}
+ /* In an amazing feat of design, the Enhanced Features Register shares
+ * the address of the Interrupt Identification Register, and is
+ * switched in by writing a magic value (0xbf) to the Line Control
+ * Register. Any interrupt firing during this time will see the EFR
+ * where it expects the IIR to be, leading to "Unexpected interrupt"
+ * messages.
+ *
+ * Prevent this possibility by claiming a mutex while accessing the
+ * EFR, and claiming the same mutex from within the interrupt handler.
+ * This is similar to disabling the interrupt, but that doesn't work
+ * because the bulk of the interrupt processing is run as a workqueue
+ * job in thread context.
+ */
+ mutex_lock(&s->efr_lock);
+
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
/* Open the LCR divisors for configuration */
@@ -514,6 +530,8 @@
/* Put LCR back to the normal mode */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+ mutex_unlock(&s->efr_lock);
+
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_CLKSEL_BIT,
prescaler);
@@ -696,6 +714,8 @@
{
struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work);
+ mutex_lock(&s->efr_lock);
+
while (1) {
bool keep_polling = false;
int i;
@@ -705,6 +725,8 @@
if (!keep_polling)
break;
}
+
+ mutex_unlock(&s->efr_lock);
}
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
@@ -899,6 +921,9 @@
if (!(termios->c_cflag & CREAD))
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
+ /* As above, claim the mutex while accessing the EFR. */
+ mutex_lock(&s->efr_lock);
+
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_B);
@@ -920,6 +945,8 @@
/* Update LCR register */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
+ mutex_unlock(&s->efr_lock);
+
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 4 / 0xffff,
@@ -1152,7 +1179,8 @@
struct regmap *regmap, int irq, unsigned long flags)
{
struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 };
- unsigned long freq, *pfreq = dev_get_platdata(dev);
+ unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
+ u32 uartclk = 0;
int i, ret;
struct sc16is7xx_port *s;
@@ -1160,18 +1188,23 @@
return PTR_ERR(regmap);
/* Alloc port structure */
- s = devm_kzalloc(dev, sizeof(*s) +
- sizeof(struct sc16is7xx_one) * devtype->nr_uart,
- GFP_KERNEL);
+ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
}
+ /* Always ask for fixed clock rate from a property. */
+ device_property_read_u32(dev, "clock-frequency", &uartclk);
+
s->clk = devm_clk_get(dev, NULL);
if (IS_ERR(s->clk)) {
+ if (uartclk)
+ freq = uartclk;
if (pfreq)
freq = *pfreq;
+ if (freq)
+ dev_dbg(dev, "Clock frequency: %luHz\n", freq);
else
return PTR_ERR(s->clk);
} else {
@@ -1185,6 +1218,7 @@
s->regmap = regmap;
s->devtype = devtype;
dev_set_drvdata(dev, s);
+ mutex_init(&s->efr_lock);
kthread_init_worker(&s->kworker);
kthread_init_work(&s->irq_work, sc16is7xx_ist);
@@ -1358,13 +1392,9 @@
return ret;
if (spi->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &spi->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&spi->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
const struct spi_device_id *id_entry = spi_get_device_id(spi);
@@ -1400,7 +1430,7 @@
static struct spi_driver sc16is7xx_spi_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_spi_probe,
.remove = sc16is7xx_spi_remove,
@@ -1419,13 +1449,9 @@
struct regmap *regmap;
if (i2c->dev.of_node) {
- const struct of_device_id *of_id =
- of_match_device(sc16is7xx_dt_ids, &i2c->dev);
-
- if (!of_id)
+ devtype = device_get_match_data(&i2c->dev);
+ if (!devtype)
return -ENODEV;
-
- devtype = (struct sc16is7xx_devtype *)of_id->data;
} else {
devtype = (struct sc16is7xx_devtype *)id->driver_data;
flags = IRQF_TRIGGER_FALLING;
@@ -1458,7 +1484,7 @@
static struct i2c_driver sc16is7xx_i2c_uart_driver = {
.driver = {
.name = SC16IS7XX_NAME,
- .of_match_table = of_match_ptr(sc16is7xx_dt_ids),
+ .of_match_table = sc16is7xx_dt_ids,
},
.probe = sc16is7xx_i2c_probe,
.remove = sc16is7xx_i2c_remove,
@@ -1481,7 +1507,7 @@
ret = i2c_add_driver(&sc16is7xx_i2c_uart_driver);
if (ret < 0) {
pr_err("failed to init sc16is7xx i2c --> %d\n", ret);
- return ret;
+ goto err_i2c;
}
#endif
@@ -1489,10 +1515,20 @@
ret = spi_register_driver(&sc16is7xx_spi_uart_driver);
if (ret < 0) {
pr_err("failed to init sc16is7xx spi --> %d\n", ret);
- return ret;
+ goto err_spi;
}
#endif
return ret;
+
+#ifdef CONFIG_SERIAL_SC16IS7XX_SPI
+err_spi:
+#endif
+#ifdef CONFIG_SERIAL_SC16IS7XX_I2C
+ i2c_del_driver(&sc16is7xx_i2c_uart_driver);
+err_i2c:
+#endif
+ uart_unregister_driver(&sc16is7xx_uart);
+ return ret;
}
module_init(sc16is7xx_init);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index 339befd..d2b77aa 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -12,6 +12,7 @@
#endif
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -47,7 +48,6 @@
# define MR2_STOP1 (7 << 0)
# define MR2_STOP2 (0xf << 0)
#define SCCNXP_SR_REG (0x01)
-#define SCCNXP_CSR_REG SCCNXP_SR_REG
# define SR_RXRDY (1 << 0)
# define SR_FULL (1 << 1)
# define SR_TXRDY (1 << 2)
@@ -56,6 +56,8 @@
# define SR_PE (1 << 5)
# define SR_FE (1 << 6)
# define SR_BRK (1 << 7)
+#define SCCNXP_CSR_REG (SCCNXP_SR_REG)
+# define CSR_TIMER_MODE (0x0d)
#define SCCNXP_CR_REG (0x02)
# define CR_RX_ENABLE (1 << 0)
# define CR_RX_DISABLE (1 << 1)
@@ -82,9 +84,12 @@
# define IMR_RXRDY (1 << 1)
# define ISR_TXRDY(x) (1 << ((x * 4) + 0))
# define ISR_RXRDY(x) (1 << ((x * 4) + 1))
+#define SCCNXP_CTPU_REG (0x06)
+#define SCCNXP_CTPL_REG (0x07)
#define SCCNXP_IPR_REG (0x0d)
#define SCCNXP_OPCR_REG SCCNXP_IPR_REG
#define SCCNXP_SOP_REG (0x0e)
+#define SCCNXP_START_COUNTER_REG SCCNXP_SOP_REG
#define SCCNXP_ROP_REG (0x0f)
/* Route helpers */
@@ -103,6 +108,8 @@
unsigned long freq_max;
unsigned int flags;
unsigned int fifosize;
+ /* Time between read/write cycles */
+ unsigned int trwd;
};
struct sccnxp_port {
@@ -137,6 +144,7 @@
.freq_max = 4000000,
.flags = SCCNXP_HAVE_IO,
.fifosize = 3,
+ .trwd = 200,
};
static const struct sccnxp_chip sc2691 = {
@@ -147,6 +155,7 @@
.freq_max = 4000000,
.flags = 0,
.fifosize = 3,
+ .trwd = 150,
};
static const struct sccnxp_chip sc2692 = {
@@ -157,6 +166,7 @@
.freq_max = 4000000,
.flags = SCCNXP_HAVE_IO,
.fifosize = 3,
+ .trwd = 30,
};
static const struct sccnxp_chip sc2891 = {
@@ -167,6 +177,7 @@
.freq_max = 8000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 16,
+ .trwd = 27,
};
static const struct sccnxp_chip sc2892 = {
@@ -177,6 +188,7 @@
.freq_max = 8000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 16,
+ .trwd = 17,
};
static const struct sccnxp_chip sc28202 = {
@@ -187,6 +199,7 @@
.freq_max = 50000000,
.flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0,
.fifosize = 256,
+ .trwd = 10,
};
static const struct sccnxp_chip sc68681 = {
@@ -197,6 +210,7 @@
.freq_max = 4000000,
.flags = SCCNXP_HAVE_IO,
.fifosize = 3,
+ .trwd = 200,
};
static const struct sccnxp_chip sc68692 = {
@@ -207,24 +221,36 @@
.freq_max = 4000000,
.flags = SCCNXP_HAVE_IO,
.fifosize = 3,
+ .trwd = 200,
};
-static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
+static u8 sccnxp_read(struct uart_port *port, u8 reg)
{
- return readb(port->membase + (reg << port->regshift));
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+ u8 ret;
+
+ ret = readb(port->membase + (reg << port->regshift));
+
+ ndelay(s->chip->trwd);
+
+ return ret;
}
-static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
+static void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
{
+ struct sccnxp_port *s = dev_get_drvdata(port->dev);
+
writeb(v, port->membase + (reg << port->regshift));
+
+ ndelay(s->chip->trwd);
}
-static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
+static u8 sccnxp_port_read(struct uart_port *port, u8 reg)
{
return sccnxp_read(port, (port->line << 3) + reg);
}
-static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
+static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
{
sccnxp_write(port, (port->line << 3) + reg, v);
}
@@ -233,7 +259,7 @@
{
int err = abs(a - b);
- if ((*besterr < 0) || (*besterr > err)) {
+ if (*besterr > err) {
*besterr = err;
return 0;
}
@@ -281,10 +307,22 @@
static int sccnxp_set_baud(struct uart_port *port, int baud)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
- int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+ int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;
struct sccnxp_chip *chip = s->chip;
u8 i, acr = 0, csr = 0, mr0 = 0;
+ /* Find divisor to load to the timer preset registers */
+ div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud);
+ if ((div_std >= 2) && (div_std <= 0xffff)) {
+ bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std);
+ sccnxp_update_best_err(baud, bestbaud, &besterr);
+ csr = CSR_TIMER_MODE;
+ sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8);
+ sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std);
+ /* Issue start timer/counter command */
+ sccnxp_port_read(port, SCCNXP_START_COUNTER_REG);
+ }
+
/* Find best baud from table */
for (i = 0; baud_std[i].baud && besterr; i++) {
if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))
@@ -923,7 +961,6 @@
if (!s->poll) {
s->irq = platform_get_irq(pdev, 0);
if (s->irq < 0) {
- dev_err(&pdev->dev, "Missing irq resource data\n");
ret = -ENXIO;
goto err_out;
}
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index af2a29c..2f59951 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -4,7 +4,7 @@
*
* High-speed serial driver for NVIDIA Tegra SoCs
*
- * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2019, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*/
@@ -62,7 +62,7 @@
#define TEGRA_UART_TX_TRIG_4B 0x20
#define TEGRA_UART_TX_TRIG_1B 0x30
-#define TEGRA_UART_MAXIMUM 5
+#define TEGRA_UART_MAXIMUM 8
/* Default UART setting when started: 115200 no parity, stop, 8 data bits */
#define TEGRA_UART_DEFAULT_BAUD 115200
@@ -72,6 +72,8 @@
#define TEGRA_TX_PIO 1
#define TEGRA_TX_DMA 2
+#define TEGRA_UART_FCR_IIR_FIFO_EN 0x40
+
/**
* tegra_uart_chip_data: SOC specific data.
*
@@ -84,6 +86,17 @@
bool tx_fifo_full_status;
bool allow_txfifo_reset_fifo_mode;
bool support_clk_src_div;
+ bool fifo_mode_enable_status;
+ int uart_max_port;
+ int max_dma_burst_bytes;
+ int error_tolerance_low_range;
+ int error_tolerance_high_range;
+};
+
+struct tegra_baud_tolerance {
+ u32 lower_range_baud;
+ u32 upper_range_baud;
+ s32 tolerance;
};
struct tegra_uart_port {
@@ -122,10 +135,18 @@
dma_cookie_t rx_cookie;
unsigned int tx_bytes_requested;
unsigned int rx_bytes_requested;
+ struct tegra_baud_tolerance *baud_tolerance;
+ int n_adjustable_baud_rates;
+ int required_rate;
+ int configured_rate;
+ bool use_rx_pio;
+ bool use_tx_pio;
};
static void tegra_uart_start_next_tx(struct tegra_uart_port *tup);
static int tegra_uart_start_rx_dma(struct tegra_uart_port *tup);
+static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
+ bool dma_to_memory);
static inline unsigned long tegra_uart_read(struct tegra_uart_port *tup,
unsigned long reg)
@@ -192,16 +213,34 @@
}
}
+static void set_loopbk(struct tegra_uart_port *tup, bool active)
+{
+ unsigned long mcr = tup->mcr_shadow;
+
+ if (active)
+ mcr |= UART_MCR_LOOP;
+ else
+ mcr &= ~UART_MCR_LOOP;
+
+ if (mcr != tup->mcr_shadow) {
+ tegra_uart_write(tup, mcr, UART_MCR);
+ tup->mcr_shadow = mcr;
+ }
+}
+
static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
- int dtr_enable;
+ int enable;
tup->rts_active = !!(mctrl & TIOCM_RTS);
set_rts(tup, tup->rts_active);
- dtr_enable = !!(mctrl & TIOCM_DTR);
- set_dtr(tup, dtr_enable);
+ enable = !!(mctrl & TIOCM_DTR);
+ set_dtr(tup, enable);
+
+ enable = !!(mctrl & TIOCM_LOOP);
+ set_loopbk(tup, enable);
}
static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl)
@@ -243,9 +282,28 @@
tup->current_baud));
}
+static int tegra_uart_wait_fifo_mode_enabled(struct tegra_uart_port *tup)
+{
+ unsigned long iir;
+ unsigned int tmout = 100;
+
+ do {
+ iir = tegra_uart_read(tup, UART_IIR);
+ if (iir & TEGRA_UART_FCR_IIR_FIFO_EN)
+ return 0;
+ udelay(1);
+ } while (--tmout);
+
+ return -ETIMEDOUT;
+}
+
static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
{
unsigned long fcr = tup->fcr_shadow;
+ unsigned int lsr, tmout = 10000;
+
+ if (tup->rts_active)
+ set_rts(tup, false);
if (tup->cdata->allow_txfifo_reset_fifo_mode) {
fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
@@ -258,6 +316,8 @@
tegra_uart_write(tup, fcr, UART_FCR);
fcr |= UART_FCR_ENABLE_FIFO;
tegra_uart_write(tup, fcr, UART_FCR);
+ if (tup->cdata->fifo_mode_enable_status)
+ tegra_uart_wait_fifo_mode_enabled(tup);
}
/* Dummy read to ensure the write is posted */
@@ -269,6 +329,47 @@
* to propagate, otherwise data could be lost.
*/
tegra_uart_wait_cycle_time(tup, 32);
+
+ do {
+ lsr = tegra_uart_read(tup, UART_LSR);
+ if ((lsr | UART_LSR_TEMT) && !(lsr & UART_LSR_DR))
+ break;
+ udelay(1);
+ } while (--tmout);
+
+ if (tup->rts_active)
+ set_rts(tup, true);
+}
+
+static long tegra_get_tolerance_rate(struct tegra_uart_port *tup,
+ unsigned int baud, long rate)
+{
+ int i;
+
+ for (i = 0; i < tup->n_adjustable_baud_rates; ++i) {
+ if (baud >= tup->baud_tolerance[i].lower_range_baud &&
+ baud <= tup->baud_tolerance[i].upper_range_baud)
+ return (rate + (rate *
+ tup->baud_tolerance[i].tolerance) / 10000);
+ }
+
+ return rate;
+}
+
+static int tegra_check_rate_in_range(struct tegra_uart_port *tup)
+{
+ long diff;
+
+ diff = ((long)(tup->configured_rate - tup->required_rate) * 10000)
+ / tup->required_rate;
+ if (diff < (tup->cdata->error_tolerance_low_range * 100) ||
+ diff > (tup->cdata->error_tolerance_high_range * 100)) {
+ dev_err(tup->uport.dev,
+ "configured baud rate is out of range by %ld", diff);
+ return -EIO;
+ }
+
+ return 0;
}
static int tegra_set_baudrate(struct tegra_uart_port *tup, unsigned int baud)
@@ -276,6 +377,7 @@
unsigned long rate;
unsigned int divisor;
unsigned long lcr;
+ unsigned long flags;
int ret;
if (tup->current_baud == baud)
@@ -283,18 +385,28 @@
if (tup->cdata->support_clk_src_div) {
rate = baud * 16;
+ tup->required_rate = rate;
+
+ if (tup->n_adjustable_baud_rates)
+ rate = tegra_get_tolerance_rate(tup, baud, rate);
+
ret = clk_set_rate(tup->uart_clk, rate);
if (ret < 0) {
dev_err(tup->uport.dev,
"clk_set_rate() failed for rate %lu\n", rate);
return ret;
}
+ tup->configured_rate = clk_get_rate(tup->uart_clk);
divisor = 1;
+ ret = tegra_check_rate_in_range(tup);
+ if (ret < 0)
+ return ret;
} else {
rate = clk_get_rate(tup->uart_clk);
divisor = DIV_ROUND_CLOSEST(rate, baud * 16);
}
+ spin_lock_irqsave(&tup->uport.lock, flags);
lcr = tup->lcr_shadow;
lcr |= UART_LCR_DLAB;
tegra_uart_write(tup, lcr, UART_LCR);
@@ -307,6 +419,7 @@
/* Dummy read to ensure the write is posted */
tegra_uart_read(tup, UART_SCR);
+ spin_unlock_irqrestore(&tup->uport.lock, flags);
tup->current_baud = baud;
@@ -336,13 +449,21 @@
tup->uport.icount.frame++;
dev_err(tup->uport.dev, "Got frame errors\n");
} else if (lsr & UART_LSR_BI) {
- dev_err(tup->uport.dev, "Got Break\n");
- tup->uport.icount.brk++;
- /* If FIFO read error without any data, reset Rx FIFO */
+ /*
+ * Break error
+ * If FIFO read error without any data, reset Rx FIFO
+ */
if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE))
tegra_uart_fifo_reset(tup, UART_FCR_CLEAR_RCVR);
+ if (tup->uport.ignore_status_mask & UART_LSR_BI)
+ return TTY_BREAK;
+ flag = TTY_BREAK;
+ tup->uport.icount.brk++;
+ dev_dbg(tup->uport.dev, "Got Break\n");
}
+ uart_insert_char(&tup->uport, lsr, UART_LSR_OE, 0, flag);
}
+
return flag;
}
@@ -440,12 +561,15 @@
unsigned long count;
struct circ_buf *xmit = &tup->uport.state->xmit;
+ if (!tup->current_baud)
+ return;
+
tail = (unsigned long)&xmit->buf[xmit->tail];
count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
if (!count)
return;
- if (count < TEGRA_UART_MIN_DMA)
+ if (tup->use_tx_pio || count < TEGRA_UART_MIN_DMA)
tegra_uart_start_pio_tx(tup, count);
else if (BYTES_TO_ALIGN(tail) > 0)
tegra_uart_start_pio_tx(tup, BYTES_TO_ALIGN(tail));
@@ -521,11 +645,17 @@
break;
flag = tegra_uart_decode_rx_error(tup, lsr);
+ if (flag != TTY_NORMAL)
+ continue;
+
ch = (unsigned char) tegra_uart_read(tup, UART_RX);
tup->uport.icount.rx++;
if (!uart_handle_sysrq_char(&tup->uport, ch) && tty)
tty_insert_flip_char(tty, ch, flag);
+
+ if (tup->uport.ignore_status_mask & UART_LSR_DR)
+ continue;
} while (1);
}
@@ -544,6 +674,10 @@
dev_err(tup->uport.dev, "No tty port\n");
return;
}
+
+ if (tup->uport.ignore_status_mask & UART_LSR_DR)
+ return;
+
dma_sync_single_for_cpu(tup->uport.dev, tup->rx_dma_buf_phys,
TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
copied = tty_insert_flip_string(tty,
@@ -668,6 +802,18 @@
uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS);
}
+static void do_handle_rx_pio(struct tegra_uart_port *tup)
+{
+ struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);
+ struct tty_port *port = &tup->uport.state->port;
+
+ tegra_uart_handle_rx_pio(tup, port);
+ if (tty) {
+ tty_flip_buffer_push(port);
+ tty_kref_put(tty);
+ }
+}
+
static irqreturn_t tegra_uart_isr(int irq, void *data)
{
struct tegra_uart_port *tup = data;
@@ -681,7 +827,7 @@
while (1) {
iir = tegra_uart_read(tup, UART_IIR);
if (iir & UART_IIR_NO_INT) {
- if (is_rx_int) {
+ if (!tup->use_rx_pio && is_rx_int) {
tegra_uart_handle_rx_dma(tup);
if (tup->rx_in_progress) {
ier = tup->ier_shadow;
@@ -709,7 +855,7 @@
case 4: /* End of data */
case 6: /* Rx timeout */
case 2: /* Receive */
- if (!is_rx_int) {
+ if (!tup->use_rx_pio && !is_rx_int) {
is_rx_int = true;
/* Disable Rx interrupts */
ier = tup->ier_shadow;
@@ -719,6 +865,8 @@
UART_IER_RTOIE | TEGRA_UART_IER_EORD);
tup->ier_shadow = ier;
tegra_uart_write(tup, ier, UART_IER);
+ } else {
+ do_handle_rx_pio(tup);
}
break;
@@ -737,6 +885,7 @@
static void tegra_uart_stop_rx(struct uart_port *u)
{
struct tegra_uart_port *tup = to_tegra_uport(u);
+ struct tty_port *port = &tup->uport.state->port;
struct dma_tx_state state;
unsigned long ier;
@@ -746,7 +895,7 @@
if (!tup->rx_in_progress)
return;
- tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */
+ tegra_uart_wait_sym_time(tup, 1); /* wait one character interval */
ier = tup->ier_shadow;
ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE |
@@ -754,9 +903,13 @@
tup->ier_shadow = ier;
tegra_uart_write(tup, ier, UART_IER);
tup->rx_in_progress = 0;
- dmaengine_terminate_all(tup->rx_dma_chan);
- dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
- tegra_uart_rx_buffer_push(tup, state.residue);
+ if (tup->rx_dma_chan && !tup->use_rx_pio) {
+ dmaengine_terminate_all(tup->rx_dma_chan);
+ dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);
+ tegra_uart_rx_buffer_push(tup, state.residue);
+ } else {
+ tegra_uart_handle_rx_pio(tup, port);
+ }
}
static void tegra_uart_hw_deinit(struct tegra_uart_port *tup)
@@ -804,6 +957,14 @@
tup->current_baud = 0;
spin_unlock_irqrestore(&tup->uport.lock, flags);
+ tup->rx_in_progress = 0;
+ tup->tx_in_progress = 0;
+
+ if (!tup->use_rx_pio)
+ tegra_uart_dma_channel_free(tup, true);
+ if (!tup->use_tx_pio)
+ tegra_uart_dma_channel_free(tup, false);
+
clk_disable_unprepare(tup->uart_clk);
}
@@ -846,35 +1007,60 @@
* programmed in the DMA registers.
*/
tup->fcr_shadow = UART_FCR_ENABLE_FIFO;
- tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+
+ if (tup->use_rx_pio) {
+ tup->fcr_shadow |= UART_FCR_R_TRIG_11;
+ } else {
+ if (tup->cdata->max_dma_burst_bytes == 8)
+ tup->fcr_shadow |= UART_FCR_R_TRIG_10;
+ else
+ tup->fcr_shadow |= UART_FCR_R_TRIG_01;
+ }
+
tup->fcr_shadow |= TEGRA_UART_TX_TRIG_16B;
tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
/* Dummy read to ensure the write is posted */
tegra_uart_read(tup, UART_SCR);
- /*
- * For all tegra devices (up to t210), there is a hardware issue that
- * requires software to wait for 3 UART clock periods after enabling
- * the TX fifo, otherwise data could be lost.
- */
- tegra_uart_wait_cycle_time(tup, 3);
+ if (tup->cdata->fifo_mode_enable_status) {
+ ret = tegra_uart_wait_fifo_mode_enabled(tup);
+ dev_err(tup->uport.dev, "FIFO mode not enabled\n");
+ if (ret < 0)
+ return ret;
+ } else {
+ /*
+ * For all tegra devices (up to t210), there is a hardware
+ * issue that requires software to wait for 3 UART clock
+ * periods after enabling the TX fifo, otherwise data could
+ * be lost.
+ */
+ tegra_uart_wait_cycle_time(tup, 3);
+ }
/*
* Initialize the UART with default configuration
* (115200, N, 8, 1) so that the receive DMA buffer may be
* enqueued
*/
- tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
- tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
- tup->fcr_shadow |= UART_FCR_DMA_SELECT;
- tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
-
- ret = tegra_uart_start_rx_dma(tup);
+ ret = tegra_set_baudrate(tup, TEGRA_UART_DEFAULT_BAUD);
if (ret < 0) {
- dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
+ dev_err(tup->uport.dev, "Failed to set baud rate\n");
return ret;
}
+ if (!tup->use_rx_pio) {
+ tup->lcr_shadow = TEGRA_UART_DEFAULT_LSR;
+ tup->fcr_shadow |= UART_FCR_DMA_SELECT;
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+
+ ret = tegra_uart_start_rx_dma(tup);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Not able to start Rx DMA\n");
+ return ret;
+ }
+ } else {
+ tegra_uart_write(tup, tup->fcr_shadow, UART_FCR);
+ }
tup->rx_in_progress = 1;
/*
@@ -887,7 +1073,7 @@
*
* EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when
* the DATA is sitting in the FIFO and couldn't be transferred to the
- * DMA as the DMA size alignment(4 bytes) is not met. EORD will be
+ * DMA as the DMA size alignment (4 bytes) is not met. EORD will be
* triggered when there is a pause of the incomming data stream for 4
* characters long.
*
@@ -895,7 +1081,12 @@
* both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
* then the EORD.
*/
- tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | TEGRA_UART_IER_EORD;
+ if (!tup->use_rx_pio)
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE |
+ TEGRA_UART_IER_EORD;
+ else
+ tup->ier_shadow = UART_IER_RLSI | UART_IER_RTOIE | UART_IER_RDI;
+
tegra_uart_write(tup, tup->ier_shadow, UART_IER);
return 0;
}
@@ -952,7 +1143,7 @@
}
dma_sconfig.src_addr = tup->uport.mapbase;
dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- dma_sconfig.src_maxburst = 4;
+ dma_sconfig.src_maxburst = tup->cdata->max_dma_burst_bytes;
tup->rx_dma_chan = dma_chan;
tup->rx_dma_buf_virt = dma_buf;
tup->rx_dma_buf_phys = dma_phys;
@@ -990,16 +1181,22 @@
struct tegra_uart_port *tup = to_tegra_uport(u);
int ret;
- ret = tegra_uart_dma_channel_allocate(tup, false);
- if (ret < 0) {
- dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);
- return ret;
+ if (!tup->use_tx_pio) {
+ ret = tegra_uart_dma_channel_allocate(tup, false);
+ if (ret < 0) {
+ dev_err(u->dev, "Tx Dma allocation failed, err = %d\n",
+ ret);
+ return ret;
+ }
}
- ret = tegra_uart_dma_channel_allocate(tup, true);
- if (ret < 0) {
- dev_err(u->dev, "Rx Dma allocation failed, err = %d\n", ret);
- goto fail_rx_dma;
+ if (!tup->use_rx_pio) {
+ ret = tegra_uart_dma_channel_allocate(tup, true);
+ if (ret < 0) {
+ dev_err(u->dev, "Rx Dma allocation failed, err = %d\n",
+ ret);
+ goto fail_rx_dma;
+ }
}
ret = tegra_uart_hw_init(tup);
@@ -1017,9 +1214,11 @@
return 0;
fail_hw_init:
- tegra_uart_dma_channel_free(tup, true);
+ if (!tup->use_rx_pio)
+ tegra_uart_dma_channel_free(tup, true);
fail_rx_dma:
- tegra_uart_dma_channel_free(tup, false);
+ if (!tup->use_tx_pio)
+ tegra_uart_dma_channel_free(tup, false);
return ret;
}
@@ -1041,12 +1240,6 @@
struct tegra_uart_port *tup = to_tegra_uport(u);
tegra_uart_hw_deinit(tup);
-
- tup->rx_in_progress = 0;
- tup->tx_in_progress = 0;
-
- tegra_uart_dma_channel_free(tup, true);
- tegra_uart_dma_channel_free(tup, false);
free_irq(u->irq, tup);
}
@@ -1071,6 +1264,7 @@
struct clk *parent_clk = clk_get_parent(tup->uart_clk);
unsigned long parent_clk_rate = clk_get_rate(parent_clk);
int max_divider = (tup->cdata->support_clk_src_div) ? 0x7FFF : 0xFFFF;
+ int ret;
max_divider *= 16;
spin_lock_irqsave(&u->lock, flags);
@@ -1079,7 +1273,7 @@
if (tup->rts_active)
set_rts(tup, false);
- /* Clear all interrupts as configuration is going to be change */
+ /* Clear all interrupts as configuration is going to be changed */
tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER);
tegra_uart_read(tup, UART_IER);
tegra_uart_write(tup, 0, UART_IER);
@@ -1143,7 +1337,11 @@
parent_clk_rate/max_divider,
parent_clk_rate/16);
spin_unlock_irqrestore(&u->lock, flags);
- tegra_set_baudrate(tup, baud);
+ ret = tegra_set_baudrate(tup, baud);
+ if (ret < 0) {
+ dev_err(tup->uport.dev, "Failed to set baud rate\n");
+ return;
+ }
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
spin_lock_irqsave(&u->lock, flags);
@@ -1165,13 +1363,20 @@
/* update the port timeout based on new settings */
uart_update_timeout(u, termios->c_cflag, baud);
- /* Make sure all write has completed */
+ /* Make sure all writes have completed */
tegra_uart_read(tup, UART_IER);
- /* Reenable interrupt */
+ /* Re-enable interrupt */
tegra_uart_write(tup, tup->ier_shadow, UART_IER);
tegra_uart_read(tup, UART_IER);
+ tup->uport.ignore_status_mask = 0;
+ /* Ignore all characters if CREAD is not set */
+ if ((termios->c_cflag & CREAD) == 0)
+ tup->uport.ignore_status_mask |= UART_LSR_DR;
+ if (termios->c_iflag & IGNBRK)
+ tup->uport.ignore_status_mask |= UART_LSR_BI;
+
spin_unlock_irqrestore(&u->lock, flags);
}
@@ -1211,6 +1416,11 @@
{
struct device_node *np = pdev->dev.of_node;
int port;
+ int ret;
+ int index;
+ u32 pval;
+ int count;
+ int n_entries;
port = of_alias_get_id(np, "serial");
if (port < 0) {
@@ -1221,6 +1431,54 @@
tup->enable_modem_interrupt = of_property_read_bool(np,
"nvidia,enable-modem-interrupt");
+
+ index = of_property_match_string(np, "dma-names", "rx");
+ if (index < 0) {
+ tup->use_rx_pio = true;
+ dev_info(&pdev->dev, "RX in PIO mode\n");
+ }
+ index = of_property_match_string(np, "dma-names", "tx");
+ if (index < 0) {
+ tup->use_tx_pio = true;
+ dev_info(&pdev->dev, "TX in PIO mode\n");
+ }
+
+ n_entries = of_property_count_u32_elems(np, "nvidia,adjust-baud-rates");
+ if (n_entries > 0) {
+ tup->n_adjustable_baud_rates = n_entries / 3;
+ tup->baud_tolerance =
+ devm_kzalloc(&pdev->dev, (tup->n_adjustable_baud_rates) *
+ sizeof(*tup->baud_tolerance), GFP_KERNEL);
+ if (!tup->baud_tolerance)
+ return -ENOMEM;
+ for (count = 0, index = 0; count < n_entries; count += 3,
+ index++) {
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].lower_range_baud =
+ pval;
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count + 1, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].upper_range_baud =
+ pval;
+ ret =
+ of_property_read_u32_index(np,
+ "nvidia,adjust-baud-rates",
+ count + 2, &pval);
+ if (!ret)
+ tup->baud_tolerance[index].tolerance =
+ (s32)pval;
+ }
+ } else {
+ tup->n_adjustable_baud_rates = 0;
+ }
+
return 0;
}
@@ -1228,12 +1486,44 @@
.tx_fifo_full_status = false,
.allow_txfifo_reset_fifo_mode = true,
.support_clk_src_div = false,
+ .fifo_mode_enable_status = false,
+ .uart_max_port = 5,
+ .max_dma_burst_bytes = 4,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
};
static struct tegra_uart_chip_data tegra30_uart_chip_data = {
.tx_fifo_full_status = true,
.allow_txfifo_reset_fifo_mode = false,
.support_clk_src_div = true,
+ .fifo_mode_enable_status = false,
+ .uart_max_port = 5,
+ .max_dma_burst_bytes = 4,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
+};
+
+static struct tegra_uart_chip_data tegra186_uart_chip_data = {
+ .tx_fifo_full_status = true,
+ .allow_txfifo_reset_fifo_mode = false,
+ .support_clk_src_div = true,
+ .fifo_mode_enable_status = true,
+ .uart_max_port = 8,
+ .max_dma_burst_bytes = 8,
+ .error_tolerance_low_range = 0,
+ .error_tolerance_high_range = 4,
+};
+
+static struct tegra_uart_chip_data tegra194_uart_chip_data = {
+ .tx_fifo_full_status = true,
+ .allow_txfifo_reset_fifo_mode = false,
+ .support_clk_src_div = true,
+ .fifo_mode_enable_status = true,
+ .uart_max_port = 8,
+ .max_dma_burst_bytes = 8,
+ .error_tolerance_low_range = -2,
+ .error_tolerance_high_range = 2,
};
static const struct of_device_id tegra_uart_of_match[] = {
@@ -1244,6 +1534,12 @@
.compatible = "nvidia,tegra20-hsuart",
.data = &tegra20_uart_chip_data,
}, {
+ .compatible = "nvidia,tegra186-hsuart",
+ .data = &tegra186_uart_chip_data,
+ }, {
+ .compatible = "nvidia,tegra194-hsuart",
+ .data = &tegra194_uart_chip_data,
+ }, {
},
};
MODULE_DEVICE_TABLE(of, tegra_uart_of_match);
@@ -1307,10 +1603,8 @@
u->iotype = UPIO_MEM32;
ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "Couldn't get IRQ\n");
+ if (ret < 0)
return ret;
- }
u->irq = ret;
u->regshift = 2;
ret = uart_add_one_port(&tegra_uart_driver, u);
@@ -1365,11 +1659,22 @@
static int __init tegra_uart_init(void)
{
int ret;
+ struct device_node *node;
+ const struct of_device_id *match = NULL;
+ const struct tegra_uart_chip_data *cdata = NULL;
+
+ node = of_find_matching_node(NULL, tegra_uart_of_match);
+ if (node)
+ match = of_match_node(tegra_uart_of_match, node);
+ if (match)
+ cdata = match->data;
+ if (cdata)
+ tegra_uart_driver.nr = cdata->uart_max_port;
ret = uart_register_driver(&tegra_uart_driver);
if (ret < 0) {
pr_err("Could not register %s driver\n",
- tegra_uart_driver.driver_name);
+ tegra_uart_driver.driver_name);
return ret;
}
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 80bb56f..c4a414a 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -22,6 +22,7 @@
#include <linux/serial_core.h>
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/security.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
@@ -205,10 +206,15 @@
if (!state->xmit.buf) {
state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit);
+ uart_port_unlock(uport, flags);
} else {
+ uart_port_unlock(uport, flags);
+ /*
+ * Do not free() the page under the port lock, see
+ * uart_shutdown().
+ */
free_page(page);
}
- uart_port_unlock(uport, flags);
retval = uport->ops->startup(uport);
if (retval == 0) {
@@ -268,6 +274,7 @@
struct uart_port *uport = uart_port_check(state);
struct tty_port *port = &state->port;
unsigned long flags = 0;
+ char *xmit_buf = NULL;
/*
* Set the TTY IO error marker
@@ -298,14 +305,18 @@
tty_port_set_suspended(port, 0);
/*
- * Free the transmit buffer page.
+ * Do not free() the transmit buffer page under the port lock since
+ * this can create various circular locking scenarios. For instance,
+ * console driver may need to allocate/free a debug object, which
+ * can endup in printk() recursion.
*/
uart_port_lock(state, flags);
- if (state->xmit.buf) {
- free_page((unsigned long)state->xmit.buf);
- state->xmit.buf = NULL;
- }
+ xmit_buf = state->xmit.buf;
+ state->xmit.buf = NULL;
uart_port_unlock(uport, flags);
+
+ if (xmit_buf)
+ free_page((unsigned long)xmit_buf);
}
/**
@@ -540,10 +551,12 @@
int ret = 0;
circ = &state->xmit;
- if (!circ->buf)
- return 0;
-
port = uart_port_lock(state, flags);
+ if (!circ->buf) {
+ uart_port_unlock(port, flags);
+ return 0;
+ }
+
if (port && uart_circ_chars_free(circ) != 0) {
circ->buf[circ->head] = c;
circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
@@ -576,11 +589,13 @@
return -EL3HLT;
}
- circ = &state->xmit;
- if (!circ->buf)
- return 0;
-
port = uart_port_lock(state, flags);
+ circ = &state->xmit;
+ if (!circ->buf) {
+ uart_port_unlock(port, flags);
+ return 0;
+ }
+
while (port) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
@@ -778,17 +793,13 @@
return ret;
}
-static int uart_get_info_user(struct tty_port *port,
- struct serial_struct __user *retinfo)
+static int uart_get_info_user(struct tty_struct *tty,
+ struct serial_struct *ss)
{
- struct serial_struct tmp;
+ struct uart_state *state = tty->driver_data;
+ struct tty_port *port = &state->port;
- if (uart_get_info(port, &tmp) < 0)
- return -EIO;
-
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
+ return uart_get_info(port, ss) < 0 ? -EIO : 0;
}
static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
@@ -852,6 +863,10 @@
goto check_and_exit;
}
+ retval = security_locked_down(LOCKDOWN_TIOCSSERIAL);
+ if (retval && (change_irq || change_port))
+ goto exit;
+
/*
* Ask the low level driver to verify the settings.
*/
@@ -990,16 +1005,13 @@
return retval;
}
-static int uart_set_info_user(struct tty_struct *tty, struct uart_state *state,
- struct serial_struct __user *newinfo)
+static int uart_set_info_user(struct tty_struct *tty, struct serial_struct *ss)
{
- struct serial_struct new_serial;
+ struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
int retval;
- if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
- return -EFAULT;
-
+ down_write(&tty->termios_rwsem);
/*
* This semaphore protects port->count. It is also
* very useful to prevent opens. Also, take the
@@ -1008,8 +1020,9 @@
* under us.
*/
mutex_lock(&port->mutex);
- retval = uart_set_info(tty, port, state, &new_serial);
+ retval = uart_set_info(tty, port, state, ss);
mutex_unlock(&port->mutex);
+ up_write(&tty->termios_rwsem);
return retval;
}
@@ -1308,6 +1321,58 @@
return 0;
}
+static int uart_get_iso7816_config(struct uart_port *port,
+ struct serial_iso7816 __user *iso7816)
+{
+ unsigned long flags;
+ struct serial_iso7816 aux;
+
+ if (!port->iso7816_config)
+ return -ENOIOCTLCMD;
+
+ spin_lock_irqsave(&port->lock, flags);
+ aux = port->iso7816;
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (copy_to_user(iso7816, &aux, sizeof(aux)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uart_set_iso7816_config(struct uart_port *port,
+ struct serial_iso7816 __user *iso7816_user)
+{
+ struct serial_iso7816 iso7816;
+ int i, ret;
+ unsigned long flags;
+
+ if (!port->iso7816_config)
+ return -ENOIOCTLCMD;
+
+ if (copy_from_user(&iso7816, iso7816_user, sizeof(*iso7816_user)))
+ return -EFAULT;
+
+ /*
+ * There are 5 words reserved for future use. Check that userspace
+ * doesn't put stuff in there to prevent breakages in the future.
+ */
+ for (i = 0; i < 5; i++)
+ if (iso7816.reserved[i])
+ return -EINVAL;
+
+ spin_lock_irqsave(&port->lock, flags);
+ ret = port->iso7816_config(port, &iso7816);
+ spin_unlock_irqrestore(&port->lock, flags);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(iso7816_user, &port->iso7816, sizeof(port->iso7816)))
+ return -EFAULT;
+
+ return 0;
+}
+
/*
* Called via sys_ioctl. We can use spin_lock_irq() here.
*/
@@ -1325,26 +1390,11 @@
* These ioctls don't rely on the hardware to be present.
*/
switch (cmd) {
- case TIOCGSERIAL:
- ret = uart_get_info_user(port, uarg);
- break;
-
- case TIOCSSERIAL:
- down_write(&tty->termios_rwsem);
- ret = uart_set_info_user(tty, state, uarg);
- up_write(&tty->termios_rwsem);
- break;
-
case TIOCSERCONFIG:
down_write(&tty->termios_rwsem);
ret = uart_do_autoconfig(tty, state);
up_write(&tty->termios_rwsem);
break;
-
- case TIOCSERGWILD: /* obsolete */
- case TIOCSERSWILD: /* obsolete */
- ret = 0;
- break;
}
if (ret != -ENOIOCTLCMD)
@@ -1392,6 +1442,14 @@
case TIOCSRS485:
ret = uart_set_rs485_config(uport, uarg);
break;
+
+ case TIOCSISO7816:
+ ret = uart_set_iso7816_config(state->uart_port, uarg);
+ break;
+
+ case TIOCGISO7816:
+ ret = uart_get_iso7816_config(state->uart_port, uarg);
+ break;
default:
if (uport->ops->ioctl)
ret = uport->ops->ioctl(uport, cmd, arg);
@@ -1455,7 +1513,7 @@
}
uart_change_speed(tty, state, old_termios);
- /* reload cflag from termios; port driver may have overriden flags */
+ /* reload cflag from termios; port driver may have overridden flags */
cflag = tty->termios.c_cflag;
/* Handle transition to B0 status */
@@ -1688,6 +1746,16 @@
uart_port_deref(uport);
}
+static int uart_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct uart_driver *drv = driver->driver_state;
+ struct uart_state *state = drv->state + tty->index;
+
+ tty->driver_data = state;
+
+ return tty_standard_install(driver, tty);
+}
+
/*
* Calls to uart_open are serialised by the tty_lock in
* drivers/tty/tty_io.c:tty_open()
@@ -1700,11 +1768,8 @@
*/
static int uart_open(struct tty_struct *tty, struct file *filp)
{
- struct uart_driver *drv = tty->driver->driver_state;
- int retval, line = tty->index;
- struct uart_state *state = drv->state + line;
-
- tty->driver_data = state;
+ struct uart_state *state = tty->driver_data;
+ int retval;
retval = tty_port_open(&state->port, tty, filp);
if (retval > 0)
@@ -1717,6 +1782,7 @@
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
+ int ret;
uport = uart_port_check(state);
if (!uport || uport->flags & UPF_DEAD)
@@ -1727,7 +1793,11 @@
/*
* Start up the serial port.
*/
- return uart_startup(tty, state, 0);
+ ret = uart_startup(tty, state, 0);
+ if (ret > 0)
+ tty_port_set_active(port, 1);
+
+ return ret;
}
static const char *uart_type(struct uart_port *port)
@@ -1894,8 +1964,10 @@
* console=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
*
* The optional form
+ *
* earlycon=<name>,0x<addr>,<options>
* console=<name>,0x<addr>,<options>
+ *
* is also accepted; the returned @iotype will be UPIO_MEM.
*
* Returns 0 on success or -EINVAL on failure
@@ -2389,6 +2461,7 @@
#endif
static const struct tty_operations uart_ops = {
+ .install = uart_install,
.open = uart_open,
.close = uart_close,
.write = uart_write,
@@ -2413,6 +2486,8 @@
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
+ .set_serial = uart_set_info_user,
+ .get_serial = uart_get_info_user,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
@@ -2444,7 +2519,7 @@
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
- int i, retval;
+ int i, retval = -ENOMEM;
BUG_ON(drv->state);
@@ -2496,7 +2571,7 @@
out_kfree:
kfree(drv->state);
out:
- return -ENOMEM;
+ return retval;
}
/**
@@ -2783,7 +2858,7 @@
*/
tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
- if (likely(!IS_ERR(tty_dev))) {
+ if (!IS_ERR(tty_dev)) {
device_set_wakeup_capable(tty_dev, 1);
} else {
dev_err(uport->dev, "Cannot register tty device on line %d\n",
diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c
deleted file mode 100644
index b461d79..0000000
--- a/drivers/tty/serial/serial_ks8695.c
+++ /dev/null
@@ -1,698 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Driver for KS8695 serial ports
- *
- * Based on drivers/serial/serial_amba.c, by Kam Lee.
- *
- * Copyright 2002-2005 Micrel Inc.
- */
-#include <linux/module.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/serial.h>
-#include <linux/console.h>
-#include <linux/sysrq.h>
-#include <linux/device.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/mach/irq.h>
-
-#include <mach/regs-uart.h>
-#include <mach/regs-irq.h>
-
-#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/serial_core.h>
-
-
-#define SERIAL_KS8695_MAJOR 204
-#define SERIAL_KS8695_MINOR 16
-#define SERIAL_KS8695_DEVNAME "ttyAM"
-
-#define SERIAL_KS8695_NR 1
-
-/*
- * Access macros for the KS8695 UART
- */
-#define UART_GET_CHAR(p) (__raw_readl((p)->membase + KS8695_URRB) & 0xFF)
-#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + KS8695_URTH)
-#define UART_GET_FCR(p) __raw_readl((p)->membase + KS8695_URFC)
-#define UART_PUT_FCR(p, c) __raw_writel((c), (p)->membase + KS8695_URFC)
-#define UART_GET_MSR(p) __raw_readl((p)->membase + KS8695_URMS)
-#define UART_GET_LSR(p) __raw_readl((p)->membase + KS8695_URLS)
-#define UART_GET_LCR(p) __raw_readl((p)->membase + KS8695_URLC)
-#define UART_PUT_LCR(p, c) __raw_writel((c), (p)->membase + KS8695_URLC)
-#define UART_GET_MCR(p) __raw_readl((p)->membase + KS8695_URMC)
-#define UART_PUT_MCR(p, c) __raw_writel((c), (p)->membase + KS8695_URMC)
-#define UART_GET_BRDR(p) __raw_readl((p)->membase + KS8695_URBD)
-#define UART_PUT_BRDR(p, c) __raw_writel((c), (p)->membase + KS8695_URBD)
-
-#define KS8695_CLR_TX_INT() __raw_writel(1 << KS8695_IRQ_UART_TX, KS8695_IRQ_VA + KS8695_INTST)
-
-#define UART_DUMMY_LSR_RX 0x100
-#define UART_PORT_SIZE (KS8695_USR - KS8695_URRB + 4)
-
-static inline int tx_enabled(struct uart_port *port)
-{
- return port->unused[0] & 1;
-}
-
-static inline int rx_enabled(struct uart_port *port)
-{
- return port->unused[0] & 2;
-}
-
-static inline int ms_enabled(struct uart_port *port)
-{
- return port->unused[0] & 4;
-}
-
-static inline void ms_enable(struct uart_port *port, int enabled)
-{
- if(enabled)
- port->unused[0] |= 4;
- else
- port->unused[0] &= ~4;
-}
-
-static inline void rx_enable(struct uart_port *port, int enabled)
-{
- if(enabled)
- port->unused[0] |= 2;
- else
- port->unused[0] &= ~2;
-}
-
-static inline void tx_enable(struct uart_port *port, int enabled)
-{
- if(enabled)
- port->unused[0] |= 1;
- else
- port->unused[0] &= ~1;
-}
-
-
-#ifdef SUPPORT_SYSRQ
-static struct console ks8695_console;
-#endif
-
-static void ks8695uart_stop_tx(struct uart_port *port)
-{
- if (tx_enabled(port)) {
- /* use disable_irq_nosync() and not disable_irq() to avoid self
- * imposed deadlock by not waiting for irq handler to end,
- * since this ks8695uart_stop_tx() is called from interrupt context.
- */
- disable_irq_nosync(KS8695_IRQ_UART_TX);
- tx_enable(port, 0);
- }
-}
-
-static void ks8695uart_start_tx(struct uart_port *port)
-{
- if (!tx_enabled(port)) {
- enable_irq(KS8695_IRQ_UART_TX);
- tx_enable(port, 1);
- }
-}
-
-static void ks8695uart_stop_rx(struct uart_port *port)
-{
- if (rx_enabled(port)) {
- disable_irq(KS8695_IRQ_UART_RX);
- rx_enable(port, 0);
- }
-}
-
-static void ks8695uart_enable_ms(struct uart_port *port)
-{
- if (!ms_enabled(port)) {
- enable_irq(KS8695_IRQ_UART_MODEM_STATUS);
- ms_enable(port,1);
- }
-}
-
-static void ks8695uart_disable_ms(struct uart_port *port)
-{
- if (ms_enabled(port)) {
- disable_irq(KS8695_IRQ_UART_MODEM_STATUS);
- ms_enable(port,0);
- }
-}
-
-static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- unsigned int status, ch, lsr, flg, max_count = 256;
-
- status = UART_GET_LSR(port); /* clears pending LSR interrupts */
- while ((status & URLS_URDR) && max_count--) {
- ch = UART_GET_CHAR(port);
- flg = TTY_NORMAL;
-
- port->icount.rx++;
-
- /*
- * Note that the error handling code is
- * out of the main execution path
- */
- lsr = UART_GET_LSR(port) | UART_DUMMY_LSR_RX;
- if (unlikely(lsr & (URLS_URBI | URLS_URPE | URLS_URFE | URLS_URROE))) {
- if (lsr & URLS_URBI) {
- lsr &= ~(URLS_URFE | URLS_URPE);
- port->icount.brk++;
- if (uart_handle_break(port))
- goto ignore_char;
- }
- if (lsr & URLS_URPE)
- port->icount.parity++;
- if (lsr & URLS_URFE)
- port->icount.frame++;
- if (lsr & URLS_URROE)
- port->icount.overrun++;
-
- lsr &= port->read_status_mask;
-
- if (lsr & URLS_URBI)
- flg = TTY_BREAK;
- else if (lsr & URLS_URPE)
- flg = TTY_PARITY;
- else if (lsr & URLS_URFE)
- flg = TTY_FRAME;
- }
-
- if (uart_handle_sysrq_char(port, ch))
- goto ignore_char;
-
- uart_insert_char(port, lsr, URLS_URROE, ch, flg);
-
-ignore_char:
- status = UART_GET_LSR(port);
- }
- tty_flip_buffer_push(&port->state->port);
-
- return IRQ_HANDLED;
-}
-
-
-static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- struct circ_buf *xmit = &port->state->xmit;
- unsigned int count;
-
- if (port->x_char) {
- KS8695_CLR_TX_INT();
- UART_PUT_CHAR(port, port->x_char);
- port->icount.tx++;
- port->x_char = 0;
- return IRQ_HANDLED;
- }
-
- if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
- ks8695uart_stop_tx(port);
- return IRQ_HANDLED;
- }
-
- count = 16; /* fifo size */
- while (!uart_circ_empty(xmit) && (count-- > 0)) {
- KS8695_CLR_TX_INT();
- UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
-
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(port);
-
- if (uart_circ_empty(xmit))
- ks8695uart_stop_tx(port);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id)
-{
- struct uart_port *port = dev_id;
- unsigned int status;
-
- /*
- * clear modem interrupt by reading MSR
- */
- status = UART_GET_MSR(port);
-
- if (status & URMS_URDDCD)
- uart_handle_dcd_change(port, status & URMS_URDDCD);
-
- if (status & URMS_URDDST)
- port->icount.dsr++;
-
- if (status & URMS_URDCTS)
- uart_handle_cts_change(port, status & URMS_URDCTS);
-
- if (status & URMS_URTERI)
- port->icount.rng++;
-
- wake_up_interruptible(&port->state->port.delta_msr_wait);
-
- return IRQ_HANDLED;
-}
-
-static unsigned int ks8695uart_tx_empty(struct uart_port *port)
-{
- return (UART_GET_LSR(port) & URLS_URTE) ? TIOCSER_TEMT : 0;
-}
-
-static unsigned int ks8695uart_get_mctrl(struct uart_port *port)
-{
- unsigned int result = 0;
- unsigned int status;
-
- status = UART_GET_MSR(port);
- if (status & URMS_URDCD)
- result |= TIOCM_CAR;
- if (status & URMS_URDSR)
- result |= TIOCM_DSR;
- if (status & URMS_URCTS)
- result |= TIOCM_CTS;
- if (status & URMS_URRI)
- result |= TIOCM_RI;
-
- return result;
-}
-
-static void ks8695uart_set_mctrl(struct uart_port *port, u_int mctrl)
-{
- unsigned int mcr;
-
- mcr = UART_GET_MCR(port);
- if (mctrl & TIOCM_RTS)
- mcr |= URMC_URRTS;
- else
- mcr &= ~URMC_URRTS;
-
- if (mctrl & TIOCM_DTR)
- mcr |= URMC_URDTR;
- else
- mcr &= ~URMC_URDTR;
-
- UART_PUT_MCR(port, mcr);
-}
-
-static void ks8695uart_break_ctl(struct uart_port *port, int break_state)
-{
- unsigned int lcr;
-
- lcr = UART_GET_LCR(port);
-
- if (break_state == -1)
- lcr |= URLC_URSBC;
- else
- lcr &= ~URLC_URSBC;
-
- UART_PUT_LCR(port, lcr);
-}
-
-static int ks8695uart_startup(struct uart_port *port)
-{
- int retval;
-
- irq_modify_status(KS8695_IRQ_UART_TX, IRQ_NOREQUEST, IRQ_NOAUTOEN);
- tx_enable(port, 0);
- rx_enable(port, 1);
- ms_enable(port, 1);
-
- /*
- * Allocate the IRQ
- */
- retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, 0, "UART TX", port);
- if (retval)
- goto err_tx;
-
- retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, 0, "UART RX", port);
- if (retval)
- goto err_rx;
-
- retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, 0, "UART LineStatus", port);
- if (retval)
- goto err_ls;
-
- retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, 0, "UART ModemStatus", port);
- if (retval)
- goto err_ms;
-
- return 0;
-
-err_ms:
- free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
-err_ls:
- free_irq(KS8695_IRQ_UART_RX, port);
-err_rx:
- free_irq(KS8695_IRQ_UART_TX, port);
-err_tx:
- return retval;
-}
-
-static void ks8695uart_shutdown(struct uart_port *port)
-{
- /*
- * Free the interrupt
- */
- free_irq(KS8695_IRQ_UART_RX, port);
- free_irq(KS8695_IRQ_UART_TX, port);
- free_irq(KS8695_IRQ_UART_MODEM_STATUS, port);
- free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
-
- /* disable break condition and fifos */
- UART_PUT_LCR(port, UART_GET_LCR(port) & ~URLC_URSBC);
- UART_PUT_FCR(port, UART_GET_FCR(port) & ~URFC_URFE);
-}
-
-static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
-{
- unsigned int lcr, fcr = 0;
- unsigned long flags;
- unsigned int baud, quot;
-
- /*
- * Ask the core to calculate the divisor for us.
- */
- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
- quot = uart_get_divisor(port, baud);
-
- switch (termios->c_cflag & CSIZE) {
- case CS5:
- lcr = URCL_5;
- break;
- case CS6:
- lcr = URCL_6;
- break;
- case CS7:
- lcr = URCL_7;
- break;
- default:
- lcr = URCL_8;
- break;
- }
-
- /* stop bits */
- if (termios->c_cflag & CSTOPB)
- lcr |= URLC_URSB;
-
- /* parity */
- if (termios->c_cflag & PARENB) {
- if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */
- if (termios->c_cflag & PARODD)
- lcr |= URPE_MARK;
- else
- lcr |= URPE_SPACE;
- }
- else if (termios->c_cflag & PARODD)
- lcr |= URPE_ODD;
- else
- lcr |= URPE_EVEN;
- }
-
- if (port->fifosize > 1)
- fcr = URFC_URFRT_8 | URFC_URTFR | URFC_URRFR | URFC_URFE;
-
- spin_lock_irqsave(&port->lock, flags);
-
- /*
- * Update the per-port timeout.
- */
- uart_update_timeout(port, termios->c_cflag, baud);
-
- port->read_status_mask = URLS_URROE;
- if (termios->c_iflag & INPCK)
- port->read_status_mask |= (URLS_URFE | URLS_URPE);
- if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- port->read_status_mask |= URLS_URBI;
-
- /*
- * Characters to ignore
- */
- port->ignore_status_mask = 0;
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= (URLS_URFE | URLS_URPE);
- if (termios->c_iflag & IGNBRK) {
- port->ignore_status_mask |= URLS_URBI;
- /*
- * If we're ignoring parity and break indicators,
- * ignore overruns too (for real raw support).
- */
- if (termios->c_iflag & IGNPAR)
- port->ignore_status_mask |= URLS_URROE;
- }
-
- /*
- * Ignore all characters if CREAD is not set.
- */
- if ((termios->c_cflag & CREAD) == 0)
- port->ignore_status_mask |= UART_DUMMY_LSR_RX;
-
- /* first, disable everything */
- if (UART_ENABLE_MS(port, termios->c_cflag))
- ks8695uart_enable_ms(port);
- else
- ks8695uart_disable_ms(port);
-
- /* Set baud rate */
- UART_PUT_BRDR(port, quot);
-
- UART_PUT_LCR(port, lcr);
- UART_PUT_FCR(port, fcr);
-
- spin_unlock_irqrestore(&port->lock, flags);
-}
-
-static const char *ks8695uart_type(struct uart_port *port)
-{
- return port->type == PORT_KS8695 ? "KS8695" : NULL;
-}
-
-/*
- * Release the memory region(s) being used by 'port'
- */
-static void ks8695uart_release_port(struct uart_port *port)
-{
- release_mem_region(port->mapbase, UART_PORT_SIZE);
-}
-
-/*
- * Request the memory region(s) being used by 'port'
- */
-static int ks8695uart_request_port(struct uart_port *port)
-{
- return request_mem_region(port->mapbase, UART_PORT_SIZE,
- "serial_ks8695") != NULL ? 0 : -EBUSY;
-}
-
-/*
- * Configure/autoconfigure the port.
- */
-static void ks8695uart_config_port(struct uart_port *port, int flags)
-{
- if (flags & UART_CONFIG_TYPE) {
- port->type = PORT_KS8695;
- ks8695uart_request_port(port);
- }
-}
-
-/*
- * verify the new serial_struct (for TIOCSSERIAL).
- */
-static int ks8695uart_verify_port(struct uart_port *port, struct serial_struct *ser)
-{
- int ret = 0;
-
- if (ser->type != PORT_UNKNOWN && ser->type != PORT_KS8695)
- ret = -EINVAL;
- if (ser->irq != port->irq)
- ret = -EINVAL;
- if (ser->baud_base < 9600)
- ret = -EINVAL;
- return ret;
-}
-
-static struct uart_ops ks8695uart_pops = {
- .tx_empty = ks8695uart_tx_empty,
- .set_mctrl = ks8695uart_set_mctrl,
- .get_mctrl = ks8695uart_get_mctrl,
- .stop_tx = ks8695uart_stop_tx,
- .start_tx = ks8695uart_start_tx,
- .stop_rx = ks8695uart_stop_rx,
- .enable_ms = ks8695uart_enable_ms,
- .break_ctl = ks8695uart_break_ctl,
- .startup = ks8695uart_startup,
- .shutdown = ks8695uart_shutdown,
- .set_termios = ks8695uart_set_termios,
- .type = ks8695uart_type,
- .release_port = ks8695uart_release_port,
- .request_port = ks8695uart_request_port,
- .config_port = ks8695uart_config_port,
- .verify_port = ks8695uart_verify_port,
-};
-
-static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
- {
- .membase = KS8695_UART_VA,
- .mapbase = KS8695_UART_PA,
- .iotype = SERIAL_IO_MEM,
- .irq = KS8695_IRQ_UART_TX,
- .uartclk = KS8695_CLOCK_RATE * 16,
- .fifosize = 16,
- .ops = &ks8695uart_pops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- }
-};
-
-#ifdef CONFIG_SERIAL_KS8695_CONSOLE
-static void ks8695_console_putchar(struct uart_port *port, int ch)
-{
- while (!(UART_GET_LSR(port) & URLS_URTHRE))
- barrier();
-
- UART_PUT_CHAR(port, ch);
-}
-
-static void ks8695_console_write(struct console *co, const char *s, u_int count)
-{
- struct uart_port *port = ks8695uart_ports + co->index;
-
- uart_console_write(port, s, count, ks8695_console_putchar);
-}
-
-static void __init ks8695_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
-{
- unsigned int lcr;
-
- lcr = UART_GET_LCR(port);
-
- switch (lcr & URLC_PARITY) {
- case URPE_ODD:
- *parity = 'o';
- break;
- case URPE_EVEN:
- *parity = 'e';
- break;
- default:
- *parity = 'n';
- }
-
- switch (lcr & URLC_URCL) {
- case URCL_5:
- *bits = 5;
- break;
- case URCL_6:
- *bits = 6;
- break;
- case URCL_7:
- *bits = 7;
- break;
- default:
- *bits = 8;
- }
-
- *baud = port->uartclk / (UART_GET_BRDR(port) & 0x0FFF);
- *baud /= 16;
- *baud &= 0xFFFFFFF0;
-}
-
-static int __init ks8695_console_setup(struct console *co, char *options)
-{
- struct uart_port *port;
- int baud = 115200;
- int bits = 8;
- int parity = 'n';
- int flow = 'n';
-
- /*
- * Check whether an invalid uart number has been specified, and
- * if so, search for the first available port that does have
- * console support.
- */
- port = uart_get_console(ks8695uart_ports, SERIAL_KS8695_NR, co);
-
- if (options)
- uart_parse_options(options, &baud, &parity, &bits, &flow);
- else
- ks8695_console_get_options(port, &baud, &parity, &bits);
-
- return uart_set_options(port, co, baud, parity, bits, flow);
-}
-
-static struct uart_driver ks8695_reg;
-
-static struct console ks8695_console = {
- .name = SERIAL_KS8695_DEVNAME,
- .write = ks8695_console_write,
- .device = uart_console_device,
- .setup = ks8695_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1,
- .data = &ks8695_reg,
-};
-
-static int __init ks8695_console_init(void)
-{
- add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL);
- register_console(&ks8695_console);
- return 0;
-}
-
-console_initcall(ks8695_console_init);
-
-#define KS8695_CONSOLE &ks8695_console
-#else
-#define KS8695_CONSOLE NULL
-#endif
-
-static struct uart_driver ks8695_reg = {
- .owner = THIS_MODULE,
- .driver_name = "serial_ks8695",
- .dev_name = SERIAL_KS8695_DEVNAME,
- .major = SERIAL_KS8695_MAJOR,
- .minor = SERIAL_KS8695_MINOR,
- .nr = SERIAL_KS8695_NR,
- .cons = KS8695_CONSOLE,
-};
-
-static int __init ks8695uart_init(void)
-{
- int i, ret;
-
- printk(KERN_INFO "Serial: Micrel KS8695 UART driver\n");
-
- ret = uart_register_driver(&ks8695_reg);
- if (ret)
- return ret;
-
- for (i = 0; i < SERIAL_KS8695_NR; i++)
- uart_add_one_port(&ks8695_reg, &ks8695uart_ports[0]);
-
- return 0;
-}
-
-static void __exit ks8695uart_exit(void)
-{
- int i;
-
- for (i = 0; i < SERIAL_KS8695_NR; i++)
- uart_remove_one_port(&ks8695_reg, &ks8695uart_ports[0]);
- uart_unregister_driver(&ks8695_reg);
-}
-
-module_init(ks8695uart_init);
-module_exit(ks8695uart_exit);
-
-MODULE_DESCRIPTION("KS8695 serial port driver");
-MODULE_AUTHOR("Micrel Inc.");
-MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325..fb47812 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -12,6 +12,7 @@
#include <linux/termios.h>
#include <linux/serial_core.h>
#include <linux/module.h>
+#include <linux/property.h>
#include "serial_mctrl_gpio.h"
@@ -26,39 +27,48 @@
static const struct {
const char *name;
unsigned int mctrl;
- bool dir_out;
+ enum gpiod_flags flags;
} mctrl_gpios_desc[UART_GPIO_MAX] = {
- { "cts", TIOCM_CTS, false, },
- { "dsr", TIOCM_DSR, false, },
- { "dcd", TIOCM_CD, false, },
- { "rng", TIOCM_RNG, false, },
- { "rts", TIOCM_RTS, true, },
- { "dtr", TIOCM_DTR, true, },
+ { "cts", TIOCM_CTS, GPIOD_IN, },
+ { "dsr", TIOCM_DSR, GPIOD_IN, },
+ { "dcd", TIOCM_CD, GPIOD_IN, },
+ { "rng", TIOCM_RNG, GPIOD_IN, },
+ { "rts", TIOCM_RTS, GPIOD_OUT_LOW, },
+ { "dtr", TIOCM_DTR, GPIOD_OUT_LOW, },
};
+static bool mctrl_gpio_flags_is_dir_out(unsigned int idx)
+{
+ return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT;
+}
+
void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ DECLARE_BITMAP(values, UART_GPIO_MAX);
unsigned int count = 0;
if (gpios == NULL)
return;
for (i = 0; i < UART_GPIO_MAX; i++)
- if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, values,
+ mctrl & mctrl_gpios_desc[i].mctrl);
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, NULL, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
enum mctrl_gpio_idx gidx)
{
+ if (gpios == NULL)
+ return NULL;
+
return gpios->gpio[gidx];
}
EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
@@ -71,7 +81,7 @@
return *mctrl;
for (i = 0; i < UART_GPIO_MAX; i++) {
- if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) {
if (gpiod_get_value(gpios->gpio[i]))
*mctrl |= mctrl_gpios_desc[i].mctrl;
else
@@ -92,7 +102,7 @@
return *mctrl;
for (i = 0; i < UART_GPIO_MAX; i++) {
- if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
if (gpiod_get_value(gpios->gpio[i]))
*mctrl |= mctrl_gpios_desc[i].mctrl;
else
@@ -114,17 +124,25 @@
return ERR_PTR(-ENOMEM);
for (i = 0; i < UART_GPIO_MAX; i++) {
- enum gpiod_flags flags;
+ char *gpio_str;
+ bool present;
- if (mctrl_gpios_desc[i].dir_out)
- flags = GPIOD_OUT_LOW;
- else
- flags = GPIOD_IN;
+ /* Check if GPIO property exists and continue if not */
+ gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
+ mctrl_gpios_desc[i].name);
+ if (!gpio_str)
+ continue;
+
+ present = device_property_present(dev, gpio_str);
+ kfree(gpio_str);
+ if (!present)
+ continue;
gpios->gpio[i] =
devm_gpiod_get_index_optional(dev,
mctrl_gpios_desc[i].name,
- idx, flags);
+ idx,
+ mctrl_gpios_desc[i].flags);
if (IS_ERR(gpios->gpio[i]))
return ERR_CAST(gpios->gpio[i]);
@@ -185,7 +203,7 @@
for (i = 0; i < UART_GPIO_MAX; ++i) {
int ret;
- if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out)
+ if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i))
continue;
ret = gpiod_to_irq(gpios->gpio[i]);
diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h
index b7d3cca..1b2ff50 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.h
+++ b/drivers/tty/serial/serial_mctrl_gpio.h
@@ -114,19 +114,19 @@
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
enum mctrl_gpio_idx gidx)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline
struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline
struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline
diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c
index 1b4008d..d22ccb3 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -248,7 +248,6 @@
sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST);
/* TX4925 BUG WORKAROUND. Accessing SIOC register
* immediately after soft reset causes bus error. */
- mmiowb();
udelay(1);
while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout)
udelay(1);
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index effba6c..22e5d4e 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -838,19 +838,9 @@
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit)) {
+ if (uart_circ_empty(xmit))
sci_stop_tx(port);
- } else {
- ctrl = serial_port_in(port, SCSCR);
- if (port->type != PORT_SCI) {
- serial_port_in(port, SCxSR); /* Dummy read */
- sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
- }
-
- ctrl |= SCSCR_TIE;
- serial_port_out(port, SCSCR, ctrl);
- }
}
/* On SH3, SCIF may read end-of-break as a space->mark char */
@@ -1102,9 +1092,8 @@
scif_set_rtrg(port, 1);
}
-static ssize_t rx_trigger_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t rx_fifo_trigger_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct uart_port *port = dev_get_drvdata(dev);
struct sci_port *sci = to_sci_port(port);
@@ -1112,10 +1101,9 @@
return sprintf(buf, "%d\n", sci->rx_trigger);
}
-static ssize_t rx_trigger_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
+static ssize_t rx_fifo_trigger_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct uart_port *port = dev_get_drvdata(dev);
struct sci_port *sci = to_sci_port(port);
@@ -1133,7 +1121,7 @@
return count;
}
-static DEVICE_ATTR(rx_fifo_trigger, 0644, rx_trigger_show, rx_trigger_store);
+static DEVICE_ATTR_RW(rx_fifo_trigger);
static ssize_t rx_fifo_timeout_show(struct device *dev,
struct device_attribute *attr,
@@ -1243,12 +1231,22 @@
return -1;
}
-static void sci_rx_dma_release(struct sci_port *s)
+static void sci_dma_rx_chan_invalidate(struct sci_port *s)
+{
+ unsigned int i;
+
+ s->chan_rx = NULL;
+ for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++)
+ s->cookie_rx[i] = -EINVAL;
+ s->active_rx = 0;
+}
+
+static void sci_dma_rx_release(struct sci_port *s)
{
struct dma_chan *chan = s->chan_rx_saved;
- s->chan_rx_saved = s->chan_rx = NULL;
- s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
+ s->chan_rx_saved = NULL;
+ sci_dma_rx_chan_invalidate(s);
dmaengine_terminate_sync(chan);
dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0],
sg_dma_address(&s->sg_rx[0]));
@@ -1264,6 +1262,20 @@
hrtimer_start(hrt, t, HRTIMER_MODE_REL);
}
+static void sci_dma_rx_reenable_irq(struct sci_port *s)
+{
+ struct uart_port *port = &s->port;
+ u16 scr;
+
+ /* Direct new serial port interrupts back to CPU */
+ scr = serial_port_in(port, SCSCR);
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+ scr &= ~SCSCR_RDRQE;
+ enable_irq(s->irqs[SCIx_RXI_IRQ]);
+ }
+ serial_port_out(port, SCSCR, scr | SCSCR_RIE);
+}
+
static void sci_dma_rx_complete(void *arg)
{
struct sci_port *s = arg;
@@ -1313,12 +1325,13 @@
dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
/* Switch to PIO */
spin_lock_irqsave(&port->lock, flags);
- s->chan_rx = NULL;
- sci_start_rx(port);
+ dmaengine_terminate_async(chan);
+ sci_dma_rx_chan_invalidate(s);
+ sci_dma_rx_reenable_irq(s);
spin_unlock_irqrestore(&port->lock, flags);
}
-static void sci_tx_dma_release(struct sci_port *s)
+static void sci_dma_tx_release(struct sci_port *s)
{
struct dma_chan *chan = s->chan_tx_saved;
@@ -1331,7 +1344,7 @@
dma_release_channel(chan);
}
-static void sci_submit_rx(struct sci_port *s)
+static int sci_dma_rx_submit(struct sci_port *s, bool port_lock_held)
{
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
@@ -1359,22 +1372,22 @@
s->active_rx = s->cookie_rx[0];
dma_async_issue_pending(chan);
- return;
+ return 0;
fail:
+ /* Switch to PIO */
+ if (!port_lock_held)
+ spin_lock_irqsave(&port->lock, flags);
if (i)
dmaengine_terminate_async(chan);
- for (i = 0; i < 2; i++)
- s->cookie_rx[i] = -EINVAL;
- s->active_rx = -EINVAL;
- /* Switch to PIO */
- spin_lock_irqsave(&port->lock, flags);
- s->chan_rx = NULL;
+ sci_dma_rx_chan_invalidate(s);
sci_start_rx(port);
- spin_unlock_irqrestore(&port->lock, flags);
+ if (!port_lock_held)
+ spin_unlock_irqrestore(&port->lock, flags);
+ return -EAGAIN;
}
-static void work_fn_tx(struct work_struct *work)
+static void sci_dma_tx_work_fn(struct work_struct *work)
{
struct sci_port *s = container_of(work, struct sci_port, work_tx);
struct dma_async_tx_descriptor *desc;
@@ -1383,6 +1396,7 @@
struct circ_buf *xmit = &port->state->xmit;
unsigned long flags;
dma_addr_t buf;
+ int head, tail;
/*
* DMA is idle now.
@@ -1392,16 +1406,23 @@
* consistent xmit buffer state.
*/
spin_lock_irq(&port->lock);
- buf = s->tx_dma_addr + (xmit->tail & (UART_XMIT_SIZE - 1));
+ head = xmit->head;
+ tail = xmit->tail;
+ buf = s->tx_dma_addr + (tail & (UART_XMIT_SIZE - 1));
s->tx_dma_len = min_t(unsigned int,
- CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE),
- CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE));
- spin_unlock_irq(&port->lock);
+ CIRC_CNT(head, tail, UART_XMIT_SIZE),
+ CIRC_CNT_TO_END(head, tail, UART_XMIT_SIZE));
+ if (!s->tx_dma_len) {
+ /* Transmit buffer has been flushed */
+ spin_unlock_irq(&port->lock);
+ return;
+ }
desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
+ spin_unlock_irq(&port->lock);
dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n");
goto switch_to_pio;
}
@@ -1409,18 +1430,18 @@
dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len,
DMA_TO_DEVICE);
- spin_lock_irq(&port->lock);
desc->callback = sci_dma_tx_complete;
desc->callback_param = s;
- spin_unlock_irq(&port->lock);
s->cookie_tx = dmaengine_submit(desc);
if (dma_submit_error(s->cookie_tx)) {
+ spin_unlock_irq(&port->lock);
dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
goto switch_to_pio;
}
+ spin_unlock_irq(&port->lock);
dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n",
- __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
+ __func__, xmit->buf, tail, head, s->cookie_tx);
dma_async_issue_pending(chan);
return;
@@ -1433,7 +1454,7 @@
return;
}
-static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
+static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t)
{
struct sci_port *s = container_of(t, struct sci_port, rx_timer);
struct dma_chan *chan = s->chan_rx;
@@ -1443,7 +1464,6 @@
unsigned long flags;
unsigned int read;
int active, count;
- u16 scr;
dev_dbg(port->dev, "DMA Rx timed out\n");
@@ -1491,15 +1511,9 @@
}
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- sci_submit_rx(s);
+ sci_dma_rx_submit(s, true);
- /* Direct new serial port interrupts back to CPU */
- scr = serial_port_in(port, SCSCR);
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- scr &= ~SCSCR_RDRQE;
- enable_irq(s->irqs[SCIx_RXI_IRQ]);
- }
- serial_port_out(port, SCSCR, scr | SCSCR_RIE);
+ sci_dma_rx_reenable_irq(s);
spin_unlock_irqrestore(&port->lock, flags);
@@ -1516,7 +1530,7 @@
chan = dma_request_slave_channel(port->dev,
dir == DMA_MEM_TO_DEV ? "tx" : "rx");
if (!chan) {
- dev_warn(port->dev, "dma_request_slave_channel failed\n");
+ dev_dbg(port->dev, "dma_request_slave_channel failed\n");
return NULL;
}
@@ -1549,6 +1563,13 @@
dev_dbg(port->dev, "%s: port %d\n", __func__, port->line);
+ /*
+ * DMA on console may interfere with Kernel log messages which use
+ * plain putchar(). So, simply don't use it with a console.
+ */
+ if (uart_console(port))
+ return;
+
if (!port->dev->of_node)
return;
@@ -1577,7 +1598,7 @@
__func__, UART_XMIT_SIZE,
port->state->xmit.buf, &s->tx_dma_addr);
- INIT_WORK(&s->work_tx, work_fn_tx);
+ INIT_WORK(&s->work_tx, sci_dma_tx_work_fn);
s->chan_tx_saved = s->chan_tx = chan;
}
}
@@ -1612,12 +1633,12 @@
}
hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- s->rx_timer.function = rx_timer_fn;
+ s->rx_timer.function = sci_dma_rx_timer_fn;
s->chan_rx_saved = s->chan_rx = chan;
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
- sci_submit_rx(s);
+ sci_dma_rx_submit(s, false);
}
}
@@ -1626,18 +1647,25 @@
struct sci_port *s = to_sci_port(port);
if (s->chan_tx_saved)
- sci_tx_dma_release(s);
+ sci_dma_tx_release(s);
if (s->chan_rx_saved)
- sci_rx_dma_release(s);
+ sci_dma_rx_release(s);
}
static void sci_flush_buffer(struct uart_port *port)
{
+ struct sci_port *s = to_sci_port(port);
+
/*
* In uart_flush_buffer(), the xmit circular buffer has just been
- * cleared, so we have to reset tx_dma_len accordingly.
+ * cleared, so we have to reset tx_dma_len accordingly, and stop any
+ * pending transfers
*/
- to_sci_port(port)->tx_dma_len = 0;
+ s->tx_dma_len = 0;
+ if (s->chan_tx) {
+ dmaengine_terminate_async(s->chan_tx);
+ s->cookie_tx = -EINVAL;
+ }
}
#else /* !CONFIG_SERIAL_SH_SCI_DMA */
static inline void sci_request_dma(struct uart_port *port)
@@ -1666,8 +1694,10 @@
disable_irq_nosync(irq);
scr |= SCSCR_RDRQE;
} else {
+ if (sci_dma_rx_submit(s, false) < 0)
+ goto handle_pio;
+
scr &= ~SCSCR_RIE;
- sci_submit_rx(s);
}
serial_port_out(port, SCSCR, scr);
/* Clear current interrupt */
@@ -1679,6 +1709,8 @@
return IRQ_HANDLED;
}
+
+handle_pio:
#endif
if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) {
@@ -1693,7 +1725,7 @@
* of whether the I_IXOFF is set, otherwise, how is the interrupt
* to be disabled?
*/
- sci_receive_chars(ptr);
+ sci_receive_chars(port);
return IRQ_HANDLED;
}
@@ -1749,7 +1781,7 @@
} else {
sci_handle_fifo_overrun(port);
if (!s->chan_rx)
- sci_receive_chars(ptr);
+ sci_receive_chars(port);
}
sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));
@@ -1914,7 +1946,7 @@
static void sci_free_irq(struct sci_port *port)
{
- int i;
+ int i, j;
/*
* Intentionally in reverse order so we iterate over the muxed
@@ -1930,6 +1962,13 @@
if (unlikely(irq < 0))
continue;
+ /* Check if already freed (irq was muxed) */
+ for (j = 0; j < i; j++)
+ if (port->irqs[j] == irq)
+ j = i + 1;
+ if (j > i)
+ continue;
+
free_irq(port->irqs[i], port);
kfree(port->irqstr[i]);
@@ -2060,12 +2099,12 @@
if (s->autorts) {
if (sci_get_cts(port))
mctrl |= TIOCM_CTS;
- } else if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS))) {
+ } else if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS)) {
mctrl |= TIOCM_CTS;
}
- if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR)))
+ if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR))
mctrl |= TIOCM_DSR;
- if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD)))
+ if (!mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD))
mctrl |= TIOCM_CAR;
return mctrl;
@@ -2493,14 +2532,16 @@
* center of the last stop bit in sampling clocks.
*/
int last_stop = bits * 2 - 1;
- int deviation = min_err * srr * last_stop / 2 / baud;
+ int deviation = DIV_ROUND_CLOSEST(min_err * last_stop *
+ (int)(srr + 1),
+ 2 * (int)baud);
if (abs(deviation) >= 2) {
/* At least two sampling clocks off at the
* last stop bit; we can increase the error
* margin by shifting the sampling point.
*/
- int shift = min(-8, max(7, deviation / 2));
+ int shift = clamp(deviation / 2, -8, 7);
hssrr |= (shift << HSCIF_SRHP_SHIFT) &
HSCIF_SRHP_MASK;
@@ -2853,8 +2894,12 @@
port->mapbase = res->start;
sci_port->reg_size = resource_size(res);
- for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i)
- sci_port->irqs[i] = platform_get_irq(dev, i);
+ for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) {
+ if (i)
+ sci_port->irqs[i] = platform_get_irq_optional(dev, i);
+ else
+ sci_port->irqs[i] = platform_get_irq(dev, i);
+ }
/* The SCI generates several interrupts. They can be muxed together or
* connected to different interrupt lines. In the muxed case only one
@@ -3109,14 +3154,10 @@
sci_cleanup_single(port);
- if (port->port.fifosize > 1) {
- sysfs_remove_file(&dev->dev.kobj,
- &dev_attr_rx_fifo_trigger.attr);
- }
- if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF) {
- sysfs_remove_file(&dev->dev.kobj,
- &dev_attr_rx_fifo_timeout.attr);
- }
+ if (port->port.fifosize > 1)
+ device_remove_file(&dev->dev, &dev_attr_rx_fifo_trigger);
+ if (type == PORT_SCIFA || type == PORT_SCIFB || type == PORT_HSCIF)
+ device_remove_file(&dev->dev, &dev_attr_rx_fifo_timeout);
return 0;
}
@@ -3244,14 +3285,12 @@
return ret;
sciport->gpios = mctrl_gpio_init(&sciport->port, 0);
- if (IS_ERR(sciport->gpios) && PTR_ERR(sciport->gpios) != -ENOSYS)
+ if (IS_ERR(sciport->gpios))
return PTR_ERR(sciport->gpios);
if (sciport->has_rtscts) {
- if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
- UART_GPIO_CTS)) ||
- !IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
- UART_GPIO_RTS))) {
+ if (mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_CTS) ||
+ mctrl_gpio_to_gpiod(sciport->gpios, UART_GPIO_RTS)) {
dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
return -EINVAL;
}
@@ -3304,19 +3343,17 @@
return ret;
if (sp->port.fifosize > 1) {
- ret = sysfs_create_file(&dev->dev.kobj,
- &dev_attr_rx_fifo_trigger.attr);
+ ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_trigger);
if (ret)
return ret;
}
if (sp->port.type == PORT_SCIFA || sp->port.type == PORT_SCIFB ||
sp->port.type == PORT_HSCIF) {
- ret = sysfs_create_file(&dev->dev.kobj,
- &dev_attr_rx_fifo_timeout.attr);
+ ret = device_create_file(&dev->dev, &dev_attr_rx_fifo_timeout);
if (ret) {
if (sp->port.fifosize > 1) {
- sysfs_remove_file(&dev->dev.kobj,
- &dev_attr_rx_fifo_trigger.attr);
+ device_remove_file(&dev->dev,
+ &dev_attr_rx_fifo_trigger);
}
return ret;
}
@@ -3414,6 +3451,12 @@
{
return early_console_setup(device, PORT_SCIF);
}
+static int __init rzscifa_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ port_cfg.regtype = SCIx_RZ_SCIFA_REGTYPE;
+ return early_console_setup(device, PORT_SCIF);
+}
static int __init scifa_early_console_setup(struct earlycon_device *device,
const char *opt)
{
@@ -3432,6 +3475,7 @@
OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup);
OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup);
+OF_EARLYCON_DECLARE(scif, "renesas,scif-r7s9210", rzscifa_early_console_setup);
OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup);
OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup);
OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c
new file mode 100644
index 0000000..d5f81b9
--- /dev/null
+++ b/drivers/tty/serial/sifive.c
@@ -0,0 +1,1054 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SiFive UART driver
+ * Copyright (C) 2018 Paul Walmsley <paul@pwsan.com>
+ * Copyright (C) 2018-2019 SiFive
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Based partially on:
+ * - drivers/tty/serial/pxa.c
+ * - drivers/tty/serial/amba-pl011.c
+ * - drivers/tty/serial/uartlite.c
+ * - drivers/tty/serial/omap-serial.c
+ * - drivers/pwm/pwm-sifive.c
+ *
+ * See the following sources for further documentation:
+ * - Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of
+ * SiFive FE310-G000 v2p3
+ * - The tree/master/src/main/scala/devices/uart directory of
+ * https://github.com/sifive/sifive-blocks/
+ *
+ * The SiFive UART design is not 8250-compatible. The following common
+ * features are not supported:
+ * - Word lengths other than 8 bits
+ * - Break handling
+ * - Parity
+ * - Flow control
+ * - Modem signals (DSR, RI, etc.)
+ * On the other hand, the design is free from the baggage of the 8250
+ * programming model.
+ */
+
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/*
+ * Register offsets
+ */
+
+/* TXDATA */
+#define SIFIVE_SERIAL_TXDATA_OFFS 0x0
+#define SIFIVE_SERIAL_TXDATA_FULL_SHIFT 31
+#define SIFIVE_SERIAL_TXDATA_FULL_MASK (1 << SIFIVE_SERIAL_TXDATA_FULL_SHIFT)
+#define SIFIVE_SERIAL_TXDATA_DATA_SHIFT 0
+#define SIFIVE_SERIAL_TXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_TXDATA_DATA_SHIFT)
+
+/* RXDATA */
+#define SIFIVE_SERIAL_RXDATA_OFFS 0x4
+#define SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT 31
+#define SIFIVE_SERIAL_RXDATA_EMPTY_MASK (1 << SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT)
+#define SIFIVE_SERIAL_RXDATA_DATA_SHIFT 0
+#define SIFIVE_SERIAL_RXDATA_DATA_MASK (0xff << SIFIVE_SERIAL_RXDATA_DATA_SHIFT)
+
+/* TXCTRL */
+#define SIFIVE_SERIAL_TXCTRL_OFFS 0x8
+#define SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT 16
+#define SIFIVE_SERIAL_TXCTRL_TXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
+#define SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT 1
+#define SIFIVE_SERIAL_TXCTRL_NSTOP_MASK (1 << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT)
+#define SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT 0
+#define SIFIVE_SERIAL_TXCTRL_TXEN_MASK (1 << SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT)
+
+/* RXCTRL */
+#define SIFIVE_SERIAL_RXCTRL_OFFS 0xC
+#define SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT 16
+#define SIFIVE_SERIAL_RXCTRL_RXCNT_MASK (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
+#define SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT 0
+#define SIFIVE_SERIAL_RXCTRL_RXEN_MASK (1 << SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT)
+
+/* IE */
+#define SIFIVE_SERIAL_IE_OFFS 0x10
+#define SIFIVE_SERIAL_IE_RXWM_SHIFT 1
+#define SIFIVE_SERIAL_IE_RXWM_MASK (1 << SIFIVE_SERIAL_IE_RXWM_SHIFT)
+#define SIFIVE_SERIAL_IE_TXWM_SHIFT 0
+#define SIFIVE_SERIAL_IE_TXWM_MASK (1 << SIFIVE_SERIAL_IE_TXWM_SHIFT)
+
+/* IP */
+#define SIFIVE_SERIAL_IP_OFFS 0x14
+#define SIFIVE_SERIAL_IP_RXWM_SHIFT 1
+#define SIFIVE_SERIAL_IP_RXWM_MASK (1 << SIFIVE_SERIAL_IP_RXWM_SHIFT)
+#define SIFIVE_SERIAL_IP_TXWM_SHIFT 0
+#define SIFIVE_SERIAL_IP_TXWM_MASK (1 << SIFIVE_SERIAL_IP_TXWM_SHIFT)
+
+/* DIV */
+#define SIFIVE_SERIAL_DIV_OFFS 0x18
+#define SIFIVE_SERIAL_DIV_DIV_SHIFT 0
+#define SIFIVE_SERIAL_DIV_DIV_MASK (0xffff << SIFIVE_SERIAL_IP_DIV_SHIFT)
+
+/*
+ * Config macros
+ */
+
+/*
+ * SIFIVE_SERIAL_MAX_PORTS: maximum number of UARTs on a device that can
+ * host a serial console
+ */
+#define SIFIVE_SERIAL_MAX_PORTS 8
+
+/*
+ * SIFIVE_DEFAULT_BAUD_RATE: default baud rate that the driver should
+ * configure itself to use
+ */
+#define SIFIVE_DEFAULT_BAUD_RATE 115200
+
+/* SIFIVE_SERIAL_NAME: our driver's name that we pass to the operating system */
+#define SIFIVE_SERIAL_NAME "sifive-serial"
+
+/* SIFIVE_TTY_PREFIX: tty name prefix for SiFive serial ports */
+#define SIFIVE_TTY_PREFIX "ttySIF"
+
+/* SIFIVE_TX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
+#define SIFIVE_TX_FIFO_DEPTH 8
+
+/* SIFIVE_RX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
+#define SIFIVE_RX_FIFO_DEPTH 8
+
+#if (SIFIVE_TX_FIFO_DEPTH != SIFIVE_RX_FIFO_DEPTH)
+#error Driver does not support configurations with different TX, RX FIFO sizes
+#endif
+
+/*
+ *
+ */
+
+/**
+ * sifive_serial_port - driver-specific data extension to struct uart_port
+ * @port: struct uart_port embedded in this struct
+ * @dev: struct device *
+ * @ier: shadowed copy of the interrupt enable register
+ * @clkin_rate: input clock to the UART IP block.
+ * @baud_rate: UART serial line rate (e.g., 115200 baud)
+ * @clk_notifier: clock rate change notifier for upstream clock changes
+ *
+ * Configuration data specific to this SiFive UART.
+ */
+struct sifive_serial_port {
+ struct uart_port port;
+ struct device *dev;
+ unsigned char ier;
+ unsigned long clkin_rate;
+ unsigned long baud_rate;
+ struct clk *clk;
+ struct notifier_block clk_notifier;
+};
+
+/*
+ * Structure container-of macros
+ */
+
+#define port_to_sifive_serial_port(p) (container_of((p), \
+ struct sifive_serial_port, \
+ port))
+
+#define notifier_to_sifive_serial_port(nb) (container_of((nb), \
+ struct sifive_serial_port, \
+ clk_notifier))
+
+/*
+ * Forward declarations
+ */
+static void sifive_serial_stop_tx(struct uart_port *port);
+
+/*
+ * Internal functions
+ */
+
+/**
+ * __ssp_early_writel() - write to a SiFive serial port register (early)
+ * @port: pointer to a struct uart_port record
+ * @offs: register address offset from the IP block base address
+ * @v: value to write to the register
+ *
+ * Given a pointer @port to a struct uart_port record, write the value
+ * @v to the IP block register address offset @offs. This function is
+ * intended for early console use.
+ *
+ * Context: Intended to be used only by the earlyconsole code.
+ */
+static void __ssp_early_writel(u32 v, u16 offs, struct uart_port *port)
+{
+ writel_relaxed(v, port->membase + offs);
+}
+
+/**
+ * __ssp_early_readl() - read from a SiFive serial port register (early)
+ * @port: pointer to a struct uart_port record
+ * @offs: register address offset from the IP block base address
+ *
+ * Given a pointer @port to a struct uart_port record, read the
+ * contents of the IP block register located at offset @offs from the
+ * IP block base and return it. This function is intended for early
+ * console use.
+ *
+ * Context: Intended to be called only by the earlyconsole code or by
+ * __ssp_readl() or __ssp_writel() (in this driver)
+ *
+ * Returns: the register value read from the UART.
+ */
+static u32 __ssp_early_readl(struct uart_port *port, u16 offs)
+{
+ return readl_relaxed(port->membase + offs);
+}
+
+/**
+ * __ssp_writel() - write to a SiFive serial port register
+ * @v: value to write to the register
+ * @offs: register address offset from the IP block base address
+ * @ssp: pointer to a struct sifive_serial_port record
+ *
+ * Write the value @v to the IP block register located at offset @offs from the
+ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
+ *
+ * Context: Any context.
+ */
+static void __ssp_writel(u32 v, u16 offs, struct sifive_serial_port *ssp)
+{
+ __ssp_early_writel(v, offs, &ssp->port);
+}
+
+/**
+ * __ssp_readl() - read from a SiFive serial port register
+ * @ssp: pointer to a struct sifive_serial_port record
+ * @offs: register address offset from the IP block base address
+ *
+ * Read the contents of the IP block register located at offset @offs from the
+ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
+ *
+ * Context: Any context.
+ *
+ * Returns: the value of the UART register
+ */
+static u32 __ssp_readl(struct sifive_serial_port *ssp, u16 offs)
+{
+ return __ssp_early_readl(&ssp->port, offs);
+}
+
+/**
+ * sifive_serial_is_txfifo_full() - is the TXFIFO full?
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Read the transmit FIFO "full" bit, returning a non-zero value if the
+ * TX FIFO is full, or zero if space remains. Intended to be used to prevent
+ * writes to the TX FIFO when it's full.
+ *
+ * Returns: SIFIVE_SERIAL_TXDATA_FULL_MASK (non-zero) if the transmit FIFO
+ * is full, or 0 if space remains.
+ */
+static int sifive_serial_is_txfifo_full(struct sifive_serial_port *ssp)
+{
+ return __ssp_readl(ssp, SIFIVE_SERIAL_TXDATA_OFFS) &
+ SIFIVE_SERIAL_TXDATA_FULL_MASK;
+}
+
+/**
+ * __ssp_transmit_char() - enqueue a byte to transmit onto the TX FIFO
+ * @ssp: pointer to a struct sifive_serial_port
+ * @ch: character to transmit
+ *
+ * Enqueue a byte @ch onto the transmit FIFO, given a pointer @ssp to the
+ * struct sifive_serial_port * to transmit on. Caller should first check to
+ * ensure that the TXFIFO has space; see sifive_serial_is_txfifo_full().
+ *
+ * Context: Any context.
+ */
+static void __ssp_transmit_char(struct sifive_serial_port *ssp, int ch)
+{
+ __ssp_writel(ch, SIFIVE_SERIAL_TXDATA_OFFS, ssp);
+}
+
+/**
+ * __ssp_transmit_chars() - enqueue multiple bytes onto the TX FIFO
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Transfer up to a TX FIFO size's worth of characters from the Linux serial
+ * transmit buffer to the SiFive UART TX FIFO.
+ *
+ * Context: Any context. Expects @ssp->port.lock to be held by caller.
+ */
+static void __ssp_transmit_chars(struct sifive_serial_port *ssp)
+{
+ struct circ_buf *xmit = &ssp->port.state->xmit;
+ int count;
+
+ if (ssp->port.x_char) {
+ __ssp_transmit_char(ssp, ssp->port.x_char);
+ ssp->port.icount.tx++;
+ ssp->port.x_char = 0;
+ return;
+ }
+ if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) {
+ sifive_serial_stop_tx(&ssp->port);
+ return;
+ }
+ count = SIFIVE_TX_FIFO_DEPTH;
+ do {
+ __ssp_transmit_char(ssp, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ ssp->port.icount.tx++;
+ if (uart_circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&ssp->port);
+
+ if (uart_circ_empty(xmit))
+ sifive_serial_stop_tx(&ssp->port);
+}
+
+/**
+ * __ssp_enable_txwm() - enable transmit watermark interrupts
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Enable interrupt generation when the transmit FIFO watermark is reached
+ * on the SiFive UART referred to by @ssp.
+ */
+static void __ssp_enable_txwm(struct sifive_serial_port *ssp)
+{
+ if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)
+ return;
+
+ ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK;
+ __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
+}
+
+/**
+ * __ssp_enable_rxwm() - enable receive watermark interrupts
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Enable interrupt generation when the receive FIFO watermark is reached
+ * on the SiFive UART referred to by @ssp.
+ */
+static void __ssp_enable_rxwm(struct sifive_serial_port *ssp)
+{
+ if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)
+ return;
+
+ ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK;
+ __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
+}
+
+/**
+ * __ssp_disable_txwm() - disable transmit watermark interrupts
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Disable interrupt generation when the transmit FIFO watermark is reached
+ * on the UART referred to by @ssp.
+ */
+static void __ssp_disable_txwm(struct sifive_serial_port *ssp)
+{
+ if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK))
+ return;
+
+ ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK;
+ __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
+}
+
+/**
+ * __ssp_disable_rxwm() - disable receive watermark interrupts
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Disable interrupt generation when the receive FIFO watermark is reached
+ * on the UART referred to by @ssp.
+ */
+static void __ssp_disable_rxwm(struct sifive_serial_port *ssp)
+{
+ if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK))
+ return;
+
+ ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK;
+ __ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
+}
+
+/**
+ * __ssp_receive_char() - receive a byte from the UART
+ * @ssp: pointer to a struct sifive_serial_port
+ * @is_empty: char pointer to return whether the RX FIFO is empty
+ *
+ * Try to read a byte from the SiFive UART RX FIFO, referenced by
+ * @ssp, and to return it. Also returns the RX FIFO empty bit in
+ * the char pointed to by @ch. The caller must pass the byte back to the
+ * Linux serial layer if needed.
+ *
+ * Returns: the byte read from the UART RX FIFO.
+ */
+static char __ssp_receive_char(struct sifive_serial_port *ssp, char *is_empty)
+{
+ u32 v;
+ u8 ch;
+
+ v = __ssp_readl(ssp, SIFIVE_SERIAL_RXDATA_OFFS);
+
+ if (!is_empty)
+ WARN_ON(1);
+ else
+ *is_empty = (v & SIFIVE_SERIAL_RXDATA_EMPTY_MASK) >>
+ SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT;
+
+ ch = (v & SIFIVE_SERIAL_RXDATA_DATA_MASK) >>
+ SIFIVE_SERIAL_RXDATA_DATA_SHIFT;
+
+ return ch;
+}
+
+/**
+ * __ssp_receive_chars() - receive multiple bytes from the UART
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Receive up to an RX FIFO's worth of bytes from the SiFive UART referred
+ * to by @ssp and pass them up to the Linux serial layer.
+ *
+ * Context: Expects ssp->port.lock to be held by caller.
+ */
+static void __ssp_receive_chars(struct sifive_serial_port *ssp)
+{
+ unsigned char ch;
+ char is_empty;
+ int c;
+
+ for (c = SIFIVE_RX_FIFO_DEPTH; c > 0; --c) {
+ ch = __ssp_receive_char(ssp, &is_empty);
+ if (is_empty)
+ break;
+
+ ssp->port.icount.rx++;
+ uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL);
+ }
+
+ spin_unlock(&ssp->port.lock);
+ tty_flip_buffer_push(&ssp->port.state->port);
+ spin_lock(&ssp->port.lock);
+}
+
+/**
+ * __ssp_update_div() - calculate the divisor setting by the line rate
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Calculate the appropriate value of the clock divisor for the UART
+ * and target line rate referred to by @ssp and write it into the
+ * hardware.
+ */
+static void __ssp_update_div(struct sifive_serial_port *ssp)
+{
+ u16 div;
+
+ div = DIV_ROUND_UP(ssp->clkin_rate, ssp->baud_rate) - 1;
+
+ __ssp_writel(div, SIFIVE_SERIAL_DIV_OFFS, ssp);
+}
+
+/**
+ * __ssp_update_baud_rate() - set the UART "baud rate"
+ * @ssp: pointer to a struct sifive_serial_port
+ * @rate: new target bit rate
+ *
+ * Calculate the UART divisor value for the target bit rate @rate for the
+ * SiFive UART described by @ssp and program it into the UART. There may
+ * be some error between the target bit rate and the actual bit rate implemented
+ * by the UART due to clock ratio granularity.
+ */
+static void __ssp_update_baud_rate(struct sifive_serial_port *ssp,
+ unsigned int rate)
+{
+ if (ssp->baud_rate == rate)
+ return;
+
+ ssp->baud_rate = rate;
+ __ssp_update_div(ssp);
+}
+
+/**
+ * __ssp_set_stop_bits() - set the number of stop bits
+ * @ssp: pointer to a struct sifive_serial_port
+ * @nstop: 1 or 2 (stop bits)
+ *
+ * Program the SiFive UART referred to by @ssp to use @nstop stop bits.
+ */
+static void __ssp_set_stop_bits(struct sifive_serial_port *ssp, char nstop)
+{
+ u32 v;
+
+ if (nstop < 1 || nstop > 2) {
+ WARN_ON(1);
+ return;
+ }
+
+ v = __ssp_readl(ssp, SIFIVE_SERIAL_TXCTRL_OFFS);
+ v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK;
+ v |= (nstop - 1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT;
+ __ssp_writel(v, SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
+}
+
+/**
+ * __ssp_wait_for_xmitr() - wait for an empty slot on the TX FIFO
+ * @ssp: pointer to a struct sifive_serial_port
+ *
+ * Delay while the UART TX FIFO referred to by @ssp is marked as full.
+ *
+ * Context: Any context.
+ */
+static void __maybe_unused __ssp_wait_for_xmitr(struct sifive_serial_port *ssp)
+{
+ while (sifive_serial_is_txfifo_full(ssp))
+ udelay(1); /* XXX Could probably be more intelligent here */
+}
+
+/*
+ * Linux serial API functions
+ */
+
+static void sifive_serial_stop_tx(struct uart_port *port)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_disable_txwm(ssp);
+}
+
+static void sifive_serial_stop_rx(struct uart_port *port)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_disable_rxwm(ssp);
+}
+
+static void sifive_serial_start_tx(struct uart_port *port)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_enable_txwm(ssp);
+}
+
+static irqreturn_t sifive_serial_irq(int irq, void *dev_id)
+{
+ struct sifive_serial_port *ssp = dev_id;
+ u32 ip;
+
+ spin_lock(&ssp->port.lock);
+
+ ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS);
+ if (!ip) {
+ spin_unlock(&ssp->port.lock);
+ return IRQ_NONE;
+ }
+
+ if (ip & SIFIVE_SERIAL_IP_RXWM_MASK)
+ __ssp_receive_chars(ssp);
+ if (ip & SIFIVE_SERIAL_IP_TXWM_MASK)
+ __ssp_transmit_chars(ssp);
+
+ spin_unlock(&ssp->port.lock);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned int sifive_serial_tx_empty(struct uart_port *port)
+{
+ return TIOCSER_TEMT;
+}
+
+static unsigned int sifive_serial_get_mctrl(struct uart_port *port)
+{
+ return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* IP block does not support these signals */
+}
+
+static void sifive_serial_break_ctl(struct uart_port *port, int break_state)
+{
+ /* IP block does not support sending a break */
+}
+
+static int sifive_serial_startup(struct uart_port *port)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_enable_rxwm(ssp);
+
+ return 0;
+}
+
+static void sifive_serial_shutdown(struct uart_port *port)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_disable_rxwm(ssp);
+ __ssp_disable_txwm(ssp);
+}
+
+/**
+ * sifive_serial_clk_notifier() - clock post-rate-change notifier
+ * @nb: pointer to the struct notifier_block, from the notifier code
+ * @event: event mask from the notifier code
+ * @data: pointer to the struct clk_notifier_data from the notifier code
+ *
+ * On the V0 SoC, the UART IP block is derived from the CPU clock source
+ * after a synchronous divide-by-two divider, so any CPU clock rate change
+ * requires the UART baud rate to be updated. This presumably could corrupt any
+ * serial word currently being transmitted or received. It would probably
+ * be better to stop receives and transmits, then complete the baud rate
+ * change, then re-enable them.
+ */
+static int sifive_serial_clk_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+ struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb);
+
+ if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) {
+ ssp->clkin_rate = cnd->new_rate;
+ __ssp_update_div(ssp);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void sifive_serial_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+ unsigned long flags;
+ u32 v, old_v;
+ int rate;
+ char nstop;
+
+ if ((termios->c_cflag & CSIZE) != CS8)
+ dev_err_once(ssp->port.dev, "only 8-bit words supported\n");
+ if (termios->c_iflag & (INPCK | PARMRK))
+ dev_err_once(ssp->port.dev, "parity checking not supported\n");
+ if (termios->c_iflag & BRKINT)
+ dev_err_once(ssp->port.dev, "BREAK detection not supported\n");
+
+ /* Set number of stop bits */
+ nstop = (termios->c_cflag & CSTOPB) ? 2 : 1;
+ __ssp_set_stop_bits(ssp, nstop);
+
+ /* Set line rate */
+ rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16);
+ __ssp_update_baud_rate(ssp, rate);
+
+ spin_lock_irqsave(&ssp->port.lock, flags);
+
+ /* Update the per-port timeout */
+ uart_update_timeout(port, termios->c_cflag, rate);
+
+ ssp->port.read_status_mask = 0;
+
+ /* Ignore all characters if CREAD is not set */
+ v = __ssp_readl(ssp, SIFIVE_SERIAL_RXCTRL_OFFS);
+ old_v = v;
+ if ((termios->c_cflag & CREAD) == 0)
+ v &= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
+ else
+ v |= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
+ if (v != old_v)
+ __ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
+
+ spin_unlock_irqrestore(&ssp->port.lock, flags);
+}
+
+static void sifive_serial_release_port(struct uart_port *port)
+{
+}
+
+static int sifive_serial_request_port(struct uart_port *port)
+{
+ return 0;
+}
+
+static void sifive_serial_config_port(struct uart_port *port, int flags)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ ssp->port.type = PORT_SIFIVE_V0;
+}
+
+static int sifive_serial_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
+{
+ return -EINVAL;
+}
+
+static const char *sifive_serial_type(struct uart_port *port)
+{
+ return port->type == PORT_SIFIVE_V0 ? "SiFive UART v0" : NULL;
+}
+
+/*
+ * Early console support
+ */
+
+#ifdef CONFIG_SERIAL_EARLYCON
+static void early_sifive_serial_putc(struct uart_port *port, int c)
+{
+ while (__ssp_early_readl(port, SIFIVE_SERIAL_TXDATA_OFFS) &
+ SIFIVE_SERIAL_TXDATA_FULL_MASK)
+ cpu_relax();
+
+ __ssp_early_writel(c, SIFIVE_SERIAL_TXDATA_OFFS, port);
+}
+
+static void early_sifive_serial_write(struct console *con, const char *s,
+ unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+ struct uart_port *port = &dev->port;
+
+ uart_console_write(port, s, n, early_sifive_serial_putc);
+}
+
+static int __init early_sifive_serial_setup(struct earlycon_device *dev,
+ const char *options)
+{
+ struct uart_port *port = &dev->port;
+
+ if (!port->membase)
+ return -ENODEV;
+
+ dev->con->write = early_sifive_serial_write;
+
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup);
+OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0",
+ early_sifive_serial_setup);
+#endif /* CONFIG_SERIAL_EARLYCON */
+
+/*
+ * Linux console interface
+ */
+
+#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
+
+static struct sifive_serial_port *sifive_serial_console_ports[SIFIVE_SERIAL_MAX_PORTS];
+
+static void sifive_serial_console_putchar(struct uart_port *port, int ch)
+{
+ struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
+
+ __ssp_wait_for_xmitr(ssp);
+ __ssp_transmit_char(ssp, ch);
+}
+
+static void sifive_serial_console_write(struct console *co, const char *s,
+ unsigned int count)
+{
+ struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index];
+ unsigned long flags;
+ unsigned int ier;
+ int locked = 1;
+
+ if (!ssp)
+ return;
+
+ local_irq_save(flags);
+ if (ssp->port.sysrq)
+ locked = 0;
+ else if (oops_in_progress)
+ locked = spin_trylock(&ssp->port.lock);
+ else
+ spin_lock(&ssp->port.lock);
+
+ ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS);
+ __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp);
+
+ uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar);
+
+ __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp);
+
+ if (locked)
+ spin_unlock(&ssp->port.lock);
+ local_irq_restore(flags);
+}
+
+static int __init sifive_serial_console_setup(struct console *co, char *options)
+{
+ struct sifive_serial_port *ssp;
+ int baud = SIFIVE_DEFAULT_BAUD_RATE;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+
+ if (co->index < 0 || co->index >= SIFIVE_SERIAL_MAX_PORTS)
+ return -ENODEV;
+
+ ssp = sifive_serial_console_ports[co->index];
+ if (!ssp)
+ return -ENODEV;
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+ return uart_set_options(&ssp->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver sifive_serial_uart_driver;
+
+static struct console sifive_serial_console = {
+ .name = SIFIVE_TTY_PREFIX,
+ .write = sifive_serial_console_write,
+ .device = uart_console_device,
+ .setup = sifive_serial_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &sifive_serial_uart_driver,
+};
+
+static int __init sifive_console_init(void)
+{
+ register_console(&sifive_serial_console);
+ return 0;
+}
+
+console_initcall(sifive_console_init);
+
+static void __ssp_add_console_port(struct sifive_serial_port *ssp)
+{
+ sifive_serial_console_ports[ssp->port.line] = ssp;
+}
+
+static void __ssp_remove_console_port(struct sifive_serial_port *ssp)
+{
+ sifive_serial_console_ports[ssp->port.line] = 0;
+}
+
+#define SIFIVE_SERIAL_CONSOLE (&sifive_serial_console)
+
+#else
+
+#define SIFIVE_SERIAL_CONSOLE NULL
+
+static void __ssp_add_console_port(struct sifive_serial_port *ssp)
+{}
+static void __ssp_remove_console_port(struct sifive_serial_port *ssp)
+{}
+
+#endif
+
+static const struct uart_ops sifive_serial_uops = {
+ .tx_empty = sifive_serial_tx_empty,
+ .set_mctrl = sifive_serial_set_mctrl,
+ .get_mctrl = sifive_serial_get_mctrl,
+ .stop_tx = sifive_serial_stop_tx,
+ .start_tx = sifive_serial_start_tx,
+ .stop_rx = sifive_serial_stop_rx,
+ .break_ctl = sifive_serial_break_ctl,
+ .startup = sifive_serial_startup,
+ .shutdown = sifive_serial_shutdown,
+ .set_termios = sifive_serial_set_termios,
+ .type = sifive_serial_type,
+ .release_port = sifive_serial_release_port,
+ .request_port = sifive_serial_request_port,
+ .config_port = sifive_serial_config_port,
+ .verify_port = sifive_serial_verify_port,
+};
+
+static struct uart_driver sifive_serial_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = SIFIVE_SERIAL_NAME,
+ .dev_name = SIFIVE_TTY_PREFIX,
+ .nr = SIFIVE_SERIAL_MAX_PORTS,
+ .cons = SIFIVE_SERIAL_CONSOLE,
+};
+
+static int sifive_serial_probe(struct platform_device *pdev)
+{
+ struct sifive_serial_port *ssp;
+ struct resource *mem;
+ struct clk *clk;
+ void __iomem *base;
+ int irq, id, r;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -EPROBE_DEFER;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base)) {
+ dev_err(&pdev->dev, "could not acquire device memory\n");
+ return PTR_ERR(base);
+ }
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to find controller clock\n");
+ return PTR_ERR(clk);
+ }
+
+ id = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (id < 0) {
+ dev_err(&pdev->dev, "missing aliases entry\n");
+ return id;
+ }
+
+#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
+ if (id > SIFIVE_SERIAL_MAX_PORTS) {
+ dev_err(&pdev->dev, "too many UARTs (%d)\n", id);
+ return -EINVAL;
+ }
+#endif
+
+ ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL);
+ if (!ssp)
+ return -ENOMEM;
+
+ ssp->port.dev = &pdev->dev;
+ ssp->port.type = PORT_SIFIVE_V0;
+ ssp->port.iotype = UPIO_MEM;
+ ssp->port.irq = irq;
+ ssp->port.fifosize = SIFIVE_TX_FIFO_DEPTH;
+ ssp->port.ops = &sifive_serial_uops;
+ ssp->port.line = id;
+ ssp->port.mapbase = mem->start;
+ ssp->port.membase = base;
+ ssp->dev = &pdev->dev;
+ ssp->clk = clk;
+ ssp->clk_notifier.notifier_call = sifive_serial_clk_notifier;
+
+ r = clk_notifier_register(ssp->clk, &ssp->clk_notifier);
+ if (r) {
+ dev_err(&pdev->dev, "could not register clock notifier: %d\n",
+ r);
+ goto probe_out1;
+ }
+
+ /* Set up clock divider */
+ ssp->clkin_rate = clk_get_rate(ssp->clk);
+ ssp->baud_rate = SIFIVE_DEFAULT_BAUD_RATE;
+ __ssp_update_div(ssp);
+
+ platform_set_drvdata(pdev, ssp);
+
+ /* Enable transmits and set the watermark level to 1 */
+ __ssp_writel((1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) |
+ SIFIVE_SERIAL_TXCTRL_TXEN_MASK,
+ SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
+
+ /* Enable receives and set the watermark level to 0 */
+ __ssp_writel((0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) |
+ SIFIVE_SERIAL_RXCTRL_RXEN_MASK,
+ SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
+
+ r = request_irq(ssp->port.irq, sifive_serial_irq, ssp->port.irqflags,
+ dev_name(&pdev->dev), ssp);
+ if (r) {
+ dev_err(&pdev->dev, "could not attach interrupt: %d\n", r);
+ goto probe_out2;
+ }
+
+ __ssp_add_console_port(ssp);
+
+ r = uart_add_one_port(&sifive_serial_uart_driver, &ssp->port);
+ if (r != 0) {
+ dev_err(&pdev->dev, "could not add uart: %d\n", r);
+ goto probe_out3;
+ }
+
+ return 0;
+
+probe_out3:
+ __ssp_remove_console_port(ssp);
+ free_irq(ssp->port.irq, ssp);
+probe_out2:
+ clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
+probe_out1:
+ return r;
+}
+
+static int sifive_serial_remove(struct platform_device *dev)
+{
+ struct sifive_serial_port *ssp = platform_get_drvdata(dev);
+
+ __ssp_remove_console_port(ssp);
+ uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port);
+ free_irq(ssp->port.irq, ssp);
+ clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
+
+ return 0;
+}
+
+static const struct of_device_id sifive_serial_of_match[] = {
+ { .compatible = "sifive,fu540-c000-uart0" },
+ { .compatible = "sifive,uart0" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sifive_serial_of_match);
+
+static struct platform_driver sifive_serial_platform_driver = {
+ .probe = sifive_serial_probe,
+ .remove = sifive_serial_remove,
+ .driver = {
+ .name = SIFIVE_SERIAL_NAME,
+ .of_match_table = of_match_ptr(sifive_serial_of_match),
+ },
+};
+
+static int __init sifive_serial_init(void)
+{
+ int r;
+
+ r = uart_register_driver(&sifive_serial_uart_driver);
+ if (r)
+ goto init_out1;
+
+ r = platform_driver_register(&sifive_serial_platform_driver);
+ if (r)
+ goto init_out2;
+
+ return 0;
+
+init_out2:
+ uart_unregister_driver(&sifive_serial_uart_driver);
+init_out1:
+ return r;
+}
+
+static void __exit sifive_serial_exit(void)
+{
+ platform_driver_unregister(&sifive_serial_platform_driver);
+ uart_unregister_driver(&sifive_serial_uart_driver);
+}
+
+module_init(sifive_serial_init);
+module_exit(sifive_serial_exit);
+
+MODULE_DESCRIPTION("SiFive UART serial driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Paul Walmsley <paul@pwsan.com>");
diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c
deleted file mode 100644
index 42b9ade..0000000
--- a/drivers/tty/serial/sn_console.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * C-Brick Serial Port (and console) driver for SGI Altix machines.
- *
- * This driver is NOT suitable for talking to the l1-controller for
- * anything other than 'console activities' --- please use the l1
- * driver for that.
- *
- *
- * Copyright (c) 2004-2006 Silicon Graphics, Inc. All Rights Reserved.
- *
- * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane,
- * Mountain View, CA 94043, or:
- *
- * http://www.sgi.com
- *
- * For further information regarding this notice, see:
- *
- * http://oss.sgi.com/projects/GenInfo/NoticeExplan
- */
-
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/sysrq.h>
-#include <linux/circ_buf.h>
-#include <linux/serial_reg.h>
-#include <linux/delay.h> /* for mdelay */
-#include <linux/miscdevice.h>
-#include <linux/serial_core.h>
-
-#include <asm/io.h>
-#include <asm/sn/simulator.h>
-#include <asm/sn/sn_sal.h>
-
-/* number of characters we can transmit to the SAL console at a time */
-#define SN_SAL_MAX_CHARS 120
-
-/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to
- * avoid losing chars, (always has to be a power of 2) */
-#define SN_SAL_BUFFER_SIZE (64 * (1 << 10))
-
-#define SN_SAL_UART_FIFO_DEPTH 16
-#define SN_SAL_UART_FIFO_SPEED_CPS (9600/10)
-
-/* sn_transmit_chars() calling args */
-#define TRANSMIT_BUFFERED 0
-#define TRANSMIT_RAW 1
-
-/* To use dynamic numbers only and not use the assigned major and minor,
- * define the following.. */
- /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */
-#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */
-
-/* Device name we're using */
-#define DEVICE_NAME "ttySG"
-#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */
-/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */
-#define DEVICE_MAJOR 204
-#define DEVICE_MINOR 40
-
-#ifdef CONFIG_MAGIC_SYSRQ
-static char sysrq_serial_str[] = "\eSYS";
-static char *sysrq_serial_ptr = sysrq_serial_str;
-static unsigned long sysrq_requested;
-#endif /* CONFIG_MAGIC_SYSRQ */
-
-/*
- * Port definition - this kinda drives it all
- */
-struct sn_cons_port {
- struct timer_list sc_timer;
- struct uart_port sc_port;
- struct sn_sal_ops {
- int (*sal_puts_raw) (const char *s, int len);
- int (*sal_puts) (const char *s, int len);
- int (*sal_getc) (void);
- int (*sal_input_pending) (void);
- void (*sal_wakeup_transmit) (struct sn_cons_port *, int);
- } *sc_ops;
- unsigned long sc_interrupt_timeout;
- int sc_is_asynch;
-};
-
-static struct sn_cons_port sal_console_port;
-static int sn_process_input;
-
-/* Only used if USE_DYNAMIC_MINOR is set to 1 */
-static struct miscdevice misc; /* used with misc_register for dynamic */
-
-extern void early_sn_setup(void);
-
-#undef DEBUG
-#ifdef DEBUG
-static int sn_debug_printf(const char *fmt, ...);
-#define DPRINTF(x...) sn_debug_printf(x)
-#else
-#define DPRINTF(x...) do { } while (0)
-#endif
-
-/* Prototypes */
-static int snt_hw_puts_raw(const char *, int);
-static int snt_hw_puts_buffered(const char *, int);
-static int snt_poll_getc(void);
-static int snt_poll_input_pending(void);
-static int snt_intr_getc(void);
-static int snt_intr_input_pending(void);
-static void sn_transmit_chars(struct sn_cons_port *, int);
-
-/* A table for polling:
- */
-static struct sn_sal_ops poll_ops = {
- .sal_puts_raw = snt_hw_puts_raw,
- .sal_puts = snt_hw_puts_raw,
- .sal_getc = snt_poll_getc,
- .sal_input_pending = snt_poll_input_pending
-};
-
-/* A table for interrupts enabled */
-static struct sn_sal_ops intr_ops = {
- .sal_puts_raw = snt_hw_puts_raw,
- .sal_puts = snt_hw_puts_buffered,
- .sal_getc = snt_intr_getc,
- .sal_input_pending = snt_intr_input_pending,
- .sal_wakeup_transmit = sn_transmit_chars
-};
-
-/* the console does output in two distinctly different ways:
- * synchronous (raw) and asynchronous (buffered). initially, early_printk
- * does synchronous output. any data written goes directly to the SAL
- * to be output (incidentally, it is internally buffered by the SAL)
- * after interrupts and timers are initialized and available for use,
- * the console init code switches to asynchronous output. this is
- * also the earliest opportunity to begin polling for console input.
- * after console initialization, console output and tty (serial port)
- * output is buffered and sent to the SAL asynchronously (either by
- * timer callback or by UART interrupt) */
-
-/* routines for running the console in polling mode */
-
-/**
- * snt_poll_getc - Get a character from the console in polling mode
- *
- */
-static int snt_poll_getc(void)
-{
- int ch;
-
- ia64_sn_console_getc(&ch);
- return ch;
-}
-
-/**
- * snt_poll_input_pending - Check if any input is waiting - polling mode.
- *
- */
-static int snt_poll_input_pending(void)
-{
- int status, input;
-
- status = ia64_sn_console_check(&input);
- return !status && input;
-}
-
-/* routines for an interrupt driven console (normal) */
-
-/**
- * snt_intr_getc - Get a character from the console, interrupt mode
- *
- */
-static int snt_intr_getc(void)
-{
- return ia64_sn_console_readc();
-}
-
-/**
- * snt_intr_input_pending - Check if input is pending, interrupt mode
- *
- */
-static int snt_intr_input_pending(void)
-{
- return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV;
-}
-
-/* these functions are polled and interrupt */
-
-/**
- * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode
- * @s: String
- * @len: Length
- *
- */
-static int snt_hw_puts_raw(const char *s, int len)
-{
- /* this will call the PROM and not return until this is done */
- return ia64_sn_console_putb(s, len);
-}
-
-/**
- * snt_hw_puts_buffered - Send string to console, polled or interrupt mode
- * @s: String
- * @len: Length
- *
- */
-static int snt_hw_puts_buffered(const char *s, int len)
-{
- /* queue data to the PROM */
- return ia64_sn_console_xmit_chars((char *)s, len);
-}
-
-/* uart interface structs
- * These functions are associated with the uart_port that the serial core
- * infrastructure calls.
- *
- * Note: Due to how the console works, many routines are no-ops.
- */
-
-/**
- * snp_type - What type of console are we?
- * @port: Port to operate with (we ignore since we only have one port)
- *
- */
-static const char *snp_type(struct uart_port *port)
-{
- return ("SGI SN L1");
-}
-
-/**
- * snp_tx_empty - Is the transmitter empty? We pretend we're always empty
- * @port: Port to operate on (we ignore since we only have one port)
- *
- */
-static unsigned int snp_tx_empty(struct uart_port *port)
-{
- return 1;
-}
-
-/**
- * snp_stop_tx - stop the transmitter - no-op for us
- * @port: Port to operat eon - we ignore - no-op function
- *
- */
-static void snp_stop_tx(struct uart_port *port)
-{
-}
-
-/**
- * snp_release_port - Free i/o and resources for port - no-op for us
- * @port: Port to operate on - we ignore - no-op function
- *
- */
-static void snp_release_port(struct uart_port *port)
-{
-}
-
-/**
- * snp_shutdown - shut down the port - free irq and disable - no-op for us
- * @port: Port to shut down - we ignore
- *
- */
-static void snp_shutdown(struct uart_port *port)
-{
-}
-
-/**
- * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console
- * @port: Port to operate on - we ignore
- * @mctrl: Lines to set/unset - we ignore
- *
- */
-static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-}
-
-/**
- * snp_get_mctrl - get contorl line info, we just return a static value
- * @port: port to operate on - we only have one port so we ignore this
- *
- */
-static unsigned int snp_get_mctrl(struct uart_port *port)
-{
- return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
-}
-
-/**
- * snp_stop_rx - Stop the receiver - we ignor ethis
- * @port: Port to operate on - we ignore
- *
- */
-static void snp_stop_rx(struct uart_port *port)
-{
-}
-
-/**
- * snp_start_tx - Start transmitter
- * @port: Port to operate on
- *
- */
-static void snp_start_tx(struct uart_port *port)
-{
- if (sal_console_port.sc_ops->sal_wakeup_transmit)
- sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port,
- TRANSMIT_BUFFERED);
-
-}
-
-/**
- * snp_break_ctl - handle breaks - ignored by us
- * @port: Port to operate on
- * @break_state: Break state
- *
- */
-static void snp_break_ctl(struct uart_port *port, int break_state)
-{
-}
-
-/**
- * snp_startup - Start up the serial port - always return 0 (We're always on)
- * @port: Port to operate on
- *
- */
-static int snp_startup(struct uart_port *port)
-{
- return 0;
-}
-
-/**
- * snp_set_termios - set termios stuff - we ignore these
- * @port: port to operate on
- * @termios: New settings
- * @termios: Old
- *
- */
-static void
-snp_set_termios(struct uart_port *port, struct ktermios *termios,
- struct ktermios *old)
-{
-}
-
-/**
- * snp_request_port - allocate resources for port - ignored by us
- * @port: port to operate on
- *
- */
-static int snp_request_port(struct uart_port *port)
-{
- return 0;
-}
-
-/**
- * snp_config_port - allocate resources, set up - we ignore, we're always on
- * @port: Port to operate on
- * @flags: flags used for port setup
- *
- */
-static void snp_config_port(struct uart_port *port, int flags)
-{
-}
-
-/* Associate the uart functions above - given to serial core */
-
-static const struct uart_ops sn_console_ops = {
- .tx_empty = snp_tx_empty,
- .set_mctrl = snp_set_mctrl,
- .get_mctrl = snp_get_mctrl,
- .stop_tx = snp_stop_tx,
- .start_tx = snp_start_tx,
- .stop_rx = snp_stop_rx,
- .break_ctl = snp_break_ctl,
- .startup = snp_startup,
- .shutdown = snp_shutdown,
- .set_termios = snp_set_termios,
- .pm = NULL,
- .type = snp_type,
- .release_port = snp_release_port,
- .request_port = snp_request_port,
- .config_port = snp_config_port,
- .verify_port = NULL,
-};
-
-/* End of uart struct functions and defines */
-
-#ifdef DEBUG
-
-/**
- * sn_debug_printf - close to hardware debugging printf
- * @fmt: printf format
- *
- * This is as "close to the metal" as we can get, used when the driver
- * itself may be broken.
- *
- */
-static int sn_debug_printf(const char *fmt, ...)
-{
- static char printk_buf[1024];
- int printed_len;
- va_list args;
-
- va_start(args, fmt);
- printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
-
- if (!sal_console_port.sc_ops) {
- sal_console_port.sc_ops = &poll_ops;
- early_sn_setup();
- }
- sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len);
-
- va_end(args);
- return printed_len;
-}
-#endif /* DEBUG */
-
-/*
- * Interrupt handling routines.
- */
-
-/**
- * sn_receive_chars - Grab characters, pass them to tty layer
- * @port: Port to operate on
- * @flags: irq flags
- *
- * Note: If we're not registered with the serial core infrastructure yet,
- * we don't try to send characters to it...
- *
- */
-static void
-sn_receive_chars(struct sn_cons_port *port, unsigned long flags)
-{
- struct tty_port *tport = NULL;
- int ch;
-
- if (!port) {
- printk(KERN_ERR "sn_receive_chars - port NULL so can't receive\n");
- return;
- }
-
- if (!port->sc_ops) {
- printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receive\n");
- return;
- }
-
- if (port->sc_port.state) {
- /* The serial_core stuffs are initialized, use them */
- tport = &port->sc_port.state->port;
- }
-
- while (port->sc_ops->sal_input_pending()) {
- ch = port->sc_ops->sal_getc();
- if (ch < 0) {
- printk(KERN_ERR "sn_console: An error occurred while "
- "obtaining data from the console (0x%0x)\n", ch);
- break;
- }
-#ifdef CONFIG_MAGIC_SYSRQ
- if (sysrq_requested) {
- unsigned long sysrq_timeout = sysrq_requested + HZ*5;
-
- sysrq_requested = 0;
- if (ch && time_before(jiffies, sysrq_timeout)) {
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
- handle_sysrq(ch);
- spin_lock_irqsave(&port->sc_port.lock, flags);
- /* ignore actual sysrq command char */
- continue;
- }
- }
- if (ch == *sysrq_serial_ptr) {
- if (!(*++sysrq_serial_ptr)) {
- sysrq_requested = jiffies;
- sysrq_serial_ptr = sysrq_serial_str;
- }
- /*
- * ignore the whole sysrq string except for the
- * leading escape
- */
- if (ch != '\e')
- continue;
- }
- else
- sysrq_serial_ptr = sysrq_serial_str;
-#endif /* CONFIG_MAGIC_SYSRQ */
-
- /* record the character to pass up to the tty layer */
- if (tport) {
- if (tty_insert_flip_char(tport, ch, TTY_NORMAL) == 0)
- break;
- }
- port->sc_port.icount.rx++;
- }
-
- if (tport)
- tty_flip_buffer_push(tport);
-}
-
-/**
- * sn_transmit_chars - grab characters from serial core, send off
- * @port: Port to operate on
- * @raw: Transmit raw or buffered
- *
- * Note: If we're early, before we're registered with serial core, the
- * writes are going through sn_sal_console_write because that's how
- * register_console has been set up. We currently could have asynch
- * polls calling this function due to sn_sal_switch_to_asynch but we can
- * ignore them until we register with the serial core stuffs.
- *
- */
-static void sn_transmit_chars(struct sn_cons_port *port, int raw)
-{
- int xmit_count, tail, head, loops, ii;
- int result;
- char *start;
- struct circ_buf *xmit;
-
- if (!port)
- return;
-
- BUG_ON(!port->sc_is_asynch);
-
- if (port->sc_port.state) {
- /* We're initialized, using serial core infrastructure */
- xmit = &port->sc_port.state->xmit;
- } else {
- /* Probably sn_sal_switch_to_asynch has been run but serial core isn't
- * initialized yet. Just return. Writes are going through
- * sn_sal_console_write (due to register_console) at this time.
- */
- return;
- }
-
- if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) {
- /* Nothing to do. */
- ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT);
- return;
- }
-
- head = xmit->head;
- tail = xmit->tail;
- start = &xmit->buf[tail];
-
- /* twice around gets the tail to the end of the buffer and
- * then to the head, if needed */
- loops = (head < tail) ? 2 : 1;
-
- for (ii = 0; ii < loops; ii++) {
- xmit_count = (head < tail) ?
- (UART_XMIT_SIZE - tail) : (head - tail);
-
- if (xmit_count > 0) {
- if (raw == TRANSMIT_RAW)
- result =
- port->sc_ops->sal_puts_raw(start,
- xmit_count);
- else
- result =
- port->sc_ops->sal_puts(start, xmit_count);
-#ifdef DEBUG
- if (!result)
- DPRINTF("`");
-#endif
- if (result > 0) {
- xmit_count -= result;
- port->sc_port.icount.tx += result;
- tail += result;
- tail &= UART_XMIT_SIZE - 1;
- xmit->tail = tail;
- start = &xmit->buf[tail];
- }
- }
- }
-
- if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
- uart_write_wakeup(&port->sc_port);
-
- if (uart_circ_empty(xmit))
- snp_stop_tx(&port->sc_port); /* no-op for us */
-}
-
-/**
- * sn_sal_interrupt - Handle console interrupts
- * @irq: irq #, useful for debug statements
- * @dev_id: our pointer to our port (sn_cons_port which contains the uart port)
- *
- */
-static irqreturn_t sn_sal_interrupt(int irq, void *dev_id)
-{
- struct sn_cons_port *port = (struct sn_cons_port *)dev_id;
- unsigned long flags;
- int status = ia64_sn_console_intr_status();
-
- if (!port)
- return IRQ_NONE;
-
- spin_lock_irqsave(&port->sc_port.lock, flags);
- if (status & SAL_CONSOLE_INTR_RECV) {
- sn_receive_chars(port, flags);
- }
- if (status & SAL_CONSOLE_INTR_XMIT) {
- sn_transmit_chars(port, TRANSMIT_BUFFERED);
- }
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
- return IRQ_HANDLED;
-}
-
-/**
- * sn_sal_timer_poll - this function handles polled console mode
- * @data: A pointer to our sn_cons_port (which contains the uart port)
- *
- * data is the pointer that init_timer will store for us. This function is
- * associated with init_timer to see if there is any console traffic.
- * Obviously not used in interrupt mode
- *
- */
-static void sn_sal_timer_poll(struct timer_list *t)
-{
- struct sn_cons_port *port = from_timer(port, t, sc_timer);
- unsigned long flags;
-
- if (!port)
- return;
-
- if (!port->sc_port.irq) {
- spin_lock_irqsave(&port->sc_port.lock, flags);
- if (sn_process_input)
- sn_receive_chars(port, flags);
- sn_transmit_chars(port, TRANSMIT_RAW);
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
- mod_timer(&port->sc_timer,
- jiffies + port->sc_interrupt_timeout);
- }
-}
-
-/*
- * Boot-time initialization code
- */
-
-/**
- * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch)
- * @port: Our sn_cons_port (which contains the uart port)
- *
- * So this is used by sn_sal_serial_console_init (early on, before we're
- * registered with serial core). It's also used by sn_sal_init
- * right after we've registered with serial core. The later only happens
- * if we didn't already come through here via sn_sal_serial_console_init.
- *
- */
-static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port)
-{
- unsigned long flags;
-
- if (!port)
- return;
-
- DPRINTF("sn_console: about to switch to asynchronous console\n");
-
- /* without early_printk, we may be invoked late enough to race
- * with other cpus doing console IO at this point, however
- * console interrupts will never be enabled */
- spin_lock_irqsave(&port->sc_port.lock, flags);
-
- /* early_printk invocation may have done this for us */
- if (!port->sc_ops)
- port->sc_ops = &poll_ops;
-
- /* we can't turn on the console interrupt (as request_irq
- * calls kmalloc, which isn't set up yet), so we rely on a
- * timer to poll for input and push data from the console
- * buffer.
- */
- timer_setup(&port->sc_timer, sn_sal_timer_poll, 0);
-
- if (IS_RUNNING_ON_SIMULATOR())
- port->sc_interrupt_timeout = 6;
- else {
- /* 960cps / 16 char FIFO = 60HZ
- * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */
- port->sc_interrupt_timeout =
- HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS;
- }
- mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout);
-
- port->sc_is_asynch = 1;
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
-}
-
-/**
- * sn_sal_switch_to_interrupts - Switch to interrupt driven mode
- * @port: Our sn_cons_port (which contains the uart port)
- *
- * In sn_sal_init, after we're registered with serial core and
- * the port is added, this function is called to switch us to interrupt
- * mode. We were previously in asynch/polling mode (using init_timer).
- *
- * We attempt to switch to interrupt mode here by calling
- * request_irq. If that works out, we enable receive interrupts.
- */
-static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port)
-{
- unsigned long flags;
-
- if (port) {
- DPRINTF("sn_console: switching to interrupt driven console\n");
-
- if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt,
- IRQF_SHARED,
- "SAL console driver", port) >= 0) {
- spin_lock_irqsave(&port->sc_port.lock, flags);
- port->sc_port.irq = SGI_UART_VECTOR;
- port->sc_ops = &intr_ops;
- irq_set_handler(port->sc_port.irq, handle_level_irq);
-
- /* turn on receive interrupts */
- ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV);
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
- }
- else {
- printk(KERN_INFO
- "sn_console: console proceeding in polled mode\n");
- }
- }
-}
-
-/*
- * Kernel console definitions
- */
-
-static void sn_sal_console_write(struct console *, const char *, unsigned);
-static int sn_sal_console_setup(struct console *, char *);
-static struct uart_driver sal_console_uart;
-extern struct tty_driver *uart_console_device(struct console *, int *);
-
-static struct console sal_console = {
- .name = DEVICE_NAME,
- .write = sn_sal_console_write,
- .device = uart_console_device,
- .setup = sn_sal_console_setup,
- .index = -1, /* unspecified */
- .data = &sal_console_uart,
-};
-
-#define SAL_CONSOLE &sal_console
-
-static struct uart_driver sal_console_uart = {
- .owner = THIS_MODULE,
- .driver_name = "sn_console",
- .dev_name = DEVICE_NAME,
- .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */
- .minor = 0,
- .nr = 1, /* one port */
- .cons = SAL_CONSOLE,
-};
-
-/**
- * sn_sal_init - When the kernel loads us, get us rolling w/ serial core
- *
- * Before this is called, we've been printing kernel messages in a special
- * early mode not making use of the serial core infrastructure. When our
- * driver is loaded for real, we register the driver and port with serial
- * core and try to enable interrupt driven mode.
- *
- */
-static int __init sn_sal_init(void)
-{
- int retval;
-
- if (!ia64_platform_is("sn2"))
- return 0;
-
- printk(KERN_INFO "sn_console: Console driver init\n");
-
- if (USE_DYNAMIC_MINOR == 1) {
- misc.minor = MISC_DYNAMIC_MINOR;
- misc.name = DEVICE_NAME_DYNAMIC;
- retval = misc_register(&misc);
- if (retval != 0) {
- printk(KERN_WARNING "Failed to register console "
- "device using misc_register.\n");
- return -ENODEV;
- }
- sal_console_uart.major = MISC_MAJOR;
- sal_console_uart.minor = misc.minor;
- } else {
- sal_console_uart.major = DEVICE_MAJOR;
- sal_console_uart.minor = DEVICE_MINOR;
- }
-
- /* We register the driver and the port before switching to interrupts
- * or async above so the proper uart structures are populated */
-
- if (uart_register_driver(&sal_console_uart) < 0) {
- printk
- ("ERROR sn_sal_init failed uart_register_driver, line %d\n",
- __LINE__);
- return -ENODEV;
- }
-
- spin_lock_init(&sal_console_port.sc_port.lock);
-
- /* Setup the port struct with the minimum needed */
- sal_console_port.sc_port.membase = (char *)1; /* just needs to be non-zero */
- sal_console_port.sc_port.type = PORT_16550A;
- sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS;
- sal_console_port.sc_port.ops = &sn_console_ops;
- sal_console_port.sc_port.line = 0;
-
- if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) {
- /* error - not sure what I'd do - so I'll do nothing */
- printk(KERN_ERR "%s: unable to add port\n", __func__);
- }
-
- /* when this driver is compiled in, the console initialization
- * will have already switched us into asynchronous operation
- * before we get here through the initcalls */
- if (!sal_console_port.sc_is_asynch) {
- sn_sal_switch_to_asynch(&sal_console_port);
- }
-
- /* at this point (device_init) we can try to turn on interrupts */
- if (!IS_RUNNING_ON_SIMULATOR()) {
- sn_sal_switch_to_interrupts(&sal_console_port);
- }
- sn_process_input = 1;
- return 0;
-}
-device_initcall(sn_sal_init);
-
-/**
- * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required
- * @puts_raw : puts function to do the writing
- * @s: input string
- * @count: length
- *
- * We need a \r ahead of every \n for direct writes through
- * ia64_sn_console_putb (what sal_puts_raw below actually does).
- *
- */
-
-static void puts_raw_fixed(int (*puts_raw) (const char *s, int len),
- const char *s, int count)
-{
- const char *s1;
-
- /* Output '\r' before each '\n' */
- while ((s1 = memchr(s, '\n', count)) != NULL) {
- puts_raw(s, s1 - s);
- puts_raw("\r\n", 2);
- count -= s1 + 1 - s;
- s = s1 + 1;
- }
- puts_raw(s, count);
-}
-
-/**
- * sn_sal_console_write - Print statements before serial core available
- * @console: Console to operate on - we ignore since we have just one
- * @s: String to send
- * @count: length
- *
- * This is referenced in the console struct. It is used for early
- * console printing before we register with serial core and for things
- * such as kdb. The console_lock must be held when we get here.
- *
- * This function has some code for trying to print output even if the lock
- * is held. We try to cover the case where a lock holder could have died.
- * We don't use this special case code if we're not registered with serial
- * core yet. After we're registered with serial core, the only time this
- * function would be used is for high level kernel output like magic sys req,
- * kdb, and printk's.
- */
-static void
-sn_sal_console_write(struct console *co, const char *s, unsigned count)
-{
- unsigned long flags = 0;
- struct sn_cons_port *port = &sal_console_port;
- static int stole_lock = 0;
-
- BUG_ON(!port->sc_is_asynch);
-
- /* We can't look at the xmit buffer if we're not registered with serial core
- * yet. So only do the fancy recovery after registering
- */
- if (!port->sc_port.state) {
- /* Not yet registered with serial core - simple case */
- puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
- return;
- }
-
- /* somebody really wants this output, might be an
- * oops, kdb, panic, etc. make sure they get it. */
- if (spin_is_locked(&port->sc_port.lock)) {
- int lhead = port->sc_port.state->xmit.head;
- int ltail = port->sc_port.state->xmit.tail;
- int counter, got_lock = 0;
-
- /*
- * We attempt to determine if someone has died with the
- * lock. We wait ~20 secs after the head and tail ptrs
- * stop moving and assume the lock holder is not functional
- * and plow ahead. If the lock is freed within the time out
- * period we re-get the lock and go ahead normally. We also
- * remember if we have plowed ahead so that we don't have
- * to wait out the time out period again - the asumption
- * is that we will time out again.
- */
-
- for (counter = 0; counter < 150; mdelay(125), counter++) {
- if (!spin_is_locked(&port->sc_port.lock)
- || stole_lock) {
- if (!stole_lock) {
- spin_lock_irqsave(&port->sc_port.lock,
- flags);
- got_lock = 1;
- }
- break;
- } else {
- /* still locked */
- if ((lhead != port->sc_port.state->xmit.head)
- || (ltail !=
- port->sc_port.state->xmit.tail)) {
- lhead =
- port->sc_port.state->xmit.head;
- ltail =
- port->sc_port.state->xmit.tail;
- counter = 0;
- }
- }
- }
- /* flush anything in the serial core xmit buffer, raw */
- sn_transmit_chars(port, 1);
- if (got_lock) {
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
- stole_lock = 0;
- } else {
- /* fell thru */
- stole_lock = 1;
- }
- puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
- } else {
- stole_lock = 0;
- spin_lock_irqsave(&port->sc_port.lock, flags);
- sn_transmit_chars(port, 1);
- spin_unlock_irqrestore(&port->sc_port.lock, flags);
-
- puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
- }
-}
-
-
-/**
- * sn_sal_console_setup - Set up console for early printing
- * @co: Console to work with
- * @options: Options to set
- *
- * Altix console doesn't do anything with baud rates, etc, anyway.
- *
- * This isn't required since not providing the setup function in the
- * console struct is ok. However, other patches like KDB plop something
- * here so providing it is easier.
- *
- */
-static int sn_sal_console_setup(struct console *co, char *options)
-{
- return 0;
-}
-
-/**
- * sn_sal_console_write_early - simple early output routine
- * @co - console struct
- * @s - string to print
- * @count - count
- *
- * Simple function to provide early output, before even
- * sn_sal_serial_console_init is called. Referenced in the
- * console struct registerd in sn_serial_console_early_setup.
- *
- */
-static void __init
-sn_sal_console_write_early(struct console *co, const char *s, unsigned count)
-{
- puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count);
-}
-
-/* Used for very early console printing - again, before
- * sn_sal_serial_console_init is run */
-static struct console sal_console_early __initdata = {
- .name = "sn_sal",
- .write = sn_sal_console_write_early,
- .flags = CON_PRINTBUFFER,
- .index = -1,
-};
-
-/**
- * sn_serial_console_early_setup - Sets up early console output support
- *
- * Register a console early on... This is for output before even
- * sn_sal_serial_cosnole_init is called. This function is called from
- * setup.c. This allows us to do really early polled writes. When
- * sn_sal_serial_console_init is called, this console is unregistered
- * and a new one registered.
- */
-int __init sn_serial_console_early_setup(void)
-{
- if (!ia64_platform_is("sn2"))
- return -1;
-
- sal_console_port.sc_ops = &poll_ops;
- spin_lock_init(&sal_console_port.sc_port.lock);
- early_sn_setup(); /* Find SAL entry points */
- register_console(&sal_console_early);
-
- return 0;
-}
-
-/**
- * sn_sal_serial_console_init - Early console output - set up for register
- *
- * This function is called when regular console init happens. Because we
- * support even earlier console output with sn_serial_console_early_setup
- * (called from setup.c directly), this function unregisters the really
- * early console.
- *
- * Note: Even if setup.c doesn't register sal_console_early, unregistering
- * it here doesn't hurt anything.
- *
- */
-static int __init sn_sal_serial_console_init(void)
-{
- if (ia64_platform_is("sn2")) {
- sn_sal_switch_to_asynch(&sal_console_port);
- DPRINTF("sn_sal_serial_console_init : register console\n");
- register_console(&sal_console);
- unregister_console(&sal_console_early);
- }
- return 0;
-}
-
-console_initcall(sn_sal_serial_console_init);
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 828f114..771d111 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -10,6 +10,9 @@
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/sprd-dma.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
@@ -45,6 +48,8 @@
/* data number in TX and RX fifo */
#define SPRD_STS1 0x000C
+#define SPRD_RX_FIFO_CNT_MASK GENMASK(7, 0)
+#define SPRD_TX_FIFO_CNT_MASK GENMASK(15, 8)
/* interrupt enable register and its BITs */
#define SPRD_IEN 0x0010
@@ -66,67 +71,88 @@
#define SPRD_LCR_DATA_LEN6 0x4
#define SPRD_LCR_DATA_LEN7 0x8
#define SPRD_LCR_DATA_LEN8 0xc
-#define SPRD_LCR_PARITY (BIT(0) | BIT(1))
+#define SPRD_LCR_PARITY (BIT(0) | BIT(1))
#define SPRD_LCR_PARITY_EN 0x2
#define SPRD_LCR_EVEN_PAR 0x0
#define SPRD_LCR_ODD_PAR 0x1
/* control register 1 */
-#define SPRD_CTL1 0x001C
+#define SPRD_CTL1 0x001C
+#define SPRD_DMA_EN BIT(15)
+#define SPRD_LOOPBACK_EN BIT(14)
#define RX_HW_FLOW_CTL_THLD BIT(6)
#define RX_HW_FLOW_CTL_EN BIT(7)
#define TX_HW_FLOW_CTL_EN BIT(8)
#define RX_TOUT_THLD_DEF 0x3E00
-#define RX_HFC_THLD_DEF 0x40
+#define RX_HFC_THLD_DEF 0x40
/* fifo threshold register */
#define SPRD_CTL2 0x0020
-#define THLD_TX_EMPTY 0x40
-#define THLD_RX_FULL 0x40
+#define THLD_TX_EMPTY 0x40
+#define THLD_TX_EMPTY_SHIFT 8
+#define THLD_RX_FULL 0x40
+#define THLD_RX_FULL_MASK GENMASK(6, 0)
/* config baud rate register */
#define SPRD_CLKD0 0x0024
+#define SPRD_CLKD0_MASK GENMASK(15, 0)
#define SPRD_CLKD1 0x0028
+#define SPRD_CLKD1_MASK GENMASK(20, 16)
+#define SPRD_CLKD1_SHIFT 16
/* interrupt mask status register */
-#define SPRD_IMSR 0x002C
-#define SPRD_IMSR_RX_FIFO_FULL BIT(0)
+#define SPRD_IMSR 0x002C
+#define SPRD_IMSR_RX_FIFO_FULL BIT(0)
#define SPRD_IMSR_TX_FIFO_EMPTY BIT(1)
-#define SPRD_IMSR_BREAK_DETECT BIT(7)
-#define SPRD_IMSR_TIMEOUT BIT(13)
+#define SPRD_IMSR_BREAK_DETECT BIT(7)
+#define SPRD_IMSR_TIMEOUT BIT(13)
+#define SPRD_DEFAULT_SOURCE_CLK 26000000
-struct reg_backup {
- u32 ien;
- u32 ctrl0;
- u32 ctrl1;
- u32 ctrl2;
- u32 clkd0;
- u32 clkd1;
- u32 dspwait;
+#define SPRD_RX_DMA_STEP 1
+#define SPRD_RX_FIFO_FULL 1
+#define SPRD_TX_FIFO_FULL 0x20
+#define SPRD_UART_RX_SIZE (UART_XMIT_SIZE / 4)
+
+struct sprd_uart_dma {
+ struct dma_chan *chn;
+ unsigned char *virt;
+ dma_addr_t phys_addr;
+ dma_cookie_t cookie;
+ u32 trans_len;
+ bool enable;
};
struct sprd_uart_port {
struct uart_port port;
- struct reg_backup reg_bak;
char name[16];
+ struct clk *clk;
+ struct sprd_uart_dma tx_dma;
+ struct sprd_uart_dma rx_dma;
+ dma_addr_t pos;
+ unsigned char *rx_buf_tail;
};
static struct sprd_uart_port *sprd_port[UART_NR_MAX];
static int sprd_ports_num;
-static inline unsigned int serial_in(struct uart_port *port, int offset)
+static int sprd_start_dma_rx(struct uart_port *port);
+static int sprd_tx_dma_config(struct uart_port *port);
+
+static inline unsigned int serial_in(struct uart_port *port,
+ unsigned int offset)
{
return readl_relaxed(port->membase + offset);
}
-static inline void serial_out(struct uart_port *port, int offset, int value)
+static inline void serial_out(struct uart_port *port, unsigned int offset,
+ int value)
{
writel_relaxed(value, port->membase + offset);
}
static unsigned int sprd_tx_empty(struct uart_port *port)
{
- if (serial_in(port, SPRD_STS1) & 0xff00)
+ if (serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
return 0;
else
return TIOCSER_TEMT;
@@ -139,38 +165,25 @@
static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
- /* nothing to do */
-}
+ u32 val = serial_in(port, SPRD_CTL1);
-static void sprd_stop_tx(struct uart_port *port)
-{
- unsigned int ien, iclr;
+ if (mctrl & TIOCM_LOOP)
+ val |= SPRD_LOOPBACK_EN;
+ else
+ val &= ~SPRD_LOOPBACK_EN;
- iclr = serial_in(port, SPRD_ICLR);
- ien = serial_in(port, SPRD_IEN);
-
- iclr |= SPRD_IEN_TX_EMPTY;
- ien &= ~SPRD_IEN_TX_EMPTY;
-
- serial_out(port, SPRD_ICLR, iclr);
- serial_out(port, SPRD_IEN, ien);
-}
-
-static void sprd_start_tx(struct uart_port *port)
-{
- unsigned int ien;
-
- ien = serial_in(port, SPRD_IEN);
- if (!(ien & SPRD_IEN_TX_EMPTY)) {
- ien |= SPRD_IEN_TX_EMPTY;
- serial_out(port, SPRD_IEN, ien);
- }
+ serial_out(port, SPRD_CTL1, val);
}
static void sprd_stop_rx(struct uart_port *port)
{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
unsigned int ien, iclr;
+ if (sp->rx_dma.enable)
+ dmaengine_terminate_all(sp->rx_dma.chn);
+
iclr = serial_in(port, SPRD_ICLR);
ien = serial_in(port, SPRD_IEN);
@@ -181,6 +194,370 @@
serial_out(port, SPRD_ICLR, iclr);
}
+static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
+{
+ u32 val = serial_in(port, SPRD_CTL1);
+
+ if (enable)
+ val |= SPRD_DMA_EN;
+ else
+ val &= ~SPRD_DMA_EN;
+
+ serial_out(port, SPRD_CTL1, val);
+}
+
+static void sprd_stop_tx_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+ struct dma_tx_state state;
+ u32 trans_len;
+
+ dmaengine_pause(sp->tx_dma.chn);
+
+ dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
+ if (state.residue) {
+ trans_len = state.residue - sp->tx_dma.phys_addr;
+ xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += trans_len;
+ dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+ sp->tx_dma.trans_len, DMA_TO_DEVICE);
+ }
+
+ dmaengine_terminate_all(sp->tx_dma.chn);
+ sp->tx_dma.trans_len = 0;
+}
+
+static int sprd_tx_buf_remap(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+
+ sp->tx_dma.trans_len =
+ CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ sp->tx_dma.phys_addr = dma_map_single(port->dev,
+ (void *)&(xmit->buf[xmit->tail]),
+ sp->tx_dma.trans_len,
+ DMA_TO_DEVICE);
+ return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
+}
+
+static void sprd_complete_tx_dma(void *data)
+{
+ struct uart_port *port = (struct uart_port *)data;
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+ dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+ sp->tx_dma.trans_len, DMA_TO_DEVICE);
+
+ xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
+ port->icount.tx += sp->tx_dma.trans_len;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
+ sprd_tx_dma_config(port))
+ sp->tx_dma.trans_len = 0;
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_uart_dma_submit(struct uart_port *port,
+ struct sprd_uart_dma *ud, u32 trans_len,
+ enum dma_transfer_direction direction,
+ dma_async_tx_callback callback)
+{
+ struct dma_async_tx_descriptor *dma_des;
+ unsigned long flags;
+
+ flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
+ SPRD_DMA_NO_TRG,
+ SPRD_DMA_FRAG_REQ,
+ SPRD_DMA_TRANS_INT);
+
+ dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
+ direction, flags);
+ if (!dma_des)
+ return -ENODEV;
+
+ dma_des->callback = callback;
+ dma_des->callback_param = port;
+
+ ud->cookie = dmaengine_submit(dma_des);
+ if (dma_submit_error(ud->cookie))
+ return dma_submit_error(ud->cookie);
+
+ dma_async_issue_pending(ud->chn);
+
+ return 0;
+}
+
+static int sprd_tx_dma_config(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
+ SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
+ int ret;
+ struct dma_slave_config cfg = {
+ .dst_addr = port->mapbase + SPRD_TXD,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .src_maxburst = burst,
+ };
+
+ ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
+ if (ret < 0)
+ return ret;
+
+ return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
+ DMA_MEM_TO_DEV, sprd_complete_tx_dma);
+}
+
+static void sprd_start_tx_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct circ_buf *xmit = &port->state->xmit;
+
+ if (port->x_char) {
+ serial_out(port, SPRD_TXD, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ sprd_stop_tx_dma(port);
+ return;
+ }
+
+ if (sp->tx_dma.trans_len)
+ return;
+
+ if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
+ sp->tx_dma.trans_len = 0;
+}
+
+static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
+{
+ u32 val = serial_in(port, SPRD_CTL2);
+
+ val &= ~THLD_RX_FULL_MASK;
+ val |= thld & THLD_RX_FULL_MASK;
+ serial_out(port, SPRD_CTL2, val);
+}
+
+static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
+{
+ sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+ &sp->rx_dma.phys_addr, GFP_KERNEL);
+ if (!sp->rx_dma.virt)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void sprd_rx_free_buf(struct sprd_uart_port *sp)
+{
+ if (sp->rx_dma.virt)
+ dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+ sp->rx_dma.virt, sp->rx_dma.phys_addr);
+
+}
+
+static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_slave_config cfg = {
+ .src_addr = port->mapbase + SPRD_RXD,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .src_maxburst = burst,
+ };
+
+ return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
+}
+
+static void sprd_uart_dma_rx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct tty_port *tty = &port->state->port;
+
+ port->icount.rx += sp->rx_dma.trans_len;
+ tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
+ tty_flip_buffer_push(tty);
+}
+
+static void sprd_uart_dma_irq(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(sp->rx_dma.chn,
+ sp->rx_dma.cookie, &state);
+ if (status == DMA_ERROR)
+ sprd_stop_rx(port);
+
+ if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
+ return;
+
+ if (!state.residue) {
+ sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
+ sp->rx_dma.phys_addr - sp->pos;
+ sp->pos = sp->rx_dma.phys_addr;
+ } else {
+ sp->rx_dma.trans_len = state.residue - sp->pos;
+ sp->pos = state.residue;
+ }
+
+ sprd_uart_dma_rx(port);
+ sp->rx_buf_tail += sp->rx_dma.trans_len;
+}
+
+static void sprd_complete_rx_dma(void *data)
+{
+ struct uart_port *port = (struct uart_port *)data;
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ status = dmaengine_tx_status(sp->rx_dma.chn,
+ sp->rx_dma.cookie, &state);
+ if (status != DMA_COMPLETE) {
+ sprd_stop_rx(port);
+ spin_unlock_irqrestore(&port->lock, flags);
+ return;
+ }
+
+ if (sp->pos != sp->rx_dma.phys_addr) {
+ sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
+ sp->rx_dma.phys_addr - sp->pos;
+ sprd_uart_dma_rx(port);
+ sp->rx_buf_tail += sp->rx_dma.trans_len;
+ }
+
+ if (sprd_start_dma_rx(port))
+ sprd_stop_rx(port);
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_start_dma_rx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+ int ret;
+
+ if (!sp->rx_dma.enable)
+ return 0;
+
+ sp->pos = sp->rx_dma.phys_addr;
+ sp->rx_buf_tail = sp->rx_dma.virt;
+ sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
+ ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
+ if (ret)
+ return ret;
+
+ return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
+ DMA_DEV_TO_MEM, sprd_complete_rx_dma);
+}
+
+static void sprd_release_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+
+ sprd_uart_dma_enable(port, false);
+
+ if (sp->rx_dma.enable)
+ dma_release_channel(sp->rx_dma.chn);
+
+ if (sp->tx_dma.enable)
+ dma_release_channel(sp->tx_dma.chn);
+
+ sp->tx_dma.enable = false;
+ sp->rx_dma.enable = false;
+}
+
+static void sprd_request_dma(struct uart_port *port)
+{
+ struct sprd_uart_port *sp =
+ container_of(port, struct sprd_uart_port, port);
+
+ sp->tx_dma.enable = true;
+ sp->rx_dma.enable = true;
+
+ sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
+ if (IS_ERR(sp->tx_dma.chn)) {
+ dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
+ PTR_ERR(sp->tx_dma.chn));
+ sp->tx_dma.enable = false;
+ }
+
+ sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
+ if (IS_ERR(sp->rx_dma.chn)) {
+ dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
+ PTR_ERR(sp->rx_dma.chn));
+ sp->rx_dma.enable = false;
+ }
+}
+
+static void sprd_stop_tx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
+ unsigned int ien, iclr;
+
+ if (sp->tx_dma.enable) {
+ sprd_stop_tx_dma(port);
+ return;
+ }
+
+ iclr = serial_in(port, SPRD_ICLR);
+ ien = serial_in(port, SPRD_IEN);
+
+ iclr |= SPRD_IEN_TX_EMPTY;
+ ien &= ~SPRD_IEN_TX_EMPTY;
+
+ serial_out(port, SPRD_IEN, ien);
+ serial_out(port, SPRD_ICLR, iclr);
+}
+
+static void sprd_start_tx(struct uart_port *port)
+{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
+ unsigned int ien;
+
+ if (sp->tx_dma.enable) {
+ sprd_start_tx_dma(port);
+ return;
+ }
+
+ ien = serial_in(port, SPRD_IEN);
+ if (!(ien & SPRD_IEN_TX_EMPTY)) {
+ ien |= SPRD_IEN_TX_EMPTY;
+ serial_out(port, SPRD_IEN, ien);
+ }
+}
+
/* The Sprd serial does not support this function. */
static void sprd_break_ctl(struct uart_port *port, int break_state)
{
@@ -221,18 +598,26 @@
static inline void sprd_rx(struct uart_port *port)
{
+ struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+ port);
struct tty_port *tty = &port->state->port;
unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
- while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) {
+ if (sp->rx_dma.enable) {
+ sprd_uart_dma_irq(port);
+ return;
+ }
+
+ while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
+ max_count--) {
lsr = serial_in(port, SPRD_LSR);
ch = serial_in(port, SPRD_RXD);
flag = TTY_NORMAL;
port->icount.rx++;
if (lsr & (SPRD_LSR_BI | SPRD_LSR_PE |
- SPRD_LSR_FE | SPRD_LSR_OE))
- if (handle_lsr_errors(port, &lsr, &flag))
+ SPRD_LSR_FE | SPRD_LSR_OE))
+ if (handle_lsr_errors(port, &flag, &lsr))
continue;
if (uart_handle_sysrq_char(port, ch))
continue;
@@ -294,8 +679,8 @@
if (ims & SPRD_IMSR_TIMEOUT)
serial_out(port, SPRD_ICLR, SPRD_ICLR_TIMEOUT);
- if (ims & (SPRD_IMSR_RX_FIFO_FULL |
- SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT))
+ if (ims & (SPRD_IMSR_RX_FIFO_FULL | SPRD_IMSR_BREAK_DETECT |
+ SPRD_IMSR_TIMEOUT))
sprd_rx(port);
if (ims & SPRD_IMSR_TX_FIFO_EMPTY)
@@ -306,6 +691,25 @@
return IRQ_HANDLED;
}
+static void sprd_uart_dma_startup(struct uart_port *port,
+ struct sprd_uart_port *sp)
+{
+ int ret;
+
+ sprd_request_dma(port);
+ if (!(sp->rx_dma.enable || sp->tx_dma.enable))
+ return;
+
+ ret = sprd_start_dma_rx(port);
+ if (ret) {
+ sp->rx_dma.enable = false;
+ dma_release_channel(sp->rx_dma.chn);
+ dev_warn(port->dev, "fail to start RX dma mode\n");
+ }
+
+ sprd_uart_dma_enable(port, true);
+}
+
static int sprd_startup(struct uart_port *port)
{
int ret = 0;
@@ -314,16 +718,17 @@
struct sprd_uart_port *sp;
unsigned long flags;
- serial_out(port, SPRD_CTL2, ((THLD_TX_EMPTY << 8) | THLD_RX_FULL));
+ serial_out(port, SPRD_CTL2,
+ THLD_TX_EMPTY << THLD_TX_EMPTY_SHIFT | THLD_RX_FULL);
/* clear rx fifo */
timeout = SPRD_TIMEOUT;
- while (timeout-- && serial_in(port, SPRD_STS1) & 0x00ff)
+ while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK)
serial_in(port, SPRD_RXD);
/* clear tx fifo */
timeout = SPRD_TIMEOUT;
- while (timeout-- && serial_in(port, SPRD_STS1) & 0xff00)
+ while (timeout-- && serial_in(port, SPRD_STS1) & SPRD_TX_FIFO_CNT_MASK)
cpu_relax();
/* clear interrupt */
@@ -333,8 +738,11 @@
/* allocate irq */
sp = container_of(port, struct sprd_uart_port, port);
snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
+
+ sprd_uart_dma_startup(port, sp);
+
ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
- IRQF_SHARED, sp->name, port);
+ IRQF_SHARED, sp->name, port);
if (ret) {
dev_err(port->dev, "fail to request serial irq %d, ret=%d\n",
port->irq, ret);
@@ -347,7 +755,9 @@
/* enable interrupt */
spin_lock_irqsave(&port->lock, flags);
ien = serial_in(port, SPRD_IEN);
- ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+ ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+ if (!sp->rx_dma.enable)
+ ien |= SPRD_IEN_RX_FULL;
serial_out(port, SPRD_IEN, ien);
spin_unlock_irqrestore(&port->lock, flags);
@@ -356,14 +766,15 @@
static void sprd_shutdown(struct uart_port *port)
{
+ sprd_release_dma(port);
serial_out(port, SPRD_IEN, 0);
serial_out(port, SPRD_ICLR, ~0);
devm_free_irq(port->dev, port->irq, port);
}
static void sprd_set_termios(struct uart_port *port,
- struct ktermios *termios,
- struct ktermios *old)
+ struct ktermios *termios,
+ struct ktermios *old)
{
unsigned int baud, quot;
unsigned int lcr = 0, fc;
@@ -372,7 +783,7 @@
/* ask the core to calculate the divisor for us */
baud = uart_get_baud_rate(port, termios, old, 0, SPRD_BAUD_IO_LIMIT);
- quot = (unsigned int)((port->uartclk + baud / 2) / baud);
+ quot = port->uartclk / baud;
/* set data length */
switch (termios->c_cflag & CSIZE) {
@@ -444,10 +855,11 @@
}
/* clock divider bit0~bit15 */
- serial_out(port, SPRD_CLKD0, quot & 0xffff);
+ serial_out(port, SPRD_CLKD0, quot & SPRD_CLKD0_MASK);
/* clock divider bit16~bit20 */
- serial_out(port, SPRD_CLKD1, (quot & 0x1f0000) >> 16);
+ serial_out(port, SPRD_CLKD1,
+ (quot & SPRD_CLKD1_MASK) >> SPRD_CLKD1_SHIFT);
serial_out(port, SPRD_LCR, lcr);
fc |= RX_TOUT_THLD_DEF | RX_HFC_THLD_DEF;
serial_out(port, SPRD_CTL1, fc);
@@ -480,8 +892,7 @@
port->type = PORT_SPRD;
}
-static int sprd_verify_port(struct uart_port *port,
- struct serial_struct *ser)
+static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (ser->type != PORT_SPRD)
return -EINVAL;
@@ -492,6 +903,22 @@
return 0;
}
+static void sprd_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
+{
+ struct sprd_uart_port *sup =
+ container_of(port, struct sprd_uart_port, port);
+
+ switch (state) {
+ case UART_PM_STATE_ON:
+ clk_prepare_enable(sup->clk);
+ break;
+ case UART_PM_STATE_OFF:
+ clk_disable_unprepare(sup->clk);
+ break;
+ }
+}
+
static const struct uart_ops serial_sprd_ops = {
.tx_empty = sprd_tx_empty,
.get_mctrl = sprd_get_mctrl,
@@ -508,6 +935,7 @@
.request_port = sprd_request_port,
.config_port = sprd_config_port,
.verify_port = sprd_verify_port,
+ .pm = sprd_pm,
};
#ifdef CONFIG_SERIAL_SPRD_CONSOLE
@@ -521,7 +949,7 @@
if (--tmout == 0)
break;
udelay(1);
- } while (status & 0xff00);
+ } while (status & SPRD_TX_FIFO_CNT_MASK);
}
static void sprd_console_putchar(struct uart_port *port, int ch)
@@ -531,7 +959,7 @@
}
static void sprd_console_write(struct console *co, const char *s,
- unsigned int count)
+ unsigned int count)
{
struct uart_port *port = &sprd_port[co->index]->port;
int locked = 1;
@@ -555,7 +983,7 @@
static int __init sprd_console_setup(struct console *co, char *options)
{
- struct uart_port *port;
+ struct sprd_uart_port *sprd_uart_port;
int baud = 115200;
int bits = 8;
int parity = 'n';
@@ -564,15 +992,17 @@
if (co->index >= UART_NR_MAX || co->index < 0)
co->index = 0;
- port = &sprd_port[co->index]->port;
- if (port == NULL) {
+ sprd_uart_port = sprd_port[co->index];
+ if (!sprd_uart_port || !sprd_uart_port->port.membase) {
pr_info("serial port %d not yet initialized\n", co->index);
return -ENODEV;
}
+
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- return uart_set_options(port, co, baud, parity, bits, flow);
+ return uart_set_options(&sprd_uart_port->port, co, baud,
+ parity, bits, flow);
}
static struct uart_driver sprd_uart_driver;
@@ -586,6 +1016,13 @@
.data = &sprd_uart_driver,
};
+static int __init sprd_serial_console_init(void)
+{
+ register_console(&sprd_console);
+ return 0;
+}
+console_initcall(sprd_serial_console_init);
+
#define SPRD_CONSOLE (&sprd_console)
/* Support for earlycon */
@@ -594,23 +1031,21 @@
unsigned int timeout = SPRD_TIMEOUT;
while (timeout-- &&
- !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER))
+ !(readl(port->membase + SPRD_LSR) & SPRD_LSR_TX_OVER))
cpu_relax();
writeb(c, port->membase + SPRD_TXD);
}
-static void sprd_early_write(struct console *con, const char *s,
- unsigned n)
+static void sprd_early_write(struct console *con, const char *s, unsigned int n)
{
struct earlycon_device *dev = con->data;
uart_console_write(&dev->port, s, n, sprd_putc);
}
-static int __init sprd_early_console_setup(
- struct earlycon_device *device,
- const char *opt)
+static int __init sprd_early_console_setup(struct earlycon_device *device,
+ const char *opt)
{
if (!device->port.membase)
return -ENODEV;
@@ -671,6 +1106,60 @@
if (!sprd_ports_num)
uart_unregister_driver(&sprd_uart_driver);
+ sprd_rx_free_buf(sup);
+
+ return 0;
+}
+
+static bool sprd_uart_is_console(struct uart_port *uport)
+{
+ struct console *cons = sprd_uart_driver.cons;
+
+ if (cons && cons->index >= 0 && cons->index == uport->line)
+ return true;
+
+ return false;
+}
+
+static int sprd_clk_init(struct uart_port *uport)
+{
+ struct clk *clk_uart, *clk_parent;
+ struct sprd_uart_port *u = sprd_port[uport->line];
+
+ clk_uart = devm_clk_get(uport->dev, "uart");
+ if (IS_ERR(clk_uart)) {
+ dev_warn(uport->dev, "uart%d can't get uart clock\n",
+ uport->line);
+ clk_uart = NULL;
+ }
+
+ clk_parent = devm_clk_get(uport->dev, "source");
+ if (IS_ERR(clk_parent)) {
+ dev_warn(uport->dev, "uart%d can't get source clock\n",
+ uport->line);
+ clk_parent = NULL;
+ }
+
+ if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
+ uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
+ else
+ uport->uartclk = clk_get_rate(clk_uart);
+
+ u->clk = devm_clk_get(uport->dev, "enable");
+ if (IS_ERR(u->clk)) {
+ if (PTR_ERR(u->clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_warn(uport->dev, "uart%d can't get enable clock\n",
+ uport->line);
+
+ /* To keep console alive even if the error occurred */
+ if (!sprd_uart_is_console(uport))
+ return PTR_ERR(u->clk);
+
+ u->clk = NULL;
+ }
+
return 0;
}
@@ -678,7 +1167,6 @@
{
struct resource *res;
struct uart_port *up;
- struct clk *clk;
int irq;
int index;
int ret;
@@ -692,8 +1180,8 @@
index = sprd_probe_dt_alias(index, &pdev->dev);
- sprd_port[index] = devm_kzalloc(&pdev->dev,
- sizeof(*sprd_port[index]), GFP_KERNEL);
+ sprd_port[index] = devm_kzalloc(&pdev->dev, sizeof(*sprd_port[index]),
+ GFP_KERNEL);
if (!sprd_port[index])
return -ENOMEM;
@@ -707,27 +1195,30 @@
up->ops = &serial_sprd_ops;
up->flags = UPF_BOOT_AUTOCONF;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (!IS_ERR_OR_NULL(clk))
- up->uartclk = clk_get_rate(clk);
+ ret = sprd_clk_init(up);
+ if (ret)
+ return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "not provide mem resource\n");
- return -ENODEV;
- }
- up->mapbase = res->start;
up->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(up->membase))
return PTR_ERR(up->membase);
+ up->mapbase = res->start;
+
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "not provide irq resource: %d\n", irq);
+ if (irq < 0)
return irq;
- }
up->irq = irq;
+ /*
+ * Allocate one dma buffer to prepare for receive transfer, in case
+ * memory allocation failure at runtime.
+ */
+ ret = sprd_rx_alloc_buf(sprd_port[index]);
+ if (ret)
+ return ret;
+
if (!sprd_ports_num) {
ret = uart_register_driver(&sprd_uart_driver);
if (ret < 0) {
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index e8d7a7b..df90747 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
@@ -105,9 +106,7 @@
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 usartdiv, baud, cr1, cr3;
bool over8;
- unsigned long flags;
- spin_lock_irqsave(&port->lock, flags);
stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
port->rs485 = *rs485conf;
@@ -147,7 +146,6 @@
}
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
- spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
@@ -194,8 +192,8 @@
return 0;
}
-static unsigned long
-stm32_get_char(struct uart_port *port, u32 *sr, int *last_res)
+static unsigned long stm32_get_char(struct uart_port *port, u32 *sr,
+ int *last_res)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
@@ -205,10 +203,13 @@
c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--];
if ((*last_res) == 0)
*last_res = RX_BUF_L;
- return c;
} else {
- return readl_relaxed(port->membase + ofs->rdr);
+ c = readl_relaxed(port->membase + ofs->rdr);
+ /* apply RDR data mask */
+ c &= stm32_port->rdr_mask;
}
+
+ return c;
}
static void stm32_receive_chars(struct uart_port *port, bool threaded)
@@ -225,35 +226,51 @@
while (stm32_pending_rx(port, &sr, &stm32_port->last_res, threaded)) {
sr |= USART_SR_DUMMY_RX;
- c = stm32_get_char(port, &sr, &stm32_port->last_res);
flag = TTY_NORMAL;
- port->icount.rx++;
+ /*
+ * Status bits has to be cleared before reading the RDR:
+ * In FIFO mode, reading the RDR will pop the next data
+ * (if any) along with its status bits into the SR.
+ * Not doing so leads to misalignement between RDR and SR,
+ * and clear status bits of the next rx data.
+ *
+ * Clear errors flags for stm32f7 and stm32h7 compatible
+ * devices. On stm32f4 compatible devices, the error bit is
+ * cleared by the sequence [read SR - read DR].
+ */
+ if ((sr & USART_SR_ERR_MASK) && ofs->icr != UNDEF_REG)
+ stm32_clr_bits(port, ofs->icr, USART_ICR_ORECF |
+ USART_ICR_PECF | USART_ICR_FECF);
+
+ c = stm32_get_char(port, &sr, &stm32_port->last_res);
+ port->icount.rx++;
if (sr & USART_SR_ERR_MASK) {
- if (sr & USART_SR_LBD) {
- port->icount.brk++;
- if (uart_handle_break(port))
- continue;
- } else if (sr & USART_SR_ORE) {
- if (ofs->icr != UNDEF_REG)
- writel_relaxed(USART_ICR_ORECF,
- port->membase +
- ofs->icr);
+ if (sr & USART_SR_ORE) {
port->icount.overrun++;
} else if (sr & USART_SR_PE) {
port->icount.parity++;
} else if (sr & USART_SR_FE) {
- port->icount.frame++;
+ /* Break detection if character is null */
+ if (!c) {
+ port->icount.brk++;
+ if (uart_handle_break(port))
+ continue;
+ } else {
+ port->icount.frame++;
+ }
}
sr &= port->read_status_mask;
- if (sr & USART_SR_LBD)
- flag = TTY_BREAK;
- else if (sr & USART_SR_PE)
+ if (sr & USART_SR_PE) {
flag = TTY_PARITY;
- else if (sr & USART_SR_FE)
- flag = TTY_FRAME;
+ } else if (sr & USART_SR_FE) {
+ if (!c)
+ flag = TTY_BREAK;
+ else
+ flag = TTY_FRAME;
+ }
}
if (uart_handle_sysrq_char(port, c))
@@ -271,21 +288,6 @@
struct uart_port *port = arg;
struct stm32_port *stm32port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32port->info->ofs;
- unsigned int isr;
- int ret;
-
- ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
- isr,
- (isr & USART_SR_TC),
- 10, 100000);
-
- if (ret)
- dev_err(port->dev, "terminal count not set\n");
-
- if (ofs->icr == UNDEF_REG)
- stm32_clr_bits(port, ofs->isr, USART_SR_TC);
- else
- stm32_set_bits(port, ofs->icr, USART_CR_TC);
stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
stm32port->tx_dma_busy = false;
@@ -294,32 +296,57 @@
stm32_transmit_chars(port);
}
+static void stm32_tx_interrupt_enable(struct uart_port *port)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ /*
+ * Enables TX FIFO threashold irq when FIFO is enabled,
+ * or TX empty irq when FIFO is disabled
+ */
+ if (stm32_port->fifoen)
+ stm32_set_bits(port, ofs->cr3, USART_CR3_TXFTIE);
+ else
+ stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE);
+}
+
+static void stm32_tx_interrupt_disable(struct uart_port *port)
+{
+ struct stm32_port *stm32_port = to_stm32_port(port);
+ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
+
+ if (stm32_port->fifoen)
+ stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE);
+ else
+ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
+}
+
static void stm32_transmit_chars_pio(struct uart_port *port)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct circ_buf *xmit = &port->state->xmit;
- unsigned int isr;
- int ret;
if (stm32_port->tx_dma_busy) {
stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
stm32_port->tx_dma_busy = false;
}
- ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr,
- isr,
- (isr & USART_SR_TXE),
- 10, 100000);
+ while (!uart_circ_empty(xmit)) {
+ /* Check that TDR is empty before filling FIFO */
+ if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE))
+ break;
+ writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ }
- if (ret)
- dev_err(port->dev, "tx empty not set\n");
-
- stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE);
-
- writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr);
- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
- port->icount.tx++;
+ /* rely on TXE irq (mask or unmask) for sending remaining data */
+ if (uart_circ_empty(xmit))
+ stm32_tx_interrupt_disable(port);
+ else
+ stm32_tx_interrupt_enable(port);
}
static void stm32_transmit_chars_dma(struct uart_port *port)
@@ -377,7 +404,6 @@
/* Issue pending DMA TX requests */
dma_async_issue_pending(stm32port->tx_ch);
- stm32_clr_bits(port, ofs->isr, USART_SR_TC);
stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT);
xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
@@ -401,15 +427,15 @@
return;
}
- if (uart_tx_stopped(port)) {
- stm32_stop_tx(port);
+ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+ stm32_tx_interrupt_disable(port);
return;
}
- if (uart_circ_empty(xmit)) {
- stm32_stop_tx(port);
- return;
- }
+ if (ofs->icr == UNDEF_REG)
+ stm32_clr_bits(port, ofs->isr, USART_SR_TC);
+ else
+ stm32_set_bits(port, ofs->icr, USART_ICR_TCCF);
if (stm32_port->tx_ch)
stm32_transmit_chars_dma(port);
@@ -420,7 +446,7 @@
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
- stm32_stop_tx(port);
+ stm32_tx_interrupt_disable(port);
}
static irqreturn_t stm32_interrupt(int irq, void *ptr)
@@ -434,6 +460,10 @@
sr = readl_relaxed(port->membase + ofs->isr);
+ if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG)
+ writel_relaxed(USART_ICR_RTOCF,
+ port->membase + ofs->icr);
+
if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG))
writel_relaxed(USART_ICR_WUCF,
port->membase + ofs->icr);
@@ -495,10 +525,7 @@
/* Transmit stop */
static void stm32_stop_tx(struct uart_port *port)
{
- struct stm32_port *stm32_port = to_stm32_port(port);
- struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
-
- stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE);
+ stm32_tx_interrupt_disable(port);
}
/* There are probably characters waiting to be transmitted. */
@@ -520,7 +547,10 @@
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
+ stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
+ if (stm32_port->cr3_irq)
+ stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq);
+
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -532,7 +562,10 @@
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
- stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE);
+ stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq);
+ if (stm32_port->cr3_irq)
+ stm32_set_bits(port, ofs->cr3, stm32_port->cr3_irq);
+
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -542,7 +575,10 @@
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE);
+ stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq);
+ if (stm32_port->cr3_irq)
+ stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq);
+
}
/* Handle breaks - ignored by us */
@@ -554,7 +590,6 @@
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- struct stm32_usart_config *cfg = &stm32_port->info->cfg;
const char *name = to_platform_device(port->dev)->name;
u32 val;
int ret;
@@ -565,16 +600,21 @@
if (ret)
return ret;
- if (cfg->has_wakeup && stm32_port->wakeirq >= 0) {
- ret = dev_pm_set_dedicated_wake_irq(port->dev,
- stm32_port->wakeirq);
- if (ret) {
- free_irq(port->irq, port);
- return ret;
- }
+ /* RX FIFO Flush */
+ if (ofs->rqr != UNDEF_REG)
+ stm32_set_bits(port, ofs->rqr, USART_RQR_RXFRQ);
+
+ /* Tx and RX FIFO configuration */
+ if (stm32_port->fifoen) {
+ val = readl_relaxed(port->membase + ofs->cr3);
+ val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK);
+ val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT;
+ val |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT;
+ writel_relaxed(val, port->membase + ofs->cr3);
}
- val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
+ /* RX FIFO enabling */
+ val = stm32_port->cr1_irq | USART_CR1_RE;
if (stm32_port->fifoen)
val |= USART_CR1_FIFOEN;
stm32_set_bits(port, ofs->cr1, val);
@@ -587,18 +627,57 @@
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
- u32 val;
+ u32 val, isr;
+ int ret;
- val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
+ val = USART_CR1_TXEIE | USART_CR1_TE;
+ val |= stm32_port->cr1_irq | USART_CR1_RE;
val |= BIT(cfg->uart_enable_bit);
if (stm32_port->fifoen)
val |= USART_CR1_FIFOEN;
+
+ ret = readl_relaxed_poll_timeout(port->membase + ofs->isr,
+ isr, (isr & USART_SR_TC),
+ 10, 100000);
+
+ if (ret)
+ dev_err(port->dev, "transmission complete not set\n");
+
stm32_clr_bits(port, ofs->cr1, val);
- dev_pm_clear_wake_irq(port->dev);
free_irq(port->irq, port);
}
+static unsigned int stm32_get_databits(struct ktermios *termios)
+{
+ unsigned int bits;
+
+ tcflag_t cflag = termios->c_cflag;
+
+ switch (cflag & CSIZE) {
+ /*
+ * CSIZE settings are not necessarily supported in hardware.
+ * CSIZE unsupported configurations are handled here to set word length
+ * to 8 bits word as default configuration and to print debug message.
+ */
+ case CS5:
+ bits = 5;
+ break;
+ case CS6:
+ bits = 6;
+ break;
+ case CS7:
+ bits = 7;
+ break;
+ /* default including CS8 */
+ default:
+ bits = 8;
+ break;
+ }
+
+ return bits;
+}
+
static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
@@ -606,7 +685,7 @@
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
struct serial_rs485 *rs485conf = &port->rs485;
- unsigned int baud;
+ unsigned int baud, bits;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
u32 cr1, cr2, cr3;
@@ -622,26 +701,64 @@
/* Stop serial port and reset value */
writel_relaxed(0, port->membase + ofs->cr1);
- cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
+ /* flush RX & TX FIFO */
+ if (ofs->rqr != UNDEF_REG)
+ stm32_set_bits(port, ofs->rqr,
+ USART_RQR_TXFRQ | USART_RQR_RXFRQ);
+ cr1 = USART_CR1_TE | USART_CR1_RE;
if (stm32_port->fifoen)
cr1 |= USART_CR1_FIFOEN;
cr2 = 0;
- cr3 = 0;
+ cr3 = readl_relaxed(port->membase + ofs->cr3);
+ cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG_MASK | USART_CR3_RXFTIE
+ | USART_CR3_TXFTCFG_MASK;
if (cflag & CSTOPB)
cr2 |= USART_CR2_STOP_2B;
+ bits = stm32_get_databits(termios);
+ stm32_port->rdr_mask = (BIT(bits) - 1);
+
if (cflag & PARENB) {
+ bits++;
cr1 |= USART_CR1_PCE;
- if ((cflag & CSIZE) == CS8) {
- if (cfg->has_7bits_data)
- cr1 |= USART_CR1_M0;
- else
- cr1 |= USART_CR1_M;
- }
}
+ /*
+ * Word length configuration:
+ * CS8 + parity, 9 bits word aka [M1:M0] = 0b01
+ * CS7 or (CS6 + parity), 7 bits word aka [M1:M0] = 0b10
+ * CS8 or (CS7 + parity), 8 bits word aka [M1:M0] = 0b00
+ * M0 and M1 already cleared by cr1 initialization.
+ */
+ if (bits == 9)
+ cr1 |= USART_CR1_M0;
+ else if ((bits == 7) && cfg->has_7bits_data)
+ cr1 |= USART_CR1_M1;
+ else if (bits != 8)
+ dev_dbg(port->dev, "Unsupported data bits config: %u bits\n"
+ , bits);
+
+ if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch ||
+ stm32_port->fifoen)) {
+ if (cflag & CSTOPB)
+ bits = bits + 3; /* 1 start bit + 2 stop bits */
+ else
+ bits = bits + 2; /* 1 start bit + 1 stop bit */
+
+ /* RX timeout irq to occur after last stop bit + bits */
+ stm32_port->cr1_irq = USART_CR1_RTOIE;
+ writel_relaxed(bits, port->membase + ofs->rtor);
+ cr2 |= USART_CR2_RTOEN;
+ /* Not using dma, enable fifo threshold irq */
+ if (!stm32_port->rx_ch)
+ stm32_port->cr3_irq = USART_CR3_RXFTIE;
+ }
+
+ cr1 |= stm32_port->cr1_irq;
+ cr3 |= stm32_port->cr3_irq;
+
if (cflag & PARODD)
cr1 |= USART_CR1_PS;
@@ -679,14 +796,14 @@
if (termios->c_iflag & INPCK)
port->read_status_mask |= USART_SR_PE | USART_SR_FE;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
- port->read_status_mask |= USART_SR_LBD;
+ port->read_status_mask |= USART_SR_FE;
/* Characters to ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask = USART_SR_PE | USART_SR_FE;
if (termios->c_iflag & IGNBRK) {
- port->ignore_status_mask |= USART_SR_LBD;
+ port->ignore_status_mask |= USART_SR_FE;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
@@ -765,13 +882,13 @@
switch (state) {
case UART_PM_STATE_ON:
- clk_prepare_enable(stm32port->clk);
+ pm_runtime_get_sync(port->dev);
break;
case UART_PM_STATE_OFF:
spin_lock_irqsave(&port->lock, flags);
stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
- clk_disable_unprepare(stm32port->clk);
+ pm_runtime_put_sync(port->dev);
break;
}
}
@@ -808,12 +925,23 @@
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
- port->irq = platform_get_irq(pdev, 0);
+ port->fifosize = stm32port->info->cfg.fifosize;
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0)
+ return ret ? : -ENODEV;
+ port->irq = ret;
+
port->rs485_config = stm32_config_rs485;
stm32_init_rs485(port, pdev);
- stm32port->wakeirq = platform_get_irq(pdev, 1);
+ if (stm32port->info->cfg.has_wakeup) {
+ stm32port->wakeirq = platform_get_irq(pdev, 1);
+ if (stm32port->wakeirq <= 0 && stm32port->wakeirq != -ENXIO)
+ return stm32port->wakeirq ? : -ENODEV;
+ }
+
stm32port->fifoen = stm32port->info->cfg.has_fifo;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -862,6 +990,8 @@
stm32_ports[id].hw_flow_control = of_property_read_bool(np,
"st,hw-flow-ctrl");
stm32_ports[id].port.line = id;
+ stm32_ports[id].cr1_irq = USART_CR1_RXNEIE;
+ stm32_ports[id].cr3_irq = 0;
stm32_ports[id].last_res = RX_BUF_L;
return &stm32_ports[id];
}
@@ -1020,15 +1150,22 @@
if (ret)
return ret;
- if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) {
+ if (stm32port->wakeirq > 0) {
ret = device_init_wakeup(&pdev->dev, true);
if (ret)
goto err_uninit;
+
+ ret = dev_pm_set_dedicated_wake_irq(&pdev->dev,
+ stm32port->wakeirq);
+ if (ret)
+ goto err_nowup;
+
+ device_set_wakeup_enable(&pdev->dev, false);
}
ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port);
if (ret)
- goto err_nowup;
+ goto err_wirq;
ret = stm32_of_dma_rx_probe(stm32port, pdev);
if (ret)
@@ -1040,10 +1177,19 @@
platform_set_drvdata(pdev, &stm32port->port);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_put_sync(&pdev->dev);
+
return 0;
+err_wirq:
+ if (stm32port->wakeirq > 0)
+ dev_pm_clear_wake_irq(&pdev->dev);
+
err_nowup:
- if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0)
+ if (stm32port->wakeirq > 0)
device_init_wakeup(&pdev->dev, false);
err_uninit:
@@ -1057,7 +1203,9 @@
struct uart_port *port = platform_get_drvdata(pdev);
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
- struct stm32_usart_config *cfg = &stm32_port->info->cfg;
+ int err;
+
+ pm_runtime_get_sync(&pdev->dev);
stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
@@ -1079,12 +1227,19 @@
TX_BUF_L, stm32_port->tx_buf,
stm32_port->tx_dma_buf);
- if (cfg->has_wakeup && stm32_port->wakeirq >= 0)
+ if (stm32_port->wakeirq > 0) {
+ dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
+ }
clk_disable_unprepare(stm32_port->clk);
- return uart_remove_one_port(&stm32_usart_driver, port);
+ err = uart_remove_one_port(&stm32_usart_driver, port);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return err;
}
@@ -1187,15 +1342,15 @@
.cons = STM32_SERIAL_CONSOLE,
};
-#ifdef CONFIG_PM_SLEEP
-static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable)
+static void __maybe_unused stm32_serial_enable_wakeup(struct uart_port *port,
+ bool enable)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 val;
- if (!cfg->has_wakeup || stm32_port->wakeirq < 0)
+ if (stm32_port->wakeirq <= 0)
return;
if (enable) {
@@ -1212,7 +1367,7 @@
}
}
-static int stm32_serial_suspend(struct device *dev)
+static int __maybe_unused stm32_serial_suspend(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
@@ -1223,21 +1378,46 @@
else
stm32_serial_enable_wakeup(port, false);
+ pinctrl_pm_select_sleep_state(dev);
+
return 0;
}
-static int stm32_serial_resume(struct device *dev)
+static int __maybe_unused stm32_serial_resume(struct device *dev)
{
struct uart_port *port = dev_get_drvdata(dev);
+ pinctrl_pm_select_default_state(dev);
+
if (device_may_wakeup(dev))
stm32_serial_enable_wakeup(port, false);
return uart_resume_port(&stm32_usart_driver, port);
}
-#endif /* CONFIG_PM_SLEEP */
+
+static int __maybe_unused stm32_serial_runtime_suspend(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct stm32_port *stm32port = container_of(port,
+ struct stm32_port, port);
+
+ clk_disable_unprepare(stm32port->clk);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_serial_runtime_resume(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct stm32_port *stm32port = container_of(port,
+ struct stm32_port, port);
+
+ return clk_prepare_enable(stm32port->clk);
+}
static const struct dev_pm_ops stm32_serial_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_serial_runtime_suspend,
+ stm32_serial_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume)
};
diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h
index 6f294e2..a175c10 100644
--- a/drivers/tty/serial/stm32-usart.h
+++ b/drivers/tty/serial/stm32-usart.h
@@ -27,6 +27,7 @@
bool has_7bits_data;
bool has_wakeup;
bool has_fifo;
+ int fifosize;
};
struct stm32_usart_info {
@@ -54,6 +55,7 @@
.cfg = {
.uart_enable_bit = 13,
.has_7bits_data = false,
+ .fifosize = 1,
}
};
@@ -74,6 +76,7 @@
.cfg = {
.uart_enable_bit = 0,
.has_7bits_data = true,
+ .fifosize = 1,
}
};
@@ -96,6 +99,7 @@
.has_7bits_data = true,
.has_wakeup = true,
.has_fifo = true,
+ .fifosize = 16,
}
};
@@ -108,7 +112,6 @@
#define USART_SR_RXNE BIT(5)
#define USART_SR_TC BIT(6)
#define USART_SR_TXE BIT(7)
-#define USART_SR_LBD BIT(8)
#define USART_SR_CTSIF BIT(9)
#define USART_SR_CTS BIT(10) /* F7 */
#define USART_SR_RTOF BIT(11) /* F7 */
@@ -120,8 +123,7 @@
#define USART_SR_SBKF BIT(18) /* F7 */
#define USART_SR_WUF BIT(20) /* H7 */
#define USART_SR_TEACK BIT(21) /* F7 */
-#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
- USART_SR_FE | USART_SR_PE)
+#define USART_SR_ERR_MASK (USART_SR_ORE | USART_SR_FE | USART_SR_PE)
/* Dummy bits */
#define USART_SR_DUMMY_RX BIT(16)
@@ -151,8 +153,7 @@
#define USART_CR1_PS BIT(9)
#define USART_CR1_PCE BIT(10)
#define USART_CR1_WAKE BIT(11)
-#define USART_CR1_M BIT(12)
-#define USART_CR1_M0 BIT(12) /* F7 */
+#define USART_CR1_M0 BIT(12) /* F7 (CR1_M for F4) */
#define USART_CR1_MME BIT(13) /* F7 */
#define USART_CR1_CMIE BIT(14) /* F7 */
#define USART_CR1_OVER8 BIT(15)
@@ -169,8 +170,6 @@
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
#define USART_CR2_ADDM7 BIT(4) /* F7 */
-#define USART_CR2_LBDL BIT(5)
-#define USART_CR2_LBDIE BIT(6)
#define USART_CR2_LBCL BIT(8)
#define USART_CR2_CPHA BIT(9)
#define USART_CR2_CPOL BIT(10)
@@ -209,6 +208,19 @@
#define USART_CR3_WUS_MASK GENMASK(21, 20) /* H7 */
#define USART_CR3_WUS_START_BIT BIT(21) /* H7 */
#define USART_CR3_WUFIE BIT(22) /* H7 */
+#define USART_CR3_TXFTIE BIT(23) /* H7 */
+#define USART_CR3_TCBGTIE BIT(24) /* H7 */
+#define USART_CR3_RXFTCFG_MASK GENMASK(27, 25) /* H7 */
+#define USART_CR3_RXFTCFG_SHIFT 25 /* H7 */
+#define USART_CR3_RXFTIE BIT(28) /* H7 */
+#define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */
+#define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */
+
+/* TX FIFO threashold set to half of its depth */
+#define USART_CR3_TXFTCFG_HALF 0x2
+
+/* RX FIFO threashold set to half of its depth */
+#define USART_CR3_RXFTCFG_HALF 0x2
/* USART_GTPR */
#define USART_GTPR_PSC_MASK GENMASK(7, 0)
@@ -227,12 +239,10 @@
/* USART_ICR */
#define USART_ICR_PECF BIT(0) /* F7 */
-#define USART_ICR_FFECF BIT(1) /* F7 */
-#define USART_ICR_NCF BIT(2) /* F7 */
+#define USART_ICR_FECF BIT(1) /* F7 */
#define USART_ICR_ORECF BIT(3) /* F7 */
#define USART_ICR_IDLECF BIT(4) /* F7 */
#define USART_ICR_TCCF BIT(6) /* F7 */
-#define USART_ICR_LBDCF BIT(8) /* F7 */
#define USART_ICR_CTSCF BIT(9) /* F7 */
#define USART_ICR_RTOCF BIT(11) /* F7 */
#define USART_ICR_EOBCF BIT(12) /* F7 */
@@ -256,11 +266,14 @@
struct dma_chan *tx_ch; /* dma tx channel */
dma_addr_t tx_dma_buf; /* dma tx buffer bus address */
unsigned char *tx_buf; /* dma tx buffer cpu address */
+ u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */
+ u32 cr3_irq; /* USART_CR3_RXFTIE */
int last_res;
bool tx_dma_busy; /* dma tx busy */
bool hw_flow_control;
bool fifoen;
int wakeirq;
+ int rdr_mask; /* receive data register mask */
};
static struct stm32_port stm32_ports[STM32_MAX_PORTS];
diff --git a/drivers/tty/serial/suncore.c b/drivers/tty/serial/suncore.c
index 9903765..2491551 100644
--- a/drivers/tty/serial/suncore.c
+++ b/drivers/tty/serial/suncore.c
@@ -89,14 +89,14 @@
int baud, bits, stop, cflag;
char parity;
- if (!strcmp(uart_dp->name, "rsc") ||
- !strcmp(uart_dp->name, "rsc-console") ||
- !strcmp(uart_dp->name, "rsc-control")) {
+ if (of_node_name_eq(uart_dp, "rsc") ||
+ of_node_name_eq(uart_dp, "rsc-console") ||
+ of_node_name_eq(uart_dp, "rsc-control")) {
mode = of_get_property(uart_dp,
"ssp-console-modes", NULL);
if (!mode)
mode = "115200,8,n,1,-";
- } else if (!strcmp(uart_dp->name, "lom-console")) {
+ } else if (of_node_name_eq(uart_dp, "lom-console")) {
mode = "9600,8,n,1,-";
} else {
struct device_node *dp;
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 63e34d8..f8503f8 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -397,7 +397,7 @@
static struct uart_driver sunhv_reg = {
.owner = THIS_MODULE,
.driver_name = "sunhv",
- .dev_name = "ttyS",
+ .dev_name = "ttyHV",
.major = TTY_MAJOR,
};
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 6cf3e9b..4db6aaa 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -1394,22 +1394,43 @@
static enum su_type su_get_type(struct device_node *dp)
{
struct device_node *ap = of_find_node_by_path("/aliases");
+ enum su_type rc = SU_PORT_PORT;
if (ap) {
const char *keyb = of_get_property(ap, "keyboard", NULL);
const char *ms = of_get_property(ap, "mouse", NULL);
+ struct device_node *match;
if (keyb) {
- if (dp == of_find_node_by_path(keyb))
- return SU_PORT_KBD;
+ match = of_find_node_by_path(keyb);
+
+ /*
+ * The pointer is used as an identifier not
+ * as a pointer, we can drop the refcount on
+ * the of__node immediately after getting it.
+ */
+ of_node_put(match);
+
+ if (dp == match) {
+ rc = SU_PORT_KBD;
+ goto out;
+ }
}
if (ms) {
- if (dp == of_find_node_by_path(ms))
- return SU_PORT_MS;
+ match = of_find_node_by_path(ms);
+
+ of_node_put(match);
+
+ if (dp == match) {
+ rc = SU_PORT_MS;
+ goto out;
+ }
}
}
- return SU_PORT_PORT;
+out:
+ of_node_put(ap);
+ return rc;
}
static int su_probe(struct platform_device *op)
@@ -1482,8 +1503,8 @@
up->port.ops = &sunsu_pops;
ignore_line = false;
- if (!strcmp(dp->name, "rsc-console") ||
- !strcmp(dp->name, "lom-console"))
+ if (of_node_name_eq(dp, "rsc-console") ||
+ of_node_name_eq(dp, "lom-console"))
ignore_line = true;
sunserial_console_match(SUNSU_CONSOLE(), dp,
diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c
new file mode 100644
index 0000000..aaf8748
--- /dev/null
+++ b/drivers/tty/serial/tegra-tcu.c
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+ */
+
+#include <linux/console.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define TCU_MBOX_BYTE(i, x) ((x) << (i * 8))
+#define TCU_MBOX_BYTE_V(x, i) (((x) >> (i * 8)) & 0xff)
+#define TCU_MBOX_NUM_BYTES(x) ((x) << 24)
+#define TCU_MBOX_NUM_BYTES_V(x) (((x) >> 24) & 0x3)
+
+struct tegra_tcu {
+ struct uart_driver driver;
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+ struct console console;
+#endif
+ struct uart_port port;
+
+ struct mbox_client tx_client, rx_client;
+ struct mbox_chan *tx, *rx;
+};
+
+static unsigned int tegra_tcu_uart_tx_empty(struct uart_port *port)
+{
+ return TIOCSER_TEMT;
+}
+
+static void tegra_tcu_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static unsigned int tegra_tcu_uart_get_mctrl(struct uart_port *port)
+{
+ return 0;
+}
+
+static void tegra_tcu_uart_stop_tx(struct uart_port *port)
+{
+}
+
+static void tegra_tcu_write_one(struct tegra_tcu *tcu, u32 value,
+ unsigned int count)
+{
+ void *msg;
+
+ value |= TCU_MBOX_NUM_BYTES(count);
+ msg = (void *)(unsigned long)value;
+ mbox_send_message(tcu->tx, msg);
+ mbox_flush(tcu->tx, 1000);
+}
+
+static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s,
+ unsigned int count)
+{
+ unsigned int written = 0, i = 0;
+ bool insert_nl = false;
+ u32 value = 0;
+
+ while (i < count) {
+ if (insert_nl) {
+ value |= TCU_MBOX_BYTE(written++, '\n');
+ insert_nl = false;
+ i++;
+ } else if (s[i] == '\n') {
+ value |= TCU_MBOX_BYTE(written++, '\r');
+ insert_nl = true;
+ } else {
+ value |= TCU_MBOX_BYTE(written++, s[i++]);
+ }
+
+ if (written == 3) {
+ tegra_tcu_write_one(tcu, value, 3);
+ value = written = 0;
+ }
+ }
+
+ if (written)
+ tegra_tcu_write_one(tcu, value, written);
+}
+
+static void tegra_tcu_uart_start_tx(struct uart_port *port)
+{
+ struct tegra_tcu *tcu = port->private_data;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long count;
+
+ for (;;) {
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+ if (!count)
+ break;
+
+ tegra_tcu_write(tcu, &xmit->buf[xmit->tail], count);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ }
+
+ uart_write_wakeup(port);
+}
+
+static void tegra_tcu_uart_stop_rx(struct uart_port *port)
+{
+}
+
+static void tegra_tcu_uart_break_ctl(struct uart_port *port, int ctl)
+{
+}
+
+static int tegra_tcu_uart_startup(struct uart_port *port)
+{
+ return 0;
+}
+
+static void tegra_tcu_uart_shutdown(struct uart_port *port)
+{
+}
+
+static void tegra_tcu_uart_set_termios(struct uart_port *port,
+ struct ktermios *new,
+ struct ktermios *old)
+{
+}
+
+static const struct uart_ops tegra_tcu_uart_ops = {
+ .tx_empty = tegra_tcu_uart_tx_empty,
+ .set_mctrl = tegra_tcu_uart_set_mctrl,
+ .get_mctrl = tegra_tcu_uart_get_mctrl,
+ .stop_tx = tegra_tcu_uart_stop_tx,
+ .start_tx = tegra_tcu_uart_start_tx,
+ .stop_rx = tegra_tcu_uart_stop_rx,
+ .break_ctl = tegra_tcu_uart_break_ctl,
+ .startup = tegra_tcu_uart_startup,
+ .shutdown = tegra_tcu_uart_shutdown,
+ .set_termios = tegra_tcu_uart_set_termios,
+};
+
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+static void tegra_tcu_console_write(struct console *cons, const char *s,
+ unsigned int count)
+{
+ struct tegra_tcu *tcu = container_of(cons, struct tegra_tcu, console);
+
+ tegra_tcu_write(tcu, s, count);
+}
+
+static int tegra_tcu_console_setup(struct console *cons, char *options)
+{
+ return 0;
+}
+#endif
+
+static void tegra_tcu_receive(struct mbox_client *cl, void *msg)
+{
+ struct tegra_tcu *tcu = container_of(cl, struct tegra_tcu, rx_client);
+ struct tty_port *port = &tcu->port.state->port;
+ u32 value = (u32)(unsigned long)msg;
+ unsigned int num_bytes, i;
+
+ num_bytes = TCU_MBOX_NUM_BYTES_V(value);
+
+ for (i = 0; i < num_bytes; i++)
+ tty_insert_flip_char(port, TCU_MBOX_BYTE_V(value, i),
+ TTY_NORMAL);
+
+ tty_flip_buffer_push(port);
+}
+
+static int tegra_tcu_probe(struct platform_device *pdev)
+{
+ struct uart_port *port;
+ struct tegra_tcu *tcu;
+ int err;
+
+ tcu = devm_kzalloc(&pdev->dev, sizeof(*tcu), GFP_KERNEL);
+ if (!tcu)
+ return -ENOMEM;
+
+ tcu->tx_client.dev = &pdev->dev;
+ tcu->rx_client.dev = &pdev->dev;
+ tcu->rx_client.rx_callback = tegra_tcu_receive;
+
+ tcu->tx = mbox_request_channel_byname(&tcu->tx_client, "tx");
+ if (IS_ERR(tcu->tx)) {
+ err = PTR_ERR(tcu->tx);
+ dev_err(&pdev->dev, "failed to get tx mailbox: %d\n", err);
+ return err;
+ }
+
+ tcu->rx = mbox_request_channel_byname(&tcu->rx_client, "rx");
+ if (IS_ERR(tcu->rx)) {
+ err = PTR_ERR(tcu->rx);
+ dev_err(&pdev->dev, "failed to get rx mailbox: %d\n", err);
+ goto free_tx;
+ }
+
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+ /* setup the console */
+ strcpy(tcu->console.name, "ttyTCU");
+ tcu->console.device = uart_console_device;
+ tcu->console.flags = CON_PRINTBUFFER | CON_ANYTIME;
+ tcu->console.index = -1;
+ tcu->console.write = tegra_tcu_console_write;
+ tcu->console.setup = tegra_tcu_console_setup;
+ tcu->console.data = &tcu->driver;
+#endif
+
+ /* setup the driver */
+ tcu->driver.owner = THIS_MODULE;
+ tcu->driver.driver_name = "tegra-tcu";
+ tcu->driver.dev_name = "ttyTCU";
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+ tcu->driver.cons = &tcu->console;
+#endif
+ tcu->driver.nr = 1;
+
+ err = uart_register_driver(&tcu->driver);
+ if (err) {
+ dev_err(&pdev->dev, "failed to register UART driver: %d\n",
+ err);
+ goto free_rx;
+ }
+
+ /* setup the port */
+ port = &tcu->port;
+ spin_lock_init(&port->lock);
+ port->dev = &pdev->dev;
+ port->type = PORT_TEGRA_TCU;
+ port->ops = &tegra_tcu_uart_ops;
+ port->fifosize = 1;
+ port->iotype = UPIO_MEM;
+ port->flags = UPF_BOOT_AUTOCONF;
+ port->private_data = tcu;
+
+ err = uart_add_one_port(&tcu->driver, port);
+ if (err) {
+ dev_err(&pdev->dev, "failed to add UART port: %d\n", err);
+ goto unregister_uart;
+ }
+
+ platform_set_drvdata(pdev, tcu);
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+ register_console(&tcu->console);
+#endif
+
+ return 0;
+
+unregister_uart:
+ uart_unregister_driver(&tcu->driver);
+free_rx:
+ mbox_free_channel(tcu->rx);
+free_tx:
+ mbox_free_channel(tcu->tx);
+
+ return err;
+}
+
+static int tegra_tcu_remove(struct platform_device *pdev)
+{
+ struct tegra_tcu *tcu = platform_get_drvdata(pdev);
+
+#if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE)
+ unregister_console(&tcu->console);
+#endif
+ uart_remove_one_port(&tcu->driver, &tcu->port);
+ uart_unregister_driver(&tcu->driver);
+ mbox_free_channel(tcu->rx);
+ mbox_free_channel(tcu->tx);
+
+ return 0;
+}
+
+static const struct of_device_id tegra_tcu_match[] = {
+ { .compatible = "nvidia,tegra194-tcu" },
+ { }
+};
+
+static struct platform_driver tegra_tcu_driver = {
+ .driver = {
+ .name = "tegra-tcu",
+ .of_match_table = tegra_tcu_match,
+ },
+ .probe = tegra_tcu_probe,
+ .remove = tegra_tcu_remove,
+};
+module_platform_driver(tegra_tcu_driver);
+
+MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NVIDIA Tegra Combined UART driver");
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 98d3ead..06e79c1 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -22,6 +22,7 @@
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
+#include <linux/pm_runtime.h>
#define ULITE_NAME "ttyUL"
#define ULITE_MAJOR 204
@@ -54,10 +55,17 @@
#define ULITE_CONTROL_RST_TX 0x01
#define ULITE_CONTROL_RST_RX 0x02
#define ULITE_CONTROL_IE 0x10
+#define UART_AUTOSUSPEND_TIMEOUT 3000
+
+/* Static pointer to console port */
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+static struct uart_port *console_port;
+#endif
struct uartlite_data {
const struct uartlite_reg_ops *reg_ops;
struct clk *clk;
+ struct uart_driver *ulite_uart_driver;
};
struct uartlite_reg_ops {
@@ -385,12 +393,12 @@
static void ulite_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
- struct uartlite_data *pdata = port->private_data;
-
- if (!state)
- clk_enable(pdata->clk);
- else
- clk_disable(pdata->clk);
+ if (!state) {
+ pm_runtime_get_sync(port->dev);
+ } else {
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ }
}
#ifdef CONFIG_CONSOLE_POLL
@@ -472,7 +480,7 @@
static void ulite_console_write(struct console *co, const char *s,
unsigned int count)
{
- struct uart_port *port = &ulite_ports[co->index];
+ struct uart_port *port = console_port;
unsigned long flags;
unsigned int ier;
int locked = 1;
@@ -506,10 +514,8 @@
int parity = 'n';
int flow = 'n';
- if (co->index < 0 || co->index >= ULITE_NR_UARTS)
- return -EINVAL;
- port = &ulite_ports[co->index];
+ port = console_port;
/* Has the device been initialized yet? */
if (!port->mapbase) {
@@ -541,14 +547,6 @@
.data = &ulite_uart_driver,
};
-static int __init ulite_console_init(void)
-{
- register_console(&ulite_console);
- return 0;
-}
-
-console_initcall(ulite_console_init);
-
static void early_uartlite_putc(struct uart_port *port, int c)
{
/*
@@ -660,6 +658,17 @@
dev_set_drvdata(dev, port);
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+ /*
+ * If console hasn't been found yet try to assign this port
+ * because it is required to be assigned for console setup function.
+ * If register_console() don't assign value, then console_port pointer
+ * is cleanup.
+ */
+ if (ulite_uart_driver.cons->index == -1)
+ console_port = port;
+#endif
+
/* Register the port */
rc = uart_add_one_port(&ulite_uart_driver, port);
if (rc) {
@@ -669,6 +678,12 @@
return rc;
}
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+ /* This is not port which is used for console that's why clean it up */
+ if (ulite_uart_driver.cons->index == -1)
+ console_port = NULL;
+#endif
+
return 0;
}
@@ -682,7 +697,9 @@
int rc = 0;
if (port) {
- rc = uart_remove_one_port(&ulite_uart_driver, port);
+ struct uartlite_data *pdata = port->private_data;
+
+ rc = uart_remove_one_port(pdata->ulite_uart_driver, port);
dev_set_drvdata(dev, NULL);
port->mapbase = 0;
}
@@ -700,8 +717,11 @@
{
struct uart_port *port = dev_get_drvdata(dev);
- if (port)
- uart_suspend_port(&ulite_uart_driver, port);
+ if (port) {
+ struct uartlite_data *pdata = port->private_data;
+
+ uart_suspend_port(pdata->ulite_uart_driver, port);
+ }
return 0;
}
@@ -716,17 +736,41 @@
{
struct uart_port *port = dev_get_drvdata(dev);
- if (port)
- uart_resume_port(&ulite_uart_driver, port);
+ if (port) {
+ struct uartlite_data *pdata = port->private_data;
+
+ uart_resume_port(pdata->ulite_uart_driver, port);
+ }
return 0;
}
+static int __maybe_unused ulite_runtime_suspend(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct uartlite_data *pdata = port->private_data;
+
+ clk_disable(pdata->clk);
+ return 0;
+};
+
+static int __maybe_unused ulite_runtime_resume(struct device *dev)
+{
+ struct uart_port *port = dev_get_drvdata(dev);
+ struct uartlite_data *pdata = port->private_data;
+
+ clk_enable(pdata->clk);
+ return 0;
+}
/* ---------------------------------------------------------------------
* Platform bus binding
*/
-static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume);
+static const struct dev_pm_ops ulite_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ulite_suspend, ulite_resume)
+ SET_RUNTIME_PM_OPS(ulite_runtime_suspend,
+ ulite_runtime_resume, NULL)
+};
#if defined(CONFIG_OF)
/* Match table for of_platform binding */
@@ -751,6 +795,22 @@
if (prop)
id = be32_to_cpup(prop);
#endif
+ if (id < 0) {
+ /* Look for a serialN alias */
+ id = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (id < 0)
+ id = 0;
+ }
+
+ if (!ulite_uart_driver.state) {
+ dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n");
+ ret = uart_register_driver(&ulite_uart_driver);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register driver\n");
+ return ret;
+ }
+ }
+
pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data),
GFP_KERNEL);
if (!pdata)
@@ -776,22 +836,38 @@
pdata->clk = NULL;
}
- ret = clk_prepare(pdata->clk);
+ pdata->ulite_uart_driver = &ulite_uart_driver;
+ ret = clk_prepare_enable(pdata->clk);
if (ret) {
dev_err(&pdev->dev, "Failed to prepare clock\n");
return ret;
}
- return ulite_assign(&pdev->dev, id, res->start, irq, pdata);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata);
+
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return ret;
}
static int ulite_remove(struct platform_device *pdev)
{
struct uart_port *port = dev_get_drvdata(&pdev->dev);
struct uartlite_data *pdata = port->private_data;
+ int rc;
- clk_disable_unprepare(pdata->clk);
- return ulite_release(&pdev->dev);
+ clk_unprepare(pdata->clk);
+ rc = ulite_release(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ return rc;
}
/* work with hotplug and coldplug */
@@ -813,31 +889,16 @@
static int __init ulite_init(void)
{
- int ret;
-
- pr_debug("uartlite: calling uart_register_driver()\n");
- ret = uart_register_driver(&ulite_uart_driver);
- if (ret)
- goto err_uart;
pr_debug("uartlite: calling platform_driver_register()\n");
- ret = platform_driver_register(&ulite_platform_driver);
- if (ret)
- goto err_plat;
-
- return 0;
-
-err_plat:
- uart_unregister_driver(&ulite_uart_driver);
-err_uart:
- pr_err("registering uartlite driver failed: err=%i\n", ret);
- return ret;
+ return platform_driver_register(&ulite_platform_driver);
}
static void __exit ulite_exit(void)
{
platform_driver_unregister(&ulite_platform_driver);
- uart_unregister_driver(&ulite_uart_driver);
+ if (ulite_uart_driver.state)
+ uart_unregister_driver(&ulite_uart_driver);
}
module_init(ulite_init);
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 2b6376e..a0555ae 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1081,7 +1081,7 @@
}
/* UART operations
*
- * Details on these functions can be found in Documentation/serial/driver
+ * Details on these functions can be found in Documentation/driver-api/serial/driver.rst
*/
static const struct uart_ops qe_uart_pops = {
.tx_empty = qe_uart_tx_empty,
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index a48f19b..4e55bc3 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -26,23 +26,23 @@
#include <linux/of.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
#define CDNS_UART_TTY_NAME "ttyPS"
#define CDNS_UART_NAME "xuartps"
-#define CDNS_UART_MAJOR 0 /* use dynamic node allocation */
-#define CDNS_UART_MINOR 0 /* works best with devtmpfs */
-#define CDNS_UART_NR_PORTS 2
#define CDNS_UART_FIFO_SIZE 64 /* FIFO size */
#define CDNS_UART_REGISTER_SPACE 0x1000
+#define TX_TIMEOUT 500000
/* Rx Trigger level */
static int rx_trigger_level = 56;
-module_param(rx_trigger_level, uint, S_IRUGO);
+static int uartps_major;
+module_param(rx_trigger_level, uint, 0444);
MODULE_PARM_DESC(rx_trigger_level, "Rx trigger level, 1-63 bytes");
/* Rx Timeout */
static int rx_timeout = 10;
-module_param(rx_timeout, uint, S_IRUGO);
+module_param(rx_timeout, uint, 0444);
MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");
/* Register offsets for the UART. */
@@ -125,7 +125,7 @@
#define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */
#define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */
#define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */
-#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */
+#define CDNS_UART_IXR_RXMASK 0x000021e7 /* Valid RX bit mask */
/*
* Do not enable parity error interrupt for the following
@@ -180,7 +180,9 @@
* @port: Pointer to the UART port
* @uartclk: Reference clock
* @pclk: APB clock
+ * @cdns_uart_driver: Pointer to UART driver
* @baud: Current baud rate
+ * @id: Port ID
* @clk_rate_change_nb: Notifier block for clock changes
* @quirks: Flags for RXBS support.
*/
@@ -188,15 +190,18 @@
struct uart_port *port;
struct clk *uartclk;
struct clk *pclk;
+ struct uart_driver *cdns_uart_driver;
unsigned int baud;
+ int id;
struct notifier_block clk_rate_change_nb;
u32 quirks;
+ bool cts_override;
};
struct cdns_platform_data {
u32 quirks;
};
#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
- clk_rate_change_nb);
+ clk_rate_change_nb)
/**
* cdns_uart_handle_rx - Handle the received bytes along with Rx errors.
@@ -309,15 +314,16 @@
} else {
numbytes = port->fifosize;
while (numbytes && !uart_circ_empty(&port->state->xmit) &&
- !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
+ !(readl(port->membase + CDNS_UART_SR) &
+ CDNS_UART_SR_TXFULL)) {
/*
* Get the data from the UART circular buffer
* and write it to the cdns_uart's TX_FIFO
* register.
*/
writel(
- port->state->xmit.buf[port->state->xmit.
- tail], port->membase + CDNS_UART_FIFO);
+ port->state->xmit.buf[port->state->xmit.tail],
+ port->membase + CDNS_UART_FIFO);
port->icount.tx++;
@@ -362,7 +368,13 @@
cdns_uart_handle_tx(dev_id);
isrstatus &= ~CDNS_UART_IXR_TXEMPTY;
}
- if (isrstatus & CDNS_UART_IXR_MASK)
+
+ /*
+ * Skip RX processing if RX is disabled as RXEMPTY will never be set
+ * as read bytes will not be removed from the FIFO.
+ */
+ if (isrstatus & CDNS_UART_IXR_RXMASK &&
+ !(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_RX_DIS))
cdns_uart_handle_rx(dev_id, isrstatus);
spin_unlock(&port->lock);
@@ -675,18 +687,21 @@
unsigned int cval = 0;
unsigned int baud, minbaud, maxbaud;
unsigned long flags;
- unsigned int ctrl_reg, mode_reg;
-
- spin_lock_irqsave(&port->lock, flags);
+ unsigned int ctrl_reg, mode_reg, val;
+ int err;
/* Wait for the transmit FIFO to empty before making changes */
if (!(readl(port->membase + CDNS_UART_CR) &
CDNS_UART_CR_TX_DIS)) {
- while (!(readl(port->membase + CDNS_UART_SR) &
- CDNS_UART_SR_TXEMPTY)) {
- cpu_relax();
+ err = readl_poll_timeout(port->membase + CDNS_UART_SR,
+ val, (val & CDNS_UART_SR_TXEMPTY),
+ 1000, TX_TIMEOUT);
+ if (err) {
+ dev_err(port->dev, "timed out waiting for tx empty");
+ return;
}
}
+ spin_lock_irqsave(&port->lock, flags);
/* Disable the TX and RX to set baud rate */
ctrl_reg = readl(port->membase + CDNS_UART_CR);
@@ -992,6 +1007,11 @@
*/
static unsigned int cdns_uart_get_mctrl(struct uart_port *port)
{
+ struct cdns_uart *cdns_uart_data = port->private_data;
+
+ if (cdns_uart_data->cts_override)
+ return 0;
+
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
}
@@ -999,17 +1019,20 @@
{
u32 val;
u32 mode_reg;
+ struct cdns_uart *cdns_uart_data = port->private_data;
+
+ if (cdns_uart_data->cts_override)
+ return;
val = readl(port->membase + CDNS_UART_MODEMCR);
mode_reg = readl(port->membase + CDNS_UART_MR);
- val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR);
+ val &= ~(CDNS_UART_MODEMCR_RTS | CDNS_UART_MODEMCR_DTR |
+ CDNS_UART_MODEMCR_FCM);
mode_reg &= ~CDNS_UART_MR_CHMODE_MASK;
- if (mctrl & TIOCM_RTS)
- val |= CDNS_UART_MODEMCR_RTS;
- if (mctrl & TIOCM_DTR)
- val |= CDNS_UART_MODEMCR_DTR;
+ if (mctrl & TIOCM_RTS || mctrl & TIOCM_DTR)
+ val |= CDNS_UART_MODEMCR_FCM;
if (mctrl & TIOCM_LOOP)
mode_reg |= CDNS_UART_MR_CHMODE_L_LOOP;
else
@@ -1056,8 +1079,6 @@
cpu_relax();
spin_unlock_irqrestore(&port->lock, flags);
-
- return;
}
#endif
@@ -1173,7 +1194,7 @@
unsigned int count)
{
struct uart_port *port = console_port;
- unsigned long flags;
+ unsigned long flags = 0;
unsigned int imr, ctrl;
int locked = 1;
@@ -1217,7 +1238,7 @@
*
* Return: 0 on success, negative errno otherwise.
*/
-static int __init cdns_uart_console_setup(struct console *co, char *options)
+static int cdns_uart_console_setup(struct console *co, char *options)
{
struct uart_port *port = console_port;
@@ -1237,32 +1258,8 @@
return uart_set_options(port, co, baud, parity, bits, flow);
}
-
-static struct uart_driver cdns_uart_uart_driver;
-
-static struct console cdns_uart_console = {
- .name = CDNS_UART_TTY_NAME,
- .write = cdns_uart_console_write,
- .device = uart_console_device,
- .setup = cdns_uart_console_setup,
- .flags = CON_PRINTBUFFER,
- .index = -1, /* Specified on the cmdline (e.g. console=ttyPS ) */
- .data = &cdns_uart_uart_driver,
-};
#endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */
-static struct uart_driver cdns_uart_uart_driver = {
- .owner = THIS_MODULE,
- .driver_name = CDNS_UART_NAME,
- .dev_name = CDNS_UART_TTY_NAME,
- .major = CDNS_UART_MAJOR,
- .minor = CDNS_UART_MINOR,
- .nr = CDNS_UART_NR_PORTS,
-#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
- .cons = &cdns_uart_console,
-#endif
-};
-
#ifdef CONFIG_PM_SLEEP
/**
* cdns_uart_suspend - suspend event
@@ -1273,24 +1270,12 @@
static int cdns_uart_suspend(struct device *device)
{
struct uart_port *port = dev_get_drvdata(device);
- struct tty_struct *tty;
- struct device *tty_dev;
- int may_wake = 0;
+ struct cdns_uart *cdns_uart = port->private_data;
+ int may_wake;
- /* Get the tty which could be NULL so don't assume it's valid */
- tty = tty_port_tty_get(&port->state->port);
- if (tty) {
- tty_dev = tty->dev;
- may_wake = device_may_wakeup(tty_dev);
- tty_kref_put(tty);
- }
+ may_wake = device_may_wakeup(device);
- /*
- * Call the API provided in serial_core.c file which handles
- * the suspend.
- */
- uart_suspend_port(&cdns_uart_uart_driver, port);
- if (!(console_suspend_enabled && !may_wake)) {
+ if (console_suspend_enabled && uart_console(port) && may_wake) {
unsigned long flags = 0;
spin_lock_irqsave(&port->lock, flags);
@@ -1305,7 +1290,11 @@
spin_unlock_irqrestore(&port->lock, flags);
}
- return 0;
+ /*
+ * Call the API provided in serial_core.c file which handles
+ * the suspend.
+ */
+ return uart_suspend_port(cdns_uart->cdns_uart_driver, port);
}
/**
@@ -1317,23 +1306,14 @@
static int cdns_uart_resume(struct device *device)
{
struct uart_port *port = dev_get_drvdata(device);
+ struct cdns_uart *cdns_uart = port->private_data;
unsigned long flags = 0;
u32 ctrl_reg;
- struct tty_struct *tty;
- struct device *tty_dev;
- int may_wake = 0;
+ int may_wake;
- /* Get the tty which could be NULL so don't assume it's valid */
- tty = tty_port_tty_get(&port->state->port);
- if (tty) {
- tty_dev = tty->dev;
- may_wake = device_may_wakeup(tty_dev);
- tty_kref_put(tty);
- }
+ may_wake = device_may_wakeup(device);
- if (console_suspend_enabled && !may_wake) {
- struct cdns_uart *cdns_uart = port->private_data;
-
+ if (console_suspend_enabled && uart_console(port) && !may_wake) {
clk_enable(cdns_uart->pclk);
clk_enable(cdns_uart->uartclk);
@@ -1367,7 +1347,7 @@
spin_unlock_irqrestore(&port->lock, flags);
}
- return uart_resume_port(&cdns_uart_uart_driver, port);
+ return uart_resume_port(cdns_uart->cdns_uart_driver, port);
}
#endif /* ! CONFIG_PM_SLEEP */
static int __maybe_unused cdns_runtime_suspend(struct device *dev)
@@ -1409,6 +1389,90 @@
};
MODULE_DEVICE_TABLE(of, cdns_uart_of_match);
+/*
+ * Maximum number of instances without alias IDs but if there is alias
+ * which target "< MAX_UART_INSTANCES" range this ID can't be used.
+ */
+#define MAX_UART_INSTANCES 32
+
+/* Stores static aliases list */
+static DECLARE_BITMAP(alias_bitmap, MAX_UART_INSTANCES);
+static int alias_bitmap_initialized;
+
+/* Stores actual bitmap of allocated IDs with alias IDs together */
+static DECLARE_BITMAP(bitmap, MAX_UART_INSTANCES);
+/* Protect bitmap operations to have unique IDs */
+static DEFINE_MUTEX(bitmap_lock);
+
+static int cdns_get_id(struct platform_device *pdev)
+{
+ int id, ret;
+
+ mutex_lock(&bitmap_lock);
+
+ /* Alias list is stable that's why get alias bitmap only once */
+ if (!alias_bitmap_initialized) {
+ ret = of_alias_get_alias_list(cdns_uart_of_match, "serial",
+ alias_bitmap, MAX_UART_INSTANCES);
+ if (ret && ret != -EOVERFLOW) {
+ mutex_unlock(&bitmap_lock);
+ return ret;
+ }
+
+ alias_bitmap_initialized++;
+ }
+
+ /* Make sure that alias ID is not taken by instance without alias */
+ bitmap_or(bitmap, bitmap, alias_bitmap, MAX_UART_INSTANCES);
+
+ dev_dbg(&pdev->dev, "Alias bitmap: %*pb\n",
+ MAX_UART_INSTANCES, bitmap);
+
+ /* Look for a serialN alias */
+ id = of_alias_get_id(pdev->dev.of_node, "serial");
+ if (id < 0) {
+ dev_warn(&pdev->dev,
+ "No serial alias passed. Using the first free id\n");
+
+ /*
+ * Start with id 0 and check if there is no serial0 alias
+ * which points to device which is compatible with this driver.
+ * If alias exists then try next free position.
+ */
+ id = 0;
+
+ for (;;) {
+ dev_info(&pdev->dev, "Checking id %d\n", id);
+ id = find_next_zero_bit(bitmap, MAX_UART_INSTANCES, id);
+
+ /* No free empty instance */
+ if (id == MAX_UART_INSTANCES) {
+ dev_err(&pdev->dev, "No free ID\n");
+ mutex_unlock(&bitmap_lock);
+ return -EINVAL;
+ }
+
+ dev_dbg(&pdev->dev, "The empty id is %d\n", id);
+ /* Check if ID is empty */
+ if (!test_and_set_bit(id, bitmap)) {
+ /* Break the loop if bit is taken */
+ dev_dbg(&pdev->dev,
+ "Selected ID %d allocation passed\n",
+ id);
+ break;
+ }
+ dev_dbg(&pdev->dev,
+ "Selected ID %d allocation failed\n", id);
+ /* if taking bit fails then try next one */
+ id++;
+ }
+ }
+
+ mutex_unlock(&bitmap_lock);
+
+ return id;
+}
+
/**
* cdns_uart_probe - Platform driver probe
* @pdev: Pointer to the platform device structure
@@ -1417,11 +1481,16 @@
*/
static int cdns_uart_probe(struct platform_device *pdev)
{
- int rc, id, irq;
+ int rc, irq;
struct uart_port *port;
struct resource *res;
struct cdns_uart *cdns_uart_data;
const struct of_device_id *match;
+ struct uart_driver *cdns_uart_uart_driver;
+ char *driver_name;
+#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
+ struct console *cdns_uart_console;
+#endif
cdns_uart_data = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_data),
GFP_KERNEL);
@@ -1431,6 +1500,65 @@
if (!port)
return -ENOMEM;
+ cdns_uart_uart_driver = devm_kzalloc(&pdev->dev,
+ sizeof(*cdns_uart_uart_driver),
+ GFP_KERNEL);
+ if (!cdns_uart_uart_driver)
+ return -ENOMEM;
+
+ cdns_uart_data->id = cdns_get_id(pdev);
+ if (cdns_uart_data->id < 0)
+ return cdns_uart_data->id;
+
+ /* There is a need to use unique driver name */
+ driver_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s%d",
+ CDNS_UART_NAME, cdns_uart_data->id);
+ if (!driver_name) {
+ rc = -ENOMEM;
+ goto err_out_id;
+ }
+
+ cdns_uart_uart_driver->owner = THIS_MODULE;
+ cdns_uart_uart_driver->driver_name = driver_name;
+ cdns_uart_uart_driver->dev_name = CDNS_UART_TTY_NAME;
+ cdns_uart_uart_driver->major = uartps_major;
+ cdns_uart_uart_driver->minor = cdns_uart_data->id;
+ cdns_uart_uart_driver->nr = 1;
+
+#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
+ cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console),
+ GFP_KERNEL);
+ if (!cdns_uart_console) {
+ rc = -ENOMEM;
+ goto err_out_id;
+ }
+
+ strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME,
+ sizeof(cdns_uart_console->name));
+ cdns_uart_console->index = cdns_uart_data->id;
+ cdns_uart_console->write = cdns_uart_console_write;
+ cdns_uart_console->device = uart_console_device;
+ cdns_uart_console->setup = cdns_uart_console_setup;
+ cdns_uart_console->flags = CON_PRINTBUFFER;
+ cdns_uart_console->data = cdns_uart_uart_driver;
+ cdns_uart_uart_driver->cons = cdns_uart_console;
+#endif
+
+ rc = uart_register_driver(cdns_uart_uart_driver);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Failed to register driver\n");
+ goto err_out_id;
+ }
+
+ cdns_uart_data->cdns_uart_driver = cdns_uart_uart_driver;
+
+ /*
+ * Setting up proper name_base needs to be done after uart
+ * registration because tty_driver structure is not filled.
+ * name_base is 0 by default.
+ */
+ cdns_uart_uart_driver->tty_driver->name_base = cdns_uart_data->id;
+
match = of_match_node(cdns_uart_of_match, pdev->dev.of_node);
if (match && match->data) {
const struct cdns_platform_data *data = match->data;
@@ -1439,31 +1567,39 @@
}
cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (PTR_ERR(cdns_uart_data->pclk) == -EPROBE_DEFER) {
+ rc = PTR_ERR(cdns_uart_data->pclk);
+ goto err_out_unregister_driver;
+ }
+
if (IS_ERR(cdns_uart_data->pclk)) {
cdns_uart_data->pclk = devm_clk_get(&pdev->dev, "aper_clk");
- if (!IS_ERR(cdns_uart_data->pclk))
- dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
- }
- if (IS_ERR(cdns_uart_data->pclk)) {
- dev_err(&pdev->dev, "pclk clock not found.\n");
- return PTR_ERR(cdns_uart_data->pclk);
+ if (IS_ERR(cdns_uart_data->pclk)) {
+ rc = PTR_ERR(cdns_uart_data->pclk);
+ goto err_out_unregister_driver;
+ }
+ dev_err(&pdev->dev, "clock name 'aper_clk' is deprecated.\n");
}
cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk");
+ if (PTR_ERR(cdns_uart_data->uartclk) == -EPROBE_DEFER) {
+ rc = PTR_ERR(cdns_uart_data->uartclk);
+ goto err_out_unregister_driver;
+ }
+
if (IS_ERR(cdns_uart_data->uartclk)) {
cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "ref_clk");
- if (!IS_ERR(cdns_uart_data->uartclk))
- dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
- }
- if (IS_ERR(cdns_uart_data->uartclk)) {
- dev_err(&pdev->dev, "uart_clk clock not found.\n");
- return PTR_ERR(cdns_uart_data->uartclk);
+ if (IS_ERR(cdns_uart_data->uartclk)) {
+ rc = PTR_ERR(cdns_uart_data->uartclk);
+ goto err_out_unregister_driver;
+ }
+ dev_err(&pdev->dev, "clock name 'ref_clk' is deprecated.\n");
}
rc = clk_prepare_enable(cdns_uart_data->pclk);
if (rc) {
dev_err(&pdev->dev, "Unable to enable pclk clock.\n");
- return rc;
+ goto err_out_unregister_driver;
}
rc = clk_prepare_enable(cdns_uart_data->uartclk);
if (rc) {
@@ -1490,28 +1626,14 @@
&cdns_uart_data->clk_rate_change_nb))
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
#endif
- /* Look for a serialN alias */
- id = of_alias_get_id(pdev->dev.of_node, "serial");
- if (id < 0)
- id = 0;
-
- if (id >= CDNS_UART_NR_PORTS) {
- dev_err(&pdev->dev, "Cannot get uart_port structure\n");
- rc = -ENODEV;
- goto err_out_notif_unreg;
- }
/* At this point, we've got an empty uart_port struct, initialize it */
spin_lock_init(&port->lock);
- port->membase = NULL;
- port->irq = 0;
port->type = PORT_UNKNOWN;
port->iotype = UPIO_MEM32;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &cdns_uart_ops;
port->fifosize = CDNS_UART_FIFO_SIZE;
- port->line = id;
- port->dev = NULL;
/*
* Register the port.
@@ -1530,6 +1652,7 @@
pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
+ device_init_wakeup(port->dev, true);
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/*
@@ -1538,11 +1661,11 @@
* If register_console() don't assign value, then console_port pointer
* is cleanup.
*/
- if (cdns_uart_uart_driver.cons->index == -1)
+ if (!console_port)
console_port = port;
#endif
- rc = uart_add_one_port(&cdns_uart_uart_driver, port);
+ rc = uart_add_one_port(cdns_uart_uart_driver, port);
if (rc) {
dev_err(&pdev->dev,
"uart_add_one_port() failed; err=%i\n", rc);
@@ -1551,17 +1674,20 @@
#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
/* This is not port which is used for console that's why clean it up */
- if (cdns_uart_uart_driver.cons->index == -1)
+ if (console_port == port &&
+ !(cdns_uart_uart_driver->cons->flags & CON_ENABLED))
console_port = NULL;
#endif
+ uartps_major = cdns_uart_uart_driver->tty_driver->major;
+ cdns_uart_data->cts_override = of_property_read_bool(pdev->dev.of_node,
+ "cts-override");
return 0;
err_out_pm_disable:
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
-err_out_notif_unreg:
#ifdef CONFIG_COMMON_CLK
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
@@ -1570,7 +1696,13 @@
clk_disable_unprepare(cdns_uart_data->uartclk);
err_out_clk_dis_pclk:
clk_disable_unprepare(cdns_uart_data->pclk);
-
+err_out_unregister_driver:
+ uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
+err_out_id:
+ mutex_lock(&bitmap_lock);
+ if (cdns_uart_data->id < MAX_UART_INSTANCES)
+ clear_bit(cdns_uart_data->id, bitmap);
+ mutex_unlock(&bitmap_lock);
return rc;
}
@@ -1591,13 +1723,31 @@
clk_notifier_unregister(cdns_uart_data->uartclk,
&cdns_uart_data->clk_rate_change_nb);
#endif
- rc = uart_remove_one_port(&cdns_uart_uart_driver, port);
+ rc = uart_remove_one_port(cdns_uart_data->cdns_uart_driver, port);
port->mapbase = 0;
+ mutex_lock(&bitmap_lock);
+ if (cdns_uart_data->id < MAX_UART_INSTANCES)
+ clear_bit(cdns_uart_data->id, bitmap);
+ mutex_unlock(&bitmap_lock);
clk_disable_unprepare(cdns_uart_data->uartclk);
clk_disable_unprepare(cdns_uart_data->pclk);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
+
+#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
+ if (console_port == port)
+ console_port = NULL;
+#endif
+
+ /* If this is last instance major number should be initialized */
+ mutex_lock(&bitmap_lock);
+ if (bitmap_empty(bitmap, MAX_UART_INSTANCES))
+ uartps_major = 0;
+ mutex_unlock(&bitmap_lock);
+
+ uart_unregister_driver(cdns_uart_data->cdns_uart_driver);
return rc;
}
@@ -1608,33 +1758,20 @@
.name = CDNS_UART_NAME,
.of_match_table = cdns_uart_of_match,
.pm = &cdns_uart_dev_pm_ops,
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_XILINX_PS_UART),
},
};
static int __init cdns_uart_init(void)
{
- int retval = 0;
-
- /* Register the cdns_uart driver with the serial core */
- retval = uart_register_driver(&cdns_uart_uart_driver);
- if (retval)
- return retval;
-
/* Register the platform driver */
- retval = platform_driver_register(&cdns_uart_platform_driver);
- if (retval)
- uart_unregister_driver(&cdns_uart_uart_driver);
-
- return retval;
+ return platform_driver_register(&cdns_uart_platform_driver);
}
static void __exit cdns_uart_exit(void)
{
/* Unregister the platform driver */
platform_driver_unregister(&cdns_uart_platform_driver);
-
- /* Unregister the cdns_uart driver */
- uart_unregister_driver(&cdns_uart_uart_driver);
}
arch_initcall(cdns_uart_init);
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index fbdf4d0..84f26e4 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -2959,8 +2959,7 @@
if (mgsl_paranoia_check(info, tty->name, "mgsl_ioctl"))
return -ENODEV;
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCMIWAIT)) {
+ if (cmd != TIOCMIWAIT) {
if (tty_io_error(tty))
return -EIO;
}
@@ -4326,41 +4325,6 @@
return 0;
}
-/* enumerate user specified ISA adapters
- */
-static void mgsl_enum_isa_devices(void)
-{
- struct mgsl_struct *info;
- int i;
-
- /* Check for user specified ISA devices */
-
- for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){
- if ( debug_level >= DEBUG_LEVEL_INFO )
- printk("ISA device specified io=%04X,irq=%d,dma=%d\n",
- io[i], irq[i], dma[i] );
-
- info = mgsl_allocate_device();
- if ( !info ) {
- /* error allocating device instance data */
- if ( debug_level >= DEBUG_LEVEL_ERROR )
- printk( "can't allocate device instance data.\n");
- continue;
- }
-
- /* Copy user configuration info to device instance data */
- info->io_base = (unsigned int)io[i];
- info->irq_level = (unsigned int)irq[i];
- info->irq_level = irq_canonicalize(info->irq_level);
- info->dma_level = (unsigned int)dma[i];
- info->bus_type = MGSL_BUS_TYPE_ISA;
- info->io_addr_size = 16;
- info->irq_flags = 0;
-
- mgsl_add_device( info );
- }
-}
-
static void synclink_cleanup(void)
{
int rc;
@@ -4404,7 +4368,6 @@
printk("%s %s\n", driver_name, driver_version);
- mgsl_enum_isa_devices();
if ((rc = pci_register_driver(&synclink_pci_driver)) < 0)
printk("%s:failed to register PCI driver, error=%d\n",__FILE__,rc);
else
@@ -5026,12 +4989,6 @@
info->mbre_bit = BIT8;
outw( BIT8, info->io_base ); /* set Master Bus Enable (DCAR) */
- if (info->bus_type == MGSL_BUS_TYPE_ISA) {
- /* Enable DMAEN (Port 7, Bit 14) */
- /* This connects the DMA request signal to the ISA bus */
- usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14));
- }
-
/* DMA Control Register (DCR)
*
* <15..14> 10 Priority mode = Alternating Tx/Rx
@@ -6008,12 +5965,6 @@
usc_EnableMasterIrqBit( info );
- if (info->bus_type == MGSL_BUS_TYPE_ISA) {
- /* Enable INTEN (Port 6, Bit12) */
- /* This connects the IRQ request signal to the ISA bus */
- usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
- }
-
if (info->params.loopback) {
info->loopback_bits = 0x300;
outw(0x0300, info->io_base + CCAR);
@@ -6108,12 +6059,6 @@
usc_loopback_frame( info );
usc_set_sdlc_mode( info );
- if (info->bus_type == MGSL_BUS_TYPE_ISA) {
- /* Enable INTEN (Port 6, Bit12) */
- /* This connects the IRQ request signal to the ISA bus */
- usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12));
- }
-
usc_enable_aux_clock(info, info->params.clock_speed);
if (info->params.loopback)
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index a940865..e8a9047 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -1029,8 +1029,7 @@
return -ENODEV;
DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd));
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCMIWAIT)) {
+ if (cmd != TIOCMIWAIT) {
if (tty_io_error(tty))
return -EIO;
}
@@ -1186,14 +1185,13 @@
unsigned int cmd, unsigned long arg)
{
struct slgt_info *info = tty->driver_data;
- int rc = -ENOIOCTLCMD;
+ int rc;
if (sanity_check(info, tty->name, "compat_ioctl"))
return -ENODEV;
DBGINFO(("%s compat_ioctl() cmd=%08X\n", info->device_name, cmd));
switch (cmd) {
-
case MGSL_IOCSPARAMS32:
rc = set_params32(info, compat_ptr(arg));
break;
@@ -1213,18 +1211,11 @@
case MGSL_IOCWAITGPIO:
case MGSL_IOCGXSYNC:
case MGSL_IOCGXCTRL:
- case MGSL_IOCSTXIDLE:
- case MGSL_IOCTXENABLE:
- case MGSL_IOCRXENABLE:
- case MGSL_IOCTXABORT:
- case TIOCMIWAIT:
- case MGSL_IOCSIF:
- case MGSL_IOCSXSYNC:
- case MGSL_IOCSXCTRL:
- rc = ioctl(tty, cmd, arg);
+ rc = ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
break;
+ default:
+ rc = ioctl(tty, cmd, arg);
}
-
DBGINFO(("%s compat_ioctl() cmd=%08X rc=%d\n", info->device_name, cmd, rc));
return rc;
}
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index 1e4d5b9..fcb91bf 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -1259,8 +1259,7 @@
if (sanity_check(info, tty->name, "ioctl"))
return -ENODEV;
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCMIWAIT)) {
+ if (cmd != TIOCMIWAIT) {
if (tty_io_error(tty))
return -EIO;
}
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 06ed20d..573b205 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -134,17 +134,10 @@
static void sysrq_handle_crash(int key)
{
- char *killer = NULL;
-
- /* we need to release the RCU read lock here,
- * otherwise we get an annoying
- * 'BUG: sleeping function called from invalid context'
- * complaint from the kernel before the panic.
- */
+ /* release the RCU read lock before crashing */
rcu_read_unlock();
- panic_on_oops = 1; /* force panic */
- wmb();
- *killer = 1;
+
+ panic("sysrq triggered crash\n");
}
static struct sysrq_key_op sysrq_crash_op = {
.handler = sysrq_handle_crash,
@@ -215,7 +208,7 @@
#endif
#ifdef CONFIG_SMP
-static DEFINE_SPINLOCK(show_lock);
+static DEFINE_RAW_SPINLOCK(show_lock);
static void showacpu(void *dummy)
{
@@ -225,10 +218,10 @@
if (idle_cpu(smp_processor_id()))
return;
- spin_lock_irqsave(&show_lock, flags);
+ raw_spin_lock_irqsave(&show_lock, flags);
pr_info("CPU%d:\n", smp_processor_id());
show_stack(NULL, NULL);
- spin_unlock_irqrestore(&show_lock, flags);
+ raw_spin_unlock_irqrestore(&show_lock, flags);
}
static void sysrq_showregs_othercpus(struct work_struct *dummy)
@@ -348,7 +341,7 @@
if (is_global_init(p))
continue;
- do_send_sig_info(sig, SEND_SIG_FORCED, p, PIDTYPE_MAX);
+ do_send_sig_info(sig, SEND_SIG_PRIV, p, PIDTYPE_MAX);
}
read_unlock(&tasklist_lock);
}
@@ -534,8 +527,12 @@
{
struct sysrq_key_op *op_p;
int orig_log_level;
+ int orig_suppress_printk;
int i;
+ orig_suppress_printk = suppress_printk;
+ suppress_printk = 0;
+
rcu_sysrq_start();
rcu_read_lock();
/*
@@ -546,7 +543,6 @@
*/
orig_log_level = console_loglevel;
console_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
- pr_info("SysRq : ");
op_p = __sysrq_get_key_op(key);
if (op_p) {
@@ -555,14 +551,15 @@
* should not) and is the invoked operation enabled?
*/
if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
- pr_cont("%s\n", op_p->action_msg);
+ pr_info("%s\n", op_p->action_msg);
console_loglevel = orig_log_level;
op_p->handler(key);
} else {
- pr_cont("This sysrq operation is disabled.\n");
+ pr_info("This sysrq operation is disabled.\n");
+ console_loglevel = orig_log_level;
}
} else {
- pr_cont("HELP : ");
+ pr_info("HELP : ");
/* Only print the help msg once per handler */
for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) {
if (sysrq_key_table[i]) {
@@ -581,6 +578,8 @@
}
rcu_read_unlock();
rcu_sysrq_end();
+
+ suppress_printk = orig_suppress_printk;
}
void handle_sysrq(int key)
@@ -660,8 +659,7 @@
state->reset_requested = true;
- ksys_sync();
- kernel_restart(NULL);
+ orderly_reboot();
}
static void sysrq_handle_reset_request(struct sysrq_state *state)
@@ -736,6 +734,8 @@
/* Get reset timeout if any. */
of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms);
+
+ of_node_put(np);
}
#else
static void sysrq_of_get_keyreset_config(void)
diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c
index 50f567b..9f906a5 100644
--- a/drivers/tty/tty_audit.c
+++ b/drivers/tty/tty_audit.c
@@ -61,20 +61,19 @@
unsigned char *data, size_t size)
{
struct audit_buffer *ab;
- struct task_struct *tsk = current;
- pid_t pid = task_pid_nr(tsk);
- uid_t uid = from_kuid(&init_user_ns, task_uid(tsk));
- uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk));
- unsigned int sessionid = audit_get_sessionid(tsk);
+ pid_t pid = task_pid_nr(current);
+ uid_t uid = from_kuid(&init_user_ns, task_uid(current));
+ uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(current));
+ unsigned int sessionid = audit_get_sessionid(current);
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY);
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_TTY);
if (ab) {
- char name[sizeof(tsk->comm)];
+ char name[sizeof(current->comm)];
audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d"
" minor=%d comm=", description, pid, uid,
loginuid, sessionid, MAJOR(dev), MINOR(dev));
- get_task_comm(name, tsk);
+ get_task_comm(name, current);
audit_log_untrustedstring(ab, name);
audit_log_format(ab, " data=");
audit_log_n_hex(ab, data, size);
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index ae3ce33..ec145a5 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -26,7 +26,7 @@
* Byte threshold to limit memory consumption for flip buffers.
* The actual memory limit is > 2x this amount.
*/
-#define TTYB_DEFAULT_MEM_LIMIT 65536
+#define TTYB_DEFAULT_MEM_LIMIT (640 * 1024UL)
/*
* We default to dicing tty buffer allocations to this many characters
@@ -118,9 +118,12 @@
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *p, *next;
struct llist_node *llist;
+ unsigned int freed = 0;
+ int still_used;
while ((p = buf->head) != NULL) {
buf->head = p->next;
+ freed += p->size;
if (p->size > 0)
kfree(p);
}
@@ -132,7 +135,9 @@
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
- atomic_set(&buf->mem_used, 0);
+ still_used = atomic_xchg(&buf->mem_used, 0);
+ WARN(still_used != freed, "we still have not freed %d bytes!",
+ still_used - freed);
}
/**
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index d6f42b5..802c121 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -97,6 +97,7 @@
#include <linux/seq_file.h>
#include <linux/serial.h>
#include <linux/ratelimit.h>
+#include <linux/compat.h>
#include <linux/uaccess.h>
@@ -512,6 +513,8 @@
static DEFINE_SPINLOCK(redirect_lock);
static struct file *redirect;
+extern void tty_sysctl_init(void);
+
/**
* tty_wakeup - request more data
* @tty: terminal
@@ -1170,7 +1173,7 @@
* tty_init_termios - helper for termios setup
* @tty: the tty to set up
*
- * Initialise the termios structures for this tty. Thus runs under
+ * Initialise the termios structure for this tty. This runs under
* the tty_mutex currently so we can be relaxed about ordering.
*/
@@ -1255,7 +1258,8 @@
static int tty_reopen(struct tty_struct *tty)
{
struct tty_driver *driver = tty->driver;
- int retval;
+ struct tty_ldisc *ld;
+ int retval = 0;
if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER)
@@ -1267,14 +1271,21 @@
if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
return -EBUSY;
- tty->count++;
+ ld = tty_ldisc_ref_wait(tty);
+ if (ld) {
+ tty_ldisc_deref(ld);
+ } else {
+ retval = tty_ldisc_lock(tty, 5 * HZ);
+ if (retval)
+ return retval;
- if (tty->ldisc)
- return 0;
+ if (!tty->ldisc)
+ retval = tty_ldisc_reinit(tty, tty->termios.c_line);
+ tty_ldisc_unlock(tty);
+ }
- retval = tty_ldisc_reinit(tty, tty->termios.c_line);
- if (retval)
- tty->count--;
+ if (retval == 0)
+ tty->count++;
return retval;
}
@@ -1826,7 +1837,7 @@
static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
int *index)
{
- struct tty_driver *driver;
+ struct tty_driver *driver = NULL;
switch (device) {
#ifdef CONFIG_VT
@@ -1847,6 +1858,8 @@
break;
}
}
+ if (driver)
+ tty_driver_kref_put(driver);
return ERR_PTR(-ENODEV);
}
default:
@@ -2180,7 +2193,8 @@
ld = tty_ldisc_ref_wait(tty);
if (!ld)
return -EIO;
- ld->ops->receive_buf(tty, &ch, &mbz, 1);
+ if (ld->ops->receive_buf)
+ ld->ops->receive_buf(tty, &ch, &mbz, 1);
tty_ldisc_deref(ld);
return 0;
}
@@ -2300,34 +2314,6 @@
}
/**
- * fionbio - non blocking ioctl
- * @file: file to set blocking value
- * @p: user parameter
- *
- * Historical tty interfaces had a blocking control ioctl before
- * the generic functionality existed. This piece of history is preserved
- * in the expected tty API of posix OS's.
- *
- * Locking: none, the open file handle ensures it won't go away.
- */
-
-static int fionbio(struct file *file, int __user *p)
-{
- int nonblock;
-
- if (get_user(nonblock, p))
- return -EFAULT;
-
- spin_lock(&file->f_lock);
- if (nonblock)
- file->f_flags |= O_NONBLOCK;
- else
- file->f_flags &= ~O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-}
-
-/**
* tiocsetd - set line discipline
* @tty: tty device
* @p: pointer to user data
@@ -2495,22 +2481,40 @@
return 0;
}
-static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
+static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss)
{
static DEFINE_RATELIMIT_STATE(depr_flags,
DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
char comm[TASK_COMM_LEN];
+ struct serial_struct v;
int flags;
- if (get_user(flags, &ss->flags))
- return;
+ if (copy_from_user(&v, ss, sizeof(struct serial_struct)))
+ return -EFAULT;
- flags &= ASYNC_DEPRECATED;
+ flags = v.flags & ASYNC_DEPRECATED;
if (flags && __ratelimit(&depr_flags))
pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
__func__, get_task_comm(comm, current), flags);
+ if (!tty->ops->set_serial)
+ return -ENOTTY;
+ return tty->ops->set_serial(tty, &v);
+}
+
+static int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss)
+{
+ struct serial_struct v;
+ int err;
+
+ memset(&v, 0, sizeof(struct serial_struct));
+ if (!tty->ops->get_serial)
+ return -ENOTTY;
+ err = tty->ops->get_serial(tty, &v);
+ if (!err && copy_to_user(ss, &v, sizeof(struct serial_struct)))
+ err = -EFAULT;
+ return err;
}
/*
@@ -2573,8 +2577,6 @@
return tiocswinsz(real_tty, p);
case TIOCCONS:
return real_tty != tty ? -EINVAL : tioccons(file);
- case FIONBIO:
- return fionbio(file, p);
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
@@ -2629,11 +2631,7 @@
case TIOCMBIS:
return tty_tiocmset(tty, cmd, p);
case TIOCGICOUNT:
- retval = tty_tiocgicount(tty, p);
- /* For the moment allow fall through to the old method */
- if (retval != -EINVAL)
- return retval;
- break;
+ return tty_tiocgicount(tty, p);
case TCFLSH:
switch (arg) {
case TCIFLUSH:
@@ -2644,8 +2642,9 @@
}
break;
case TIOCSSERIAL:
- tty_warn_deprecated_flags(p);
- break;
+ return tty_tiocsserial(tty, p);
+ case TIOCGSERIAL:
+ return tty_tiocgserial(tty, p);
case TIOCGPTPEER:
/* Special because the struct file is needed */
return ptm_open_peer(file, tty, (int)arg);
@@ -2673,6 +2672,81 @@
}
#ifdef CONFIG_COMPAT
+
+struct serial_struct32 {
+ compat_int_t type;
+ compat_int_t line;
+ compat_uint_t port;
+ compat_int_t irq;
+ compat_int_t flags;
+ compat_int_t xmit_fifo_size;
+ compat_int_t custom_divisor;
+ compat_int_t baud_base;
+ unsigned short close_delay;
+ char io_type;
+ char reserved_char[1];
+ compat_int_t hub6;
+ unsigned short closing_wait; /* time to wait before closing */
+ unsigned short closing_wait2; /* no longer used... */
+ compat_uint_t iomem_base;
+ unsigned short iomem_reg_shift;
+ unsigned int port_high;
+ /* compat_ulong_t iomap_base FIXME */
+ compat_int_t reserved[1];
+};
+
+static int compat_tty_tiocsserial(struct tty_struct *tty,
+ struct serial_struct32 __user *ss)
+{
+ static DEFINE_RATELIMIT_STATE(depr_flags,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+ char comm[TASK_COMM_LEN];
+ struct serial_struct32 v32;
+ struct serial_struct v;
+ int flags;
+
+ if (copy_from_user(&v32, ss, sizeof(struct serial_struct32)))
+ return -EFAULT;
+
+ memcpy(&v, &v32, offsetof(struct serial_struct32, iomem_base));
+ v.iomem_base = compat_ptr(v32.iomem_base);
+ v.iomem_reg_shift = v32.iomem_reg_shift;
+ v.port_high = v32.port_high;
+ v.iomap_base = 0;
+
+ flags = v.flags & ASYNC_DEPRECATED;
+
+ if (flags && __ratelimit(&depr_flags))
+ pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
+ __func__, get_task_comm(comm, current), flags);
+ if (!tty->ops->set_serial)
+ return -ENOTTY;
+ return tty->ops->set_serial(tty, &v);
+}
+
+static int compat_tty_tiocgserial(struct tty_struct *tty,
+ struct serial_struct32 __user *ss)
+{
+ struct serial_struct32 v32;
+ struct serial_struct v;
+ int err;
+ memset(&v, 0, sizeof(struct serial_struct));
+
+ if (!tty->ops->set_serial)
+ return -ENOTTY;
+ err = tty->ops->get_serial(tty, &v);
+ if (!err) {
+ memcpy(&v32, &v, offsetof(struct serial_struct32, iomem_base));
+ v32.iomem_base = (unsigned long)v.iomem_base >> 32 ?
+ 0xfffffff : ptr_to_compat(v.iomem_base);
+ v32.iomem_reg_shift = v.iomem_reg_shift;
+ v32.port_high = v.port_high;
+ if (copy_to_user(ss, &v32, sizeof(struct serial_struct32)))
+ err = -EFAULT;
+ }
+ return err;
+}
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -2680,9 +2754,90 @@
struct tty_ldisc *ld;
int retval = -ENOIOCTLCMD;
+ switch (cmd) {
+ case TIOCSTI:
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ case TIOCGEXCL:
+ case TIOCGETD:
+ case TIOCSETD:
+ case TIOCGDEV:
+ case TIOCMGET:
+ case TIOCMSET:
+ case TIOCMBIC:
+ case TIOCMBIS:
+ case TIOCGICOUNT:
+ case TIOCGPGRP:
+ case TIOCSPGRP:
+ case TIOCGSID:
+ case TIOCSERGETLSR:
+ case TIOCGRS485:
+ case TIOCSRS485:
+#ifdef TIOCGETP
+ case TIOCGETP:
+ case TIOCSETP:
+ case TIOCSETN:
+#endif
+#ifdef TIOCGETC
+ case TIOCGETC:
+ case TIOCSETC:
+#endif
+#ifdef TIOCGLTC
+ case TIOCGLTC:
+ case TIOCSLTC:
+#endif
+ case TCSETSF:
+ case TCSETSW:
+ case TCSETS:
+ case TCGETS:
+#ifdef TCGETS2
+ case TCGETS2:
+ case TCSETSF2:
+ case TCSETSW2:
+ case TCSETS2:
+#endif
+ case TCGETA:
+ case TCSETAF:
+ case TCSETAW:
+ case TCSETA:
+ case TIOCGLCKTRMIOS:
+ case TIOCSLCKTRMIOS:
+#ifdef TCGETX
+ case TCGETX:
+ case TCSETX:
+ case TCSETXW:
+ case TCSETXF:
+#endif
+ case TIOCGSOFTCAR:
+ case TIOCSSOFTCAR:
+ return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+ case TIOCCONS:
+ case TIOCEXCL:
+ case TIOCNXCL:
+ case TIOCVHANGUP:
+ case TIOCSBRK:
+ case TIOCCBRK:
+ case TCSBRK:
+ case TCSBRKP:
+ case TCFLSH:
+ case TIOCGPTPEER:
+ case TIOCNOTTY:
+ case TIOCSCTTY:
+ case TCXONC:
+ case TIOCMIWAIT:
+ case TIOCSERCONFIG:
+ return tty_ioctl(file, cmd, arg);
+ }
+
if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
return -EINVAL;
+ switch (cmd) {
+ case TIOCSSERIAL:
+ return compat_tty_tiocsserial(tty, compat_ptr(arg));
+ case TIOCGSERIAL:
+ return compat_tty_tiocgserial(tty, compat_ptr(arg));
+ }
if (tty->ops->compat_ioctl) {
retval = tty->ops->compat_ioctl(tty, cmd, arg);
if (retval != -ENOIOCTLCMD)
@@ -2694,8 +2849,9 @@
return hung_up_tty_compat_ioctl(file, cmd, arg);
if (ld->ops->compat_ioctl)
retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
- else
- retval = n_tty_compat_ioctl_helper(tty, file, cmd, arg);
+ if (retval == -ENOIOCTLCMD && ld->ops->ioctl)
+ retval = ld->ops->ioctl(tty, file,
+ (unsigned long)compat_ptr(cmd), arg);
tty_ldisc_deref(ld);
return retval;
@@ -2750,7 +2906,7 @@
do_each_pid_task(session, PIDTYPE_SID, p) {
tty_notice(tty, "SAK: killed process %d (%s): by session\n",
task_pid_nr(p), p->comm);
- send_sig(SIGKILL, p, 1);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
} while_each_pid_task(session, PIDTYPE_SID, p);
/* Now kill any processes that happen to have the tty open */
@@ -2758,7 +2914,7 @@
if (p->signal->tty == tty) {
tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
task_pid_nr(p), p->comm);
- send_sig(SIGKILL, p, 1);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
continue;
}
task_lock(p);
@@ -2766,7 +2922,7 @@
if (i != 0) {
tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
task_pid_nr(p), p->comm, i - 1);
- force_sig(SIGKILL, p);
+ group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
}
task_unlock(p);
} while_each_thread(g, p);
@@ -2796,17 +2952,11 @@
EXPORT_SYMBOL(do_SAK);
-static int dev_match_devt(struct device *dev, const void *data)
-{
- const dev_t *devt = data;
- return dev->devt == *devt;
-}
-
/* Must put_device() after it's unused! */
static struct device *tty_get_device(struct tty_struct *tty)
{
dev_t devt = tty_devnum(tty);
- return class_find_device(tty_class, NULL, &devt, dev_match_devt);
+ return class_find_device_by_devt(tty_class, devt);
}
@@ -3331,6 +3481,7 @@
*/
int __init tty_init(void)
{
+ tty_sysctl_init();
cdev_init(&tty_cdev, &tty_fops);
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c
index d99fec4..9245fff 100644
--- a/drivers/tty/tty_ioctl.c
+++ b/drivers/tty/tty_ioctl.c
@@ -941,19 +941,3 @@
}
}
EXPORT_SYMBOL(n_tty_ioctl_helper);
-
-#ifdef CONFIG_COMPAT
-long n_tty_compat_ioctl_helper(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case TIOCGLCKTRMIOS:
- case TIOCSLCKTRMIOS:
- return tty_mode_ioctl(tty, file, cmd, (unsigned long) compat_ptr(arg));
- default:
- return -ENOIOCTLCMD;
- }
-}
-EXPORT_SYMBOL(n_tty_compat_ioctl_helper);
-#endif
-
diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c
index c4ecd66..f8ed50a 100644
--- a/drivers/tty/tty_jobctrl.c
+++ b/drivers/tty/tty_jobctrl.c
@@ -44,7 +44,7 @@
tty_pgrp = tty->pgrp;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
- if (tty_pgrp && pgrp != tty->pgrp) {
+ if (tty_pgrp && pgrp != tty_pgrp) {
if (is_ignored(sig)) {
if (sig == SIGTTIN)
ret = -EIO;
@@ -313,7 +313,7 @@
read_unlock(&tasklist_lock);
}
-/**
+/*
*
* no_tty - Ensure the current process does not have a controlling tty
*/
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index fc4c97c..4c49f53 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -156,6 +156,13 @@
* takes tty_ldiscs_lock to guard against ldisc races
*/
+#if defined(CONFIG_LDISC_AUTOLOAD)
+ #define INITIAL_AUTOLOAD_STATE 1
+#else
+ #define INITIAL_AUTOLOAD_STATE 0
+#endif
+static int tty_ldisc_autoload = INITIAL_AUTOLOAD_STATE;
+
static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
{
struct tty_ldisc *ld;
@@ -170,6 +177,8 @@
*/
ldops = get_ldops(disc);
if (IS_ERR(ldops)) {
+ if (!capable(CAP_SYS_MODULE) && !tty_ldisc_autoload)
+ return ERR_PTR(-EPERM);
request_module("tty-ldisc-%d", disc);
ldops = get_ldops(disc);
if (IS_ERR(ldops))
@@ -327,6 +336,11 @@
{
int ret;
+ /* Kindly asking blocked readers to release the read side */
+ set_bit(TTY_LDISC_CHANGING, &tty->flags);
+ wake_up_interruptible_all(&tty->read_wait);
+ wake_up_interruptible_all(&tty->write_wait);
+
ret = __tty_ldisc_lock(tty, timeout);
if (!ret)
return -EBUSY;
@@ -337,6 +351,8 @@
void tty_ldisc_unlock(struct tty_struct *tty)
{
clear_bit(TTY_LDISC_HALTED, &tty->flags);
+ /* Can be cleared here - ldisc_unlock will wake up writers firstly */
+ clear_bit(TTY_LDISC_CHANGING, &tty->flags);
__tty_ldisc_unlock(tty);
}
@@ -471,6 +487,7 @@
static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
{
+ lockdep_assert_held_write(&tty->ldisc_sem);
WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags));
clear_bit(TTY_LDISC_OPEN, &tty->flags);
if (ld->ops->close)
@@ -492,6 +509,7 @@
struct tty_ldisc *disc = tty_ldisc_get(tty, ld);
int r;
+ lockdep_assert_held_write(&tty->ldisc_sem);
if (IS_ERR(disc))
return PTR_ERR(disc);
tty->ldisc = disc;
@@ -615,6 +633,7 @@
*/
static void tty_ldisc_kill(struct tty_struct *tty)
{
+ lockdep_assert_held_write(&tty->ldisc_sem);
if (!tty->ldisc)
return;
/*
@@ -662,6 +681,7 @@
struct tty_ldisc *ld;
int retval;
+ lockdep_assert_held_write(&tty->ldisc_sem);
ld = tty_ldisc_get(tty, disc);
if (IS_ERR(ld)) {
BUG_ON(disc == N_TTY);
@@ -760,6 +780,10 @@
return retval;
if (o_tty) {
+ /*
+ * Called without o_tty->ldisc_sem held, as o_tty has been
+ * just allocated and no one has a reference to it.
+ */
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
tty_ldisc_close(tty, tty->ldisc);
@@ -825,7 +849,44 @@
*/
void tty_ldisc_deinit(struct tty_struct *tty)
{
+ /* no ldisc_sem, tty is being destroyed */
if (tty->ldisc)
tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;
}
+
+static struct ctl_table tty_table[] = {
+ {
+ .procname = "ldisc_autoload",
+ .data = &tty_ldisc_autoload,
+ .maxlen = sizeof(tty_ldisc_autoload),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
+ },
+ { }
+};
+
+static struct ctl_table tty_dir_table[] = {
+ {
+ .procname = "tty",
+ .mode = 0555,
+ .child = tty_table,
+ },
+ { }
+};
+
+static struct ctl_table tty_root_table[] = {
+ {
+ .procname = "dev",
+ .mode = 0555,
+ .child = tty_dir_table,
+ },
+ { }
+};
+
+void tty_sysctl_init(void)
+{
+ register_sysctl_table(tty_root_table);
+}
diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c
index 0c98d88..60ff236 100644
--- a/drivers/tty/tty_ldsem.c
+++ b/drivers/tty/tty_ldsem.c
@@ -34,29 +34,6 @@
#include <linux/sched/task.h>
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define __acq(l, s, t, r, c, n, i) \
- lock_acquire(&(l)->dep_map, s, t, r, c, n, i)
-# define __rel(l, n, i) \
- lock_release(&(l)->dep_map, n, i)
-#define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i)
-#define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i)
-#define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i)
-#define lockdep_release(l, n, i) __rel(l, n, i)
-#else
-# define lockdep_acquire(l, s, t, i) do { } while (0)
-# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0)
-# define lockdep_acquire_read(l, s, t, i) do { } while (0)
-# define lockdep_release(l, n, i) do { } while (0)
-#endif
-
-#ifdef CONFIG_LOCK_STAT
-# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_)
-#else
-# define lock_stat(_lock, stat) do { } while (0)
-#endif
-
-
#if BITS_PER_LONG == 64
# define LDSEM_ACTIVE_MASK 0xffffffffL
#else
@@ -116,8 +93,7 @@
list_for_each_entry_safe(waiter, next, &sem->read_wait, list) {
tsk = waiter->task;
- smp_mb();
- waiter->task = NULL;
+ smp_store_release(&waiter->task, NULL);
wake_up_process(tsk);
put_task_struct(tsk);
}
@@ -217,7 +193,7 @@
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
- if (!waiter.task)
+ if (!smp_load_acquire(&waiter.task))
break;
if (!timeout)
break;
@@ -235,6 +211,7 @@
raw_spin_lock_irq(&sem->wait_lock);
if (waiter.task) {
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
+ sem->wait_readers--;
list_del(&waiter.list);
raw_spin_unlock_irq(&sem->wait_lock);
put_task_struct(waiter.task);
@@ -293,6 +270,16 @@
if (!locked)
atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count);
list_del(&waiter.list);
+
+ /*
+ * In case of timeout, wake up every reader who gave the right of way
+ * to writer. Prevent separation readers into two groups:
+ * one that helds semaphore and another that sleeps.
+ * (in case of no contention with a writer)
+ */
+ if (!locked && list_empty(&sem->write_wait))
+ __ldsem_wake_readers(sem);
+
raw_spin_unlock_irq(&sem->wait_lock);
__set_current_state(TASK_RUNNING);
@@ -310,17 +297,17 @@
{
long count;
- lockdep_acquire_read(sem, subclass, 0, _RET_IP_);
+ rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_);
count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count);
if (count <= 0) {
- lock_stat(sem, contended);
+ lock_contended(&sem->dep_map, _RET_IP_);
if (!down_read_failed(sem, count, timeout)) {
- lockdep_release(sem, 1, _RET_IP_);
+ rwsem_release(&sem->dep_map, 1, _RET_IP_);
return 0;
}
}
- lock_stat(sem, acquired);
+ lock_acquired(&sem->dep_map, _RET_IP_);
return 1;
}
@@ -329,17 +316,17 @@
{
long count;
- lockdep_acquire(sem, subclass, 0, _RET_IP_);
+ rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count);
if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) {
- lock_stat(sem, contended);
+ lock_contended(&sem->dep_map, _RET_IP_);
if (!down_write_failed(sem, count, timeout)) {
- lockdep_release(sem, 1, _RET_IP_);
+ rwsem_release(&sem->dep_map, 1, _RET_IP_);
return 0;
}
}
- lock_stat(sem, acquired);
+ lock_acquired(&sem->dep_map, _RET_IP_);
return 1;
}
@@ -362,8 +349,8 @@
while (count >= 0) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) {
- lockdep_acquire_read(sem, 0, 1, _RET_IP_);
- lock_stat(sem, acquired);
+ rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_);
+ lock_acquired(&sem->dep_map, _RET_IP_);
return 1;
}
}
@@ -388,8 +375,8 @@
while ((count & LDSEM_ACTIVE_MASK) == 0) {
if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) {
- lockdep_acquire(sem, 0, 1, _RET_IP_);
- lock_stat(sem, acquired);
+ rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
+ lock_acquired(&sem->dep_map, _RET_IP_);
return 1;
}
}
@@ -403,7 +390,7 @@
{
long count;
- lockdep_release(sem, 1, _RET_IP_);
+ rwsem_release(&sem->dep_map, 1, _RET_IP_);
count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count);
if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0)
@@ -417,7 +404,7 @@
{
long count;
- lockdep_release(sem, 1, _RET_IP_);
+ rwsem_release(&sem->dep_map, 1, _RET_IP_);
count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count);
if (count < 0)
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index c699d41..044c3cb 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -279,7 +279,6 @@
* Return a refcount protected tty instance or NULL if the port is not
* associated with a tty (eg due to close or hangup)
*/
-
struct tty_struct *tty_port_tty_get(struct tty_port *port)
{
unsigned long flags;
@@ -300,7 +299,6 @@
* Associate the port and tty pair. Manages any internal refcounts.
* Pass NULL to deassociate a port
*/
-
void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;
@@ -343,7 +341,6 @@
*
* Caller holds tty lock.
*/
-
void tty_port_hangup(struct tty_port *port)
{
struct tty_struct *tty;
@@ -399,7 +396,6 @@
* to hide some internal details. This will eventually become entirely
* internal to the tty port.
*/
-
int tty_port_carrier_raised(struct tty_port *port)
{
if (port->ops->carrier_raised == NULL)
@@ -416,7 +412,6 @@
* to hide some internal details. This will eventually become entirely
* internal to the tty port.
*/
-
void tty_port_raise_dtr_rts(struct tty_port *port)
{
if (port->ops->dtr_rts)
@@ -432,7 +427,6 @@
* to hide some internal details. This will eventually become entirely
* internal to the tty port.
*/
-
void tty_port_lower_dtr_rts(struct tty_port *port)
{
if (port->ops->dtr_rts)
@@ -464,7 +458,6 @@
* NB: May drop and reacquire tty lock when blocking, so tty and tty_port
* may have changed state (eg., may have been hung up).
*/
-
int tty_port_block_til_ready(struct tty_port *port,
struct tty_struct *tty, struct file *filp)
{
diff --git a/drivers/tty/ttynull.c b/drivers/tty/ttynull.c
new file mode 100644
index 0000000..17f05b7
--- /dev/null
+++ b/drivers/tty/ttynull.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Axis Communications AB
+ *
+ * Based on ttyprintk.c:
+ * Copyright (C) 2010 Samo Pogacnik
+ */
+
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+
+static const struct tty_port_operations ttynull_port_ops;
+static struct tty_driver *ttynull_driver;
+static struct tty_port ttynull_port;
+
+static int ttynull_open(struct tty_struct *tty, struct file *filp)
+{
+ return tty_port_open(&ttynull_port, tty, filp);
+}
+
+static void ttynull_close(struct tty_struct *tty, struct file *filp)
+{
+ tty_port_close(&ttynull_port, tty, filp);
+}
+
+static void ttynull_hangup(struct tty_struct *tty)
+{
+ tty_port_hangup(&ttynull_port);
+}
+
+static int ttynull_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ return count;
+}
+
+static int ttynull_write_room(struct tty_struct *tty)
+{
+ return 65536;
+}
+
+static const struct tty_operations ttynull_ops = {
+ .open = ttynull_open,
+ .close = ttynull_close,
+ .hangup = ttynull_hangup,
+ .write = ttynull_write,
+ .write_room = ttynull_write_room,
+};
+
+static struct tty_driver *ttynull_device(struct console *c, int *index)
+{
+ *index = 0;
+ return ttynull_driver;
+}
+
+static struct console ttynull_console = {
+ .name = "ttynull",
+ .device = ttynull_device,
+};
+
+static int __init ttynull_init(void)
+{
+ struct tty_driver *driver;
+ int ret;
+
+ driver = tty_alloc_driver(1,
+ TTY_DRIVER_RESET_TERMIOS |
+ TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_UNNUMBERED_NODE);
+ if (IS_ERR(driver))
+ return PTR_ERR(driver);
+
+ tty_port_init(&ttynull_port);
+ ttynull_port.ops = &ttynull_port_ops;
+
+ driver->driver_name = "ttynull";
+ driver->name = "ttynull";
+ driver->type = TTY_DRIVER_TYPE_CONSOLE;
+ driver->init_termios = tty_std_termios;
+ driver->init_termios.c_oflag = OPOST | OCRNL | ONOCR | ONLRET;
+ tty_set_operations(driver, &ttynull_ops);
+ tty_port_link_device(&ttynull_port, driver, 0);
+
+ ret = tty_register_driver(driver);
+ if (ret < 0) {
+ put_tty_driver(driver);
+ tty_port_destroy(&ttynull_port);
+ return ret;
+ }
+
+ ttynull_driver = driver;
+ register_console(&ttynull_console);
+
+ return 0;
+}
+
+static void __exit ttynull_exit(void)
+{
+ unregister_console(&ttynull_console);
+ tty_unregister_driver(ttynull_driver);
+ put_tty_driver(ttynull_driver);
+ tty_port_destroy(&ttynull_port);
+}
+
+module_init(ttynull_init);
+module_exit(ttynull_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c
index 58b454c..d2a1e12 100644
--- a/drivers/tty/vcc.c
+++ b/drivers/tty/vcc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* vcc.c: sun4v virtual channel concentrator
*
* Copyright (C) 2017 Oracle. All rights reserved.
diff --git a/drivers/tty/vt/.gitignore b/drivers/tty/vt/.gitignore
index 83683a2..9b38b85 100644
--- a/drivers/tty/vt/.gitignore
+++ b/drivers/tty/vt/.gitignore
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
consolemap_deftbl.c
defkeymap.c
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c
index 7c7ada0..b28aa0d 100644
--- a/drivers/tty/vt/consolemap.c
+++ b/drivers/tty/vt/consolemap.c
@@ -542,7 +542,7 @@
if (!ct)
return 0;
- unilist = memdup_user(list, ct * sizeof(struct unipair));
+ unilist = vmemdup_user(list, ct * sizeof(struct unipair));
if (IS_ERR(unilist))
return PTR_ERR(unilist);
@@ -641,7 +641,7 @@
out_unlock:
console_unlock();
- kfree(unilist);
+ kvfree(unilist);
return err;
}
@@ -743,7 +743,7 @@
struct uni_pagedir *p;
struct unipair *unilist;
- unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
+ unilist = kvmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL);
if (!unilist)
return -ENOMEM;
@@ -775,7 +775,7 @@
if (copy_to_user(list, unilist, min(ect, ct) * sizeof(struct unipair)))
ret = -EFAULT;
put_user(ect, uct);
- kfree(unilist);
+ kvfree(unilist);
return ret ? ret : (ect <= ct) ? 0 : -ENOMEM;
}
diff --git a/drivers/tty/vt/cp437.uni b/drivers/tty/vt/cp437.uni
index bc61634..a199190 100644
--- a/drivers/tty/vt/cp437.uni
+++ b/drivers/tty/vt/cp437.uni
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Unicode table for IBM Codepage 437. Note that there are many more
# substitutions that could be conceived (for example, thick-line
diff --git a/drivers/tty/vt/defkeymap.c_shipped b/drivers/tty/vt/defkeymap.c_shipped
index d2208df..c7095fb 100644
--- a/drivers/tty/vt/defkeymap.c_shipped
+++ b/drivers/tty/vt/defkeymap.c_shipped
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/* Do not edit this file! It was automatically generated by */
/* loadkeys --mktable defkeymap.map > defkeymap.c */
diff --git a/drivers/tty/vt/defkeymap.map b/drivers/tty/vt/defkeymap.map
index 50b30ca..37f1ac6 100644
--- a/drivers/tty/vt/defkeymap.map
+++ b/drivers/tty/vt/defkeymap.map
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
# Default kernel keymap. This uses 7 modifier combinations.
keymaps 0-2,4-5,8,12
# Change the above line into
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 88312c6..515fc09 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -123,6 +123,7 @@
static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock);
static DEFINE_SPINLOCK(led_lock);
+static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
static bool dead_key_next;
@@ -1449,7 +1450,7 @@
KBD_UNICODE, ¶m);
if (rc != NOTIFY_STOP)
if (down && !raw_mode)
- to_utf8(vc, keysym);
+ k_unicode(vc, keysym, !down);
return;
}
@@ -1990,11 +1991,12 @@
char *p;
u_char *q;
u_char __user *up;
- int sz;
+ int sz, fnw_sz;
int delta;
char *first_free, *fj, *fnw;
int i, j, k;
int ret;
+ unsigned long flags;
if (!capable(CAP_SYS_TTY_CONFIG))
perm = 0;
@@ -2037,7 +2039,14 @@
goto reterr;
}
+ fnw = NULL;
+ fnw_sz = 0;
+ /* race aginst other writers */
+ again:
+ spin_lock_irqsave(&func_buf_lock, flags);
q = func_table[i];
+
+ /* fj pointer to next entry after 'q' */
first_free = funcbufptr + (funcbufsize - funcbufleft);
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
;
@@ -2045,10 +2054,12 @@
fj = func_table[j];
else
fj = first_free;
-
+ /* buffer usage increase by new entry */
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
+
if (delta <= funcbufleft) { /* it fits in current buf */
if (j < MAX_NR_FUNC) {
+ /* make enough space for new entry at 'fj' */
memmove(fj + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
if (func_table[k])
@@ -2061,20 +2072,28 @@
sz = 256;
while (sz < funcbufsize - funcbufleft + delta)
sz <<= 1;
- fnw = kmalloc(sz, GFP_KERNEL);
- if(!fnw) {
- ret = -ENOMEM;
- goto reterr;
+ if (fnw_sz != sz) {
+ spin_unlock_irqrestore(&func_buf_lock, flags);
+ kfree(fnw);
+ fnw = kmalloc(sz, GFP_KERNEL);
+ fnw_sz = sz;
+ if (!fnw) {
+ ret = -ENOMEM;
+ goto reterr;
+ }
+ goto again;
}
if (!q)
func_table[i] = fj;
+ /* copy data before insertion point to new location */
if (fj > funcbufptr)
memmove(fnw, funcbufptr, fj - funcbufptr);
for (k = 0; k < j; k++)
if (func_table[k])
func_table[k] = fnw + (func_table[k] - funcbufptr);
+ /* copy data after insertion point to new location */
if (first_free > fj) {
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
@@ -2087,7 +2106,9 @@
funcbufleft = funcbufleft - delta + sz - funcbufsize;
funcbufsize = sz;
}
+ /* finally insert item itself */
strcpy(func_table[i], kbs->kb_string);
+ spin_unlock_irqrestore(&func_buf_lock, flags);
break;
}
ret = 0;
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 07496c7..78732fe 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -2,7 +2,9 @@
/*
* This module exports the functions:
*
- * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)'
+ * 'int set_selection_user(struct tiocl_selection __user *,
+ * struct tty_struct *)'
+ * 'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)'
* 'void clear_selection(void)'
* 'int paste_selection(struct tty_struct *)'
* 'int sel_loadlut(char __user *)'
@@ -80,6 +82,7 @@
sel_start = -1;
}
}
+EXPORT_SYMBOL_GPL(clear_selection);
/*
* User settable table: what characters are to be considered alphabetic?
@@ -154,7 +157,7 @@
}
/**
- * set_selection - set the current selection.
+ * set_selection_user - set the current selection.
* @sel: user selection info
* @tty: the console tty
*
@@ -163,35 +166,44 @@
* The entire selection process is managed under the console_lock. It's
* a lot under the lock but its hardly a performance path
*/
-int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
+int set_selection_user(const struct tiocl_selection __user *sel,
+ struct tty_struct *tty)
+{
+ struct tiocl_selection v;
+
+ if (copy_from_user(&v, sel, sizeof(*sel)))
+ return -EFAULT;
+
+ return set_selection_kernel(&v, tty);
+}
+
+int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
{
struct vc_data *vc = vc_cons[fg_console].d;
int new_sel_start, new_sel_end, spc;
- struct tiocl_selection v;
char *bp, *obp;
int i, ps, pe, multiplier;
u32 c;
int mode;
poke_blanked_console();
- if (copy_from_user(&v, sel, sizeof(*sel)))
- return -EFAULT;
- v.xs = min_t(u16, v.xs - 1, vc->vc_cols - 1);
- v.ys = min_t(u16, v.ys - 1, vc->vc_rows - 1);
- v.xe = min_t(u16, v.xe - 1, vc->vc_cols - 1);
- v.ye = min_t(u16, v.ye - 1, vc->vc_rows - 1);
- ps = v.ys * vc->vc_size_row + (v.xs << 1);
- pe = v.ye * vc->vc_size_row + (v.xe << 1);
+ v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
+ v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
+ v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
+ v->ye = min_t(u16, v->ye - 1, vc->vc_rows - 1);
+ ps = v->ys * vc->vc_size_row + (v->xs << 1);
+ pe = v->ye * vc->vc_size_row + (v->xe << 1);
- if (v.sel_mode == TIOCL_SELCLEAR) {
+ if (v->sel_mode == TIOCL_SELCLEAR) {
/* useful for screendump without selection highlights */
clear_selection();
return 0;
}
- if (mouse_reporting() && (v.sel_mode & TIOCL_SELMOUSEREPORT)) {
- mouse_report(tty, v.sel_mode & TIOCL_SELBUTTONMASK, v.xs, v.ys);
+ if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
+ mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
+ v->ys);
return 0;
}
@@ -208,7 +220,7 @@
else
use_unicode = 0;
- switch (v.sel_mode)
+ switch (v->sel_mode)
{
case TIOCL_SELCHAR: /* character-by-character selection */
new_sel_start = ps;
@@ -322,6 +334,7 @@
sel_buffer_lth = bp - sel_buffer;
return 0;
}
+EXPORT_SYMBOL_GPL(set_selection_kernel);
/* Insert the contents of the selection buffer into the
* queue of the tty associated with the current console.
@@ -367,3 +380,4 @@
tty_ldisc_deref(ld);
return 0;
}
+EXPORT_SYMBOL_GPL(paste_selection);
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 2384ea8..1f04234 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Provide access to virtual console memory.
- * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcs: the screen as it is being viewed right now (possibly scrolled)
* /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
* [minor: N]
*
@@ -80,7 +80,7 @@
struct vcs_poll_data {
struct notifier_block notifier;
unsigned int cons_num;
- bool seen_last_update;
+ int event;
wait_queue_head_t waitq;
struct fasync_struct *fasync;
};
@@ -93,9 +93,18 @@
struct vcs_poll_data *poll =
container_of(nb, struct vcs_poll_data, notifier);
int currcons = poll->cons_num;
+ int fa_band;
- if (code != VT_UPDATE)
+ switch (code) {
+ case VT_UPDATE:
+ fa_band = POLL_PRI;
+ break;
+ case VT_DEALLOCATE:
+ fa_band = POLL_HUP;
+ break;
+ default:
return NOTIFY_DONE;
+ }
if (currcons == 0)
currcons = fg_console;
@@ -104,9 +113,9 @@
if (currcons != vc->vc_num)
return NOTIFY_DONE;
- poll->seen_last_update = false;
+ poll->event = code;
wake_up_interruptible(&poll->waitq);
- kill_fasync(&poll->fasync, SIGIO, POLL_IN);
+ kill_fasync(&poll->fasync, SIGIO, fa_band);
return NOTIFY_OK;
}
@@ -131,6 +140,15 @@
poll->cons_num = console(file_inode(file));
init_waitqueue_head(&poll->waitq);
poll->notifier.notifier_call = vcs_notifier;
+ /*
+ * In order not to lose any update event, we must pretend one might
+ * have occurred before we have a chance to register our notifier.
+ * This is also how user space has come to detect which kernels
+ * support POLLPRI on /dev/vcs* devices i.e. using poll() with
+ * POLLPRI and a zero timeout.
+ */
+ poll->event = VT_UPDATE;
+
if (register_vt_notifier(&poll->notifier) != 0) {
kfree(poll);
return NULL;
@@ -261,7 +279,7 @@
poll = file->private_data;
if (count && poll)
- poll->seen_last_update = true;
+ poll->event = 0;
read = 0;
ret = 0;
while (count) {
@@ -335,8 +353,9 @@
if (p < HEADER_SIZE) {
size_t tmp_count;
- con_buf0[0] = (char)vc->vc_rows;
- con_buf0[1] = (char)vc->vc_cols;
+ /* clamp header values if they don't fit */
+ con_buf0[0] = min(vc->vc_rows, 0xFFu);
+ con_buf0[1] = min(vc->vc_cols, 0xFFu);
getconsxy(vc, con_buf0 + 2);
con_buf_start += p;
@@ -615,12 +634,21 @@
vcs_poll(struct file *file, poll_table *wait)
{
struct vcs_poll_data *poll = vcs_poll_data_get(file);
- __poll_t ret = DEFAULT_POLLMASK|EPOLLERR|EPOLLPRI;
+ __poll_t ret = DEFAULT_POLLMASK|EPOLLERR;
if (poll) {
poll_wait(file, &poll->waitq, wait);
- if (poll->seen_last_update)
+ switch (poll->event) {
+ case VT_UPDATE:
+ ret = DEFAULT_POLLMASK|EPOLLPRI;
+ break;
+ case VT_DEALLOCATE:
+ ret = DEFAULT_POLLMASK|EPOLLHUP|EPOLLERR;
+ break;
+ case 0:
ret = DEFAULT_POLLMASK;
+ break;
+ }
}
return ret;
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 476ec4b..34aa39d 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -935,8 +935,11 @@
{
WARN_CONSOLE_UNLOCKED();
+ set_origin(vc);
if (vc->vc_sw->con_flush_scrollback)
vc->vc_sw->con_flush_scrollback(vc);
+ else
+ vc->vc_sw->con_switch(vc);
}
/*
@@ -1004,9 +1007,7 @@
clear_buffer_attributes(vc);
}
- /* Forcibly update if we're panicing */
- if ((update && vc->vc_mode != KD_GRAPHICS) ||
- vt_force_oops_output(vc))
+ if (update && vc->vc_mode != KD_GRAPHICS)
do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
}
set_cursor(vc);
@@ -1046,7 +1047,6 @@
vc->vc_hi_font_mask = 0;
vc->vc_complement_mask = 0;
vc->vc_can_do_color = 0;
- vc->vc_panic_force_write = false;
vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
vc->vc_sw->con_init(vc, init);
if (!vc->vc_complement_mask)
@@ -1056,6 +1056,13 @@
vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
}
+
+static void visual_deinit(struct vc_data *vc)
+{
+ vc->vc_sw->con_deinit(vc);
+ module_put(vc->vc_sw->owner);
+}
+
int vc_allocate(unsigned int currcons) /* return 0 on success */
{
struct vt_notifier_param param;
@@ -1103,6 +1110,7 @@
return 0;
err_free:
+ visual_deinit(vc);
kfree(vc);
vc_cons[currcons].d = NULL;
return -ENOMEM;
@@ -1275,6 +1283,7 @@
if (con_is_visible(vc))
update_screen(vc);
vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
+ notify_update(vc);
return err;
}
@@ -1330,9 +1339,8 @@
param.vc = vc = vc_cons[currcons].d;
atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m);
vcs_remove_sysfs(currcons);
- vc->vc_sw->con_deinit(vc);
+ visual_deinit(vc);
put_pid(vc->vt_pid);
- module_put(vc->vc_sw->owner);
vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
vc_cons[currcons].d = NULL;
@@ -1344,6 +1352,8 @@
* VT102 emulator
*/
+enum { EPecma = 0, EPdec, EPeq, EPgt, EPlt};
+
#define set_kbd(vc, x) vt_set_kbd_mode_bit((vc)->vc_num, (x))
#define clr_kbd(vc, x) vt_clr_kbd_mode_bit((vc)->vc_num, (x))
#define is_kbd(vc, x) vt_get_kbd_mode_bit((vc)->vc_num, (x))
@@ -1505,8 +1515,10 @@
count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1;
start = (unsigned short *)vc->vc_origin;
break;
+ case 3: /* include scrollback */
+ flush_scrollback(vc);
+ /* fallthrough */
case 2: /* erase whole display */
- case 3: /* (and scrollback buffer later) */
vc_uniscr_clear_lines(vc, 0, vc->vc_rows);
count = vc->vc_cols * vc->vc_rows;
start = (unsigned short *)vc->vc_origin;
@@ -1515,12 +1527,7 @@
return;
}
scr_memsetw(start, vc->vc_video_erase_char, 2 * count);
- if (vpar == 3) {
- set_origin(vc);
- flush_scrollback(vc);
- if (con_is_visible(vc))
- update_screen(vc);
- } else if (con_should_update(vc))
+ if (con_should_update(vc))
do_update_region(vc, (unsigned long) start, count);
vc->vc_need_wrap = 0;
}
@@ -1630,9 +1637,9 @@
/*
* ITU T.416 Higher colour modes. They break the usual properties of SGR codes
- * and thus need to be detected and ignored by hand. Strictly speaking, that
- * standard also wants : rather than ; as separators, contrary to ECMA-48, but
- * no one produces such codes and almost no one accepts them.
+ * and thus need to be detected and ignored by hand. That standard also
+ * wants : rather than ; as separators but sequences containing : are currently
+ * completely ignored by the parser.
*
* Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in
* supporting them.
@@ -1805,7 +1812,7 @@
respond_string(buf, tty->port);
}
-/* invoked via ioctl(TIOCLINUX) and through set_selection */
+/* invoked via ioctl(TIOCLINUX) and through set_selection_user */
int mouse_reporting(void)
{
return vc_cons[fg_console].d->vc_report_mouse;
@@ -1817,7 +1824,7 @@
int i;
for (i = 0; i <= vc->vc_npar; i++)
- if (vc->vc_ques) {
+ if (vc->vc_priv == EPdec) {
switch(vc->vc_par[i]) { /* DEC private modes set/reset */
case 1: /* Cursor keys send ^[Ox/^[[x */
if (on_off)
@@ -2024,7 +2031,7 @@
}
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
- EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
+ EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
ESpalette, ESosc };
/* console_lock is held (except via vc_init()) */
@@ -2033,7 +2040,7 @@
vc->vc_top = 0;
vc->vc_bottom = vc->vc_rows;
vc->vc_state = ESnormal;
- vc->vc_ques = 0;
+ vc->vc_priv = EPecma;
vc->vc_translate = set_translate(LAT1_MAP, vc);
vc->vc_G0_charset = LAT1_MAP;
vc->vc_G1_charset = GRAF_MAP;
@@ -2114,6 +2121,7 @@
lf(vc);
if (!is_kbd(vc, lnm))
return;
+ /* fall through */
case 13:
cr(vc);
return;
@@ -2236,9 +2244,22 @@
vc->vc_state=ESfunckey;
return;
}
- vc->vc_ques = (c == '?');
- if (vc->vc_ques)
+ switch (c) {
+ case '?':
+ vc->vc_priv = EPdec;
return;
+ case '>':
+ vc->vc_priv = EPgt;
+ return;
+ case '=':
+ vc->vc_priv = EPeq;
+ return;
+ case '<':
+ vc->vc_priv = EPlt;
+ return;
+ }
+ vc->vc_priv = EPecma;
+ /* fall through */
case ESgetpars:
if (c == ';' && vc->vc_npar < NPAR - 1) {
vc->vc_npar++;
@@ -2248,16 +2269,22 @@
vc->vc_par[vc->vc_npar] += c - '0';
return;
}
+ if (c >= 0x20 && c <= 0x3f) { /* 0x2x, 0x3a and 0x3c - 0x3f */
+ vc->vc_state = EScsiignore;
+ return;
+ }
vc->vc_state = ESnormal;
switch(c) {
case 'h':
- set_mode(vc, 1);
+ if (vc->vc_priv <= EPdec)
+ set_mode(vc, 1);
return;
case 'l':
- set_mode(vc, 0);
+ if (vc->vc_priv <= EPdec)
+ set_mode(vc, 0);
return;
case 'c':
- if (vc->vc_ques) {
+ if (vc->vc_priv == EPdec) {
if (vc->vc_par[0])
vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16);
else
@@ -2266,7 +2293,7 @@
}
break;
case 'm':
- if (vc->vc_ques) {
+ if (vc->vc_priv == EPdec) {
clear_selection();
if (vc->vc_par[0])
vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1];
@@ -2276,7 +2303,7 @@
}
break;
case 'n':
- if (!vc->vc_ques) {
+ if (vc->vc_priv == EPecma) {
if (vc->vc_par[0] == 5)
status_report(tty);
else if (vc->vc_par[0] == 6)
@@ -2284,8 +2311,8 @@
}
return;
}
- if (vc->vc_ques) {
- vc->vc_ques = 0;
+ if (vc->vc_priv != EPecma) {
+ vc->vc_priv = EPecma;
return;
}
switch(c) {
@@ -2408,6 +2435,11 @@
return;
}
return;
+ case EScsiignore:
+ if (c >= 20 && c <= 0x3f)
+ return;
+ vc->vc_state = ESnormal;
+ return;
case ESpercent:
vc->vc_state = ESnormal;
switch (c) {
@@ -2767,8 +2799,8 @@
con_flush(vc, draw_from, draw_to, &draw_x);
vc_uniscr_debug_check(vc);
console_conditional_schedule();
- console_unlock();
notify_update(vc);
+ console_unlock();
return n;
}
@@ -2887,8 +2919,7 @@
unsigned char c;
static DEFINE_SPINLOCK(printing_lock);
const ushort *start;
- ushort cnt = 0;
- ushort myx;
+ ushort start_x, cnt;
int kmsg_console;
/* console busy or not yet initialized */
@@ -2901,17 +2932,13 @@
if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
vc = vc_cons[kmsg_console - 1].d;
- /* read `x' only after setting currcons properly (otherwise
- the `x' macro will read the x of the foreground console). */
- myx = vc->vc_x;
-
if (!vc_cons_allocated(fg_console)) {
/* impossible */
/* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */
goto quit;
}
- if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
+ if (vc->vc_mode != KD_TEXT)
goto quit;
/* undraw cursor first */
@@ -2919,53 +2946,41 @@
hide_cursor(vc);
start = (ushort *)vc->vc_pos;
-
- /* Contrived structure to try to emulate original need_wrap behaviour
- * Problems caused when we have need_wrap set on '\n' character */
+ start_x = vc->vc_x;
+ cnt = 0;
while (count--) {
c = *b++;
if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {
- if (cnt > 0) {
- if (con_is_visible(vc))
- vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
- vc->vc_x += cnt;
- if (vc->vc_need_wrap)
- vc->vc_x--;
- cnt = 0;
- }
+ if (cnt && con_is_visible(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, start_x);
+ cnt = 0;
if (c == 8) { /* backspace */
bs(vc);
start = (ushort *)vc->vc_pos;
- myx = vc->vc_x;
+ start_x = vc->vc_x;
continue;
}
if (c != 13)
lf(vc);
cr(vc);
start = (ushort *)vc->vc_pos;
- myx = vc->vc_x;
+ start_x = vc->vc_x;
if (c == 10 || c == 13)
continue;
}
+ vc_uniscr_putc(vc, c);
scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos);
notify_write(vc, c);
cnt++;
- if (myx == vc->vc_cols - 1) {
+ if (vc->vc_x == vc->vc_cols - 1) {
vc->vc_need_wrap = 1;
- continue;
- }
- vc->vc_pos += 2;
- myx++;
- }
- if (cnt > 0) {
- if (con_is_visible(vc))
- vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x);
- vc->vc_x += cnt;
- if (vc->vc_x == vc->vc_cols) {
- vc->vc_x--;
- vc->vc_need_wrap = 1;
+ } else {
+ vc->vc_pos += 2;
+ vc->vc_x++;
}
}
+ if (cnt && con_is_visible(vc))
+ vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, start_x);
set_cursor(vc);
notify_update(vc);
@@ -3001,7 +3016,7 @@
* There are some functions which can sleep for arbitrary periods
* (paste_selection) but we don't need the lock there anyway.
*
- * set_selection has locking, and definitely needs it
+ * set_selection_user has locking, and definitely needs it
*/
int tioclinux(struct tty_struct *tty, unsigned long arg)
@@ -3021,7 +3036,8 @@
{
case TIOCL_SETSEL:
console_lock();
- ret = set_selection((struct tiocl_selection __user *)(p+1), tty);
+ ret = set_selection_user((struct tiocl_selection
+ __user *)(p+1), tty);
console_unlock();
break;
case TIOCL_PASTESEL:
@@ -3755,7 +3771,11 @@
char *buf)
{
struct con_driver *con = dev_get_drvdata(dev);
- int bind = con_is_bound(con->con);
+ int bind;
+
+ console_lock();
+ bind = con_is_bound(con->con);
+ console_unlock();
return snprintf(buf, PAGE_SIZE, "%i\n", bind);
}
@@ -3806,6 +3826,8 @@
{
int i, bound = 0;
+ WARN_CONSOLE_UNLOCKED();
+
for (i = 0; i < MAX_NR_CONSOLES; i++) {
if (con_driver_map[i] == csw) {
bound = 1;
@@ -3818,6 +3840,20 @@
EXPORT_SYMBOL(con_is_bound);
/**
+ * con_is_visible - checks whether the current console is visible
+ * @vc: virtual console
+ *
+ * RETURNS: zero if not visible, nonzero if visible
+ */
+bool con_is_visible(const struct vc_data *vc)
+{
+ WARN_CONSOLE_UNLOCKED();
+
+ return *vc->vc_display_fg == vc;
+}
+EXPORT_SYMBOL(con_is_visible);
+
+/**
* con_debug_enter - prepare the console for the kernel debugger
* @sw: console driver
*
@@ -4150,6 +4186,8 @@
struct vc_data *vc = vc_cons[fg_console].d;
int i;
+ might_sleep();
+
WARN_CONSOLE_UNLOCKED();
if (console_blanked) {
@@ -4171,8 +4209,6 @@
return;
}
- if (blank_state != blank_normal_wait)
- return;
blank_state = blank_off;
/* don't blank graphics */
@@ -4229,8 +4265,7 @@
return;
}
vc = vc_cons[fg_console].d;
- /* Try to unblank in oops case too */
- if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc))
+ if (vc->vc_mode != KD_TEXT)
return; /* but leave console_blanked != 0 */
if (blankinterval) {
@@ -4239,7 +4274,7 @@
}
console_blanked = 0;
- if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
+ if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
/* Low-level driver cannot restore -> do it ourselves */
update_screen(vc);
if (console_blank_hook)
@@ -4611,8 +4646,9 @@
void getconsxy(struct vc_data *vc, unsigned char *p)
{
- p[0] = vc->vc_x;
- p[1] = vc->vc_y;
+ /* clamp values if they don't fit */
+ p[0] = min(vc->vc_x, 0xFFu);
+ p[1] = min(vc->vc_y, 0xFFu);
}
void putconsxy(struct vc_data *vc, unsigned char *p)
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 73cdc0d..8b0ed13 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -1175,17 +1175,13 @@
{
struct vc_data *vc = tty->driver_data;
struct console_font_op op; /* used in multiple places here */
- unsigned int console;
- void __user *up = (void __user *)arg;
+ unsigned int console = vc->vc_num;
+ void __user *up = compat_ptr(arg);
int perm;
- int ret = 0;
- console = vc->vc_num;
- if (!vc_cons_allocated(console)) { /* impossible? */
- ret = -ENOIOCTLCMD;
- goto out;
- }
+ if (!vc_cons_allocated(console)) /* impossible? */
+ return -ENOIOCTLCMD;
/*
* To have permissions to do most of the vt ioctls, we either have
@@ -1201,17 +1197,14 @@
*/
case PIO_FONTX:
case GIO_FONTX:
- ret = compat_fontx_ioctl(cmd, up, perm, &op);
- break;
+ return compat_fontx_ioctl(cmd, up, perm, &op);
case KDFONTOP:
- ret = compat_kdfontop_ioctl(up, perm, &op, vc);
- break;
+ return compat_kdfontop_ioctl(up, perm, &op, vc);
case PIO_UNIMAP:
case GIO_UNIMAP:
- ret = compat_unimap_ioctl(cmd, up, perm, vc);
- break;
+ return compat_unimap_ioctl(cmd, up, perm, vc);
/*
* all these treat 'arg' as an integer
@@ -1236,21 +1229,15 @@
case VT_DISALLOCATE:
case VT_RESIZE:
case VT_RESIZEX:
- goto fallback;
+ return vt_ioctl(tty, cmd, arg);
/*
* the rest has a compatible data structure behind arg,
* but we have to convert it to a proper 64 bit pointer.
*/
default:
- arg = (unsigned long)compat_ptr(arg);
- goto fallback;
+ return vt_ioctl(tty, cmd, (unsigned long)up);
}
-out:
- return ret;
-
-fallback:
- return vt_ioctl(tty, cmd, arg);
}