[PATCH v2 4/5] serial: split generic UART driver helper functions into a separate file

Nicolas Pitre nicolas.pitre at linaro.org
Sat Apr 1 15:21:18 PDT 2017


This contains code that is common between serial_core.c and the
minitty code to come. Mainly helper functions used by UART drivers.

Signed-off-by: Nicolas Pitre <nico at linaro.org>
---
 drivers/tty/serial/Makefile      |   2 +-
 drivers/tty/serial/serial_core.c | 419 +------------------------------------
 drivers/tty/serial/serial_lib.c  | 440 +++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h      |   1 +
 4 files changed, 443 insertions(+), 419 deletions(-)
 create mode 100644 drivers/tty/serial/serial_lib.c

diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 53c03e0051..073afd10c5 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -2,7 +2,7 @@
 # Makefile for the kernel serial device drivers.
 #
 
-obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o serial_lib.o
 
 obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
 obj-$(CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST) += earlycon-arm-semihost.o
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 3fe5689497..20214e1d87 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -44,12 +44,6 @@
  */
 static DEFINE_MUTEX(port_mutex);
 
-/*
- * lockdep: port->lock is initialized in two places, but we
- *          want only one lock-class:
- */
-static struct lock_class_key port_lock_key;
-
 #define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)
 
 static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
@@ -293,183 +287,6 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
 	}
 }
 
-/**
- *	uart_update_timeout - update per-port FIFO timeout.
- *	@port:  uart_port structure describing the port
- *	@cflag: termios cflag value
- *	@baud:  speed of the port
- *
- *	Set the port FIFO timeout value.  The @cflag value should
- *	reflect the actual hardware settings.
- */
-void
-uart_update_timeout(struct uart_port *port, unsigned int cflag,
-		    unsigned int baud)
-{
-	unsigned int bits;
-
-	/* byte size and parity */
-	switch (cflag & CSIZE) {
-	case CS5:
-		bits = 7;
-		break;
-	case CS6:
-		bits = 8;
-		break;
-	case CS7:
-		bits = 9;
-		break;
-	default:
-		bits = 10;
-		break; /* CS8 */
-	}
-
-	if (cflag & CSTOPB)
-		bits++;
-	if (cflag & PARENB)
-		bits++;
-
-	/*
-	 * The total number of bits to be transmitted in the fifo.
-	 */
-	bits = bits * port->fifosize;
-
-	/*
-	 * Figure the timeout to send the above number of bits.
-	 * Add .02 seconds of slop
-	 */
-	port->timeout = (HZ * bits) / baud + HZ/50;
-}
-
-EXPORT_SYMBOL(uart_update_timeout);
-
-/**
- *	uart_get_baud_rate - return baud rate for a particular port
- *	@port: uart_port structure describing the port in question.
- *	@termios: desired termios settings.
- *	@old: old termios (or NULL)
- *	@min: minimum acceptable baud rate
- *	@max: maximum acceptable baud rate
- *
- *	Decode the termios structure into a numeric baud rate,
- *	taking account of the magic 38400 baud rate (with spd_*
- *	flags), and mapping the %B0 rate to 9600 baud.
- *
- *	If the new baud rate is invalid, try the old termios setting.
- *	If it's still invalid, we try 9600 baud.
- *
- *	Update the @termios structure to reflect the baud rate
- *	we're actually going to be using. Don't do this for the case
- *	where B0 is requested ("hang up").
- */
-unsigned int
-uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
-		   struct ktermios *old, unsigned int min, unsigned int max)
-{
-	unsigned int try;
-	unsigned int baud;
-	unsigned int altbaud;
-	int hung_up = 0;
-	upf_t flags = port->flags & UPF_SPD_MASK;
-
-	switch (flags) {
-	case UPF_SPD_HI:
-		altbaud = 57600;
-		break;
-	case UPF_SPD_VHI:
-		altbaud = 115200;
-		break;
-	case UPF_SPD_SHI:
-		altbaud = 230400;
-		break;
-	case UPF_SPD_WARP:
-		altbaud = 460800;
-		break;
-	default:
-		altbaud = 38400;
-		break;
-	}
-
-	for (try = 0; try < 2; try++) {
-		baud = tty_termios_baud_rate(termios);
-
-		/*
-		 * The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
-		 * Die! Die! Die!
-		 */
-		if (try == 0 && baud == 38400)
-			baud = altbaud;
-
-		/*
-		 * Special case: B0 rate.
-		 */
-		if (baud == 0) {
-			hung_up = 1;
-			baud = 9600;
-		}
-
-		if (baud >= min && baud <= max)
-			return baud;
-
-		/*
-		 * Oops, the quotient was zero.  Try again with
-		 * the old baud rate if possible.
-		 */
-		termios->c_cflag &= ~CBAUD;
-		if (old) {
-			baud = tty_termios_baud_rate(old);
-			if (!hung_up)
-				tty_termios_encode_baud_rate(termios,
-								baud, baud);
-			old = NULL;
-			continue;
-		}
-
-		/*
-		 * As a last resort, if the range cannot be met then clip to
-		 * the nearest chip supported rate.
-		 */
-		if (!hung_up) {
-			if (baud <= min)
-				tty_termios_encode_baud_rate(termios,
-							min + 1, min + 1);
-			else
-				tty_termios_encode_baud_rate(termios,
-							max - 1, max - 1);
-		}
-	}
-	/* Should never happen */
-	WARN_ON(1);
-	return 0;
-}
-
-EXPORT_SYMBOL(uart_get_baud_rate);
-
-/**
- *	uart_get_divisor - return uart clock divisor
- *	@port: uart_port structure describing the port.
- *	@baud: desired baud rate
- *
- *	Calculate the uart clock divisor for the port.
- */
-unsigned int
-uart_get_divisor(struct uart_port *port, unsigned int baud)
-{
-	unsigned int quot;
-
-	/*
-	 * Old custom speed handling.
-	 */
-	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
-		quot = port->custom_divisor;
-	else
-		quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
-
-	return quot;
-}
-
-EXPORT_SYMBOL(uart_get_divisor);
-
 /* Caller holds port mutex */
 static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
 					struct ktermios *old_termios)
@@ -1837,207 +1654,6 @@ static const struct file_operations uart_proc_fops = {
 };
 #endif
 
-#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
-/**
- *	uart_console_write - write a console message to a serial port
- *	@port: the port to write the message
- *	@s: array of characters
- *	@count: number of characters in string to write
- *	@putchar: function to write character to port
- */
-void uart_console_write(struct uart_port *port, const char *s,
-			unsigned int count,
-			void (*putchar)(struct uart_port *, int))
-{
-	unsigned int i;
-
-	for (i = 0; i < count; i++, s++) {
-		if (*s == '\n')
-			putchar(port, '\r');
-		putchar(port, *s);
-	}
-}
-EXPORT_SYMBOL_GPL(uart_console_write);
-
-/*
- *	Check whether an invalid uart number has been specified, and
- *	if so, search for the first available port that does have
- *	console support.
- */
-struct uart_port * __init
-uart_get_console(struct uart_port *ports, int nr, struct console *co)
-{
-	int idx = co->index;
-
-	if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
-				     ports[idx].membase == NULL))
-		for (idx = 0; idx < nr; idx++)
-			if (ports[idx].iobase != 0 ||
-			    ports[idx].membase != NULL)
-				break;
-
-	co->index = idx;
-
-	return ports + idx;
-}
-
-/**
- *	uart_parse_earlycon - Parse earlycon options
- *	@p:	  ptr to 2nd field (ie., just beyond '<name>,')
- *	@iotype:  ptr for decoded iotype (out)
- *	@addr:    ptr for decoded mapbase/iobase (out)
- *	@options: ptr for <options> field; NULL if not present (out)
- *
- *	Decodes earlycon kernel command line parameters of the form
- *	   earlycon=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
- *	   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
- */
-int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr,
-			char **options)
-{
-	if (strncmp(p, "mmio,", 5) == 0) {
-		*iotype = UPIO_MEM;
-		p += 5;
-	} else if (strncmp(p, "mmio16,", 7) == 0) {
-		*iotype = UPIO_MEM16;
-		p += 7;
-	} else if (strncmp(p, "mmio32,", 7) == 0) {
-		*iotype = UPIO_MEM32;
-		p += 7;
-	} else if (strncmp(p, "mmio32be,", 9) == 0) {
-		*iotype = UPIO_MEM32BE;
-		p += 9;
-	} else if (strncmp(p, "mmio32native,", 13) == 0) {
-		*iotype = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) ?
-			UPIO_MEM32BE : UPIO_MEM32;
-		p += 13;
-	} else if (strncmp(p, "io,", 3) == 0) {
-		*iotype = UPIO_PORT;
-		p += 3;
-	} else if (strncmp(p, "0x", 2) == 0) {
-		*iotype = UPIO_MEM;
-	} else {
-		return -EINVAL;
-	}
-
-	/*
-	 * Before you replace it with kstrtoull(), think about options separator
-	 * (',') it will not tolerate
-	 */
-	*addr = simple_strtoull(p, NULL, 0);
-	p = strchr(p, ',');
-	if (p)
-		p++;
-
-	*options = p;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(uart_parse_earlycon);
-
-/**
- *	uart_parse_options - Parse serial port baud/parity/bits/flow control.
- *	@options: pointer to option string
- *	@baud: pointer to an 'int' variable for the baud rate.
- *	@parity: pointer to an 'int' variable for the parity.
- *	@bits: pointer to an 'int' variable for the number of data bits.
- *	@flow: pointer to an 'int' variable for the flow control character.
- *
- *	uart_parse_options decodes a string containing the serial console
- *	options.  The format of the string is <baud><parity><bits><flow>,
- *	eg: 115200n8r
- */
-void
-uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
-{
-	char *s = options;
-
-	*baud = simple_strtoul(s, NULL, 10);
-	while (*s >= '0' && *s <= '9')
-		s++;
-	if (*s)
-		*parity = *s++;
-	if (*s)
-		*bits = *s++ - '0';
-	if (*s)
-		*flow = *s;
-}
-EXPORT_SYMBOL_GPL(uart_parse_options);
-
-/**
- *	uart_set_options - setup the serial console parameters
- *	@port: pointer to the serial ports uart_port structure
- *	@co: console pointer
- *	@baud: baud rate
- *	@parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
- *	@bits: number of data bits
- *	@flow: flow control character - 'r' (rts)
- */
-int
-uart_set_options(struct uart_port *port, struct console *co,
-		 int baud, int parity, int bits, int flow)
-{
-	struct ktermios termios;
-	static struct ktermios dummy;
-
-	/*
-	 * Ensure that the serial console lock is initialised
-	 * early.
-	 * If this port is a console, then the spinlock is already
-	 * initialised.
-	 */
-	if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
-		spin_lock_init(&port->lock);
-		lockdep_set_class(&port->lock, &port_lock_key);
-	}
-
-	memset(&termios, 0, sizeof(struct ktermios));
-
-	termios.c_cflag |= CREAD | HUPCL | CLOCAL;
-	tty_termios_encode_baud_rate(&termios, baud, baud);
-
-	if (bits == 7)
-		termios.c_cflag |= CS7;
-	else
-		termios.c_cflag |= CS8;
-
-	switch (parity) {
-	case 'o': case 'O':
-		termios.c_cflag |= PARODD;
-		/*fall through*/
-	case 'e': case 'E':
-		termios.c_cflag |= PARENB;
-		break;
-	}
-
-	if (flow == 'r')
-		termios.c_cflag |= CRTSCTS;
-
-	/*
-	 * some uarts on other side don't support no flow control.
-	 * So we set * DTR in host uart to make them happy
-	 */
-	port->mctrl |= TIOCM_DTR;
-
-	port->ops->set_termios(port, &termios, &dummy);
-	/*
-	 * Allow the setting of the UART parameters with a NULL console
-	 * too:
-	 */
-	if (co)
-		co->cflag = termios.c_cflag;
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(uart_set_options);
-#endif /* CONFIG_SERIAL_CORE_CONSOLE */
-
 /**
  * uart_change_pm - set power state of the port
  *
@@ -2751,15 +2367,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
 	state->pm_state = UART_PM_STATE_UNDEFINED;
 	uport->cons = drv->cons;
 	uport->minor = drv->tty_driver->minor_start + uport->line;
+	uart_port_lock_init(uport);
 
-	/*
-	 * If this port is a console, then the spinlock is already
-	 * initialised.
-	 */
-	if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
-		spin_lock_init(&uport->lock);
-		lockdep_set_class(&uport->lock, &port_lock_key);
-	}
 	if (uport->cons && uport->dev)
 		of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
 
@@ -2885,32 +2494,6 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
 	return ret;
 }
 
-/*
- *	Are the two ports equivalent?
- */
-int uart_match_port(struct uart_port *port1, struct uart_port *port2)
-{
-	if (port1->iotype != port2->iotype)
-		return 0;
-
-	switch (port1->iotype) {
-	case UPIO_PORT:
-		return (port1->iobase == port2->iobase);
-	case UPIO_HUB6:
-		return (port1->iobase == port2->iobase) &&
-		       (port1->hub6   == port2->hub6);
-	case UPIO_MEM:
-	case UPIO_MEM16:
-	case UPIO_MEM32:
-	case UPIO_MEM32BE:
-	case UPIO_AU:
-	case UPIO_TSI:
-		return (port1->mapbase == port2->mapbase);
-	}
-	return 0;
-}
-EXPORT_SYMBOL(uart_match_port);
-
 /**
  *	uart_handle_dcd_change - handle a change of carrier detect state
  *	@uport: uart_port structure for the open port
diff --git a/drivers/tty/serial/serial_lib.c b/drivers/tty/serial/serial_lib.c
new file mode 100644
index 0000000000..c3f521b401
--- /dev/null
+++ b/drivers/tty/serial/serial_lib.c
@@ -0,0 +1,440 @@
+/*
+ *  Common support functions for serial port drivers
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/export.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/spinlock.h>
+
+/*
+ * lockdep: port->lock is initialized in two places, but we
+ *          want only one lock-class:
+ */
+static struct lock_class_key port_lock_key;
+
+void uart_port_lock_init(struct uart_port *port)
+{
+	/*
+	 * If this port is a console, then the spinlock is already
+	 * initialised.
+	 */
+	if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
+		spin_lock_init(&port->lock);
+		lockdep_set_class(&port->lock, &port_lock_key);
+	}
+}
+
+/**
+ *	uart_update_timeout - update per-port FIFO timeout.
+ *	@port:  uart_port structure describing the port
+ *	@cflag: termios cflag value
+ *	@baud:  speed of the port
+ *
+ *	Set the port FIFO timeout value.  The @cflag value should
+ *	reflect the actual hardware settings.
+ */
+void
+uart_update_timeout(struct uart_port *port, unsigned int cflag,
+		    unsigned int baud)
+{
+	unsigned int bits;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:
+		bits = 7;
+		break;
+	case CS6:
+		bits = 8;
+		break;
+	case CS7:
+		bits = 9;
+		break;
+	default:
+		bits = 10;
+		break; /* CS8 */
+	}
+
+	if (cflag & CSTOPB)
+		bits++;
+	if (cflag & PARENB)
+		bits++;
+
+	/*
+	 * The total number of bits to be transmitted in the fifo.
+	 */
+	bits = bits * port->fifosize;
+
+	/*
+	 * Figure the timeout to send the above number of bits.
+	 * Add .02 seconds of slop
+	 */
+	port->timeout = (HZ * bits) / baud + HZ/50;
+}
+EXPORT_SYMBOL(uart_update_timeout);
+
+/**
+ *	uart_get_baud_rate - return baud rate for a particular port
+ *	@port: uart_port structure describing the port in question.
+ *	@termios: desired termios settings.
+ *	@old: old termios (or NULL)
+ *	@min: minimum acceptable baud rate
+ *	@max: maximum acceptable baud rate
+ *
+ *	Decode the termios structure into a numeric baud rate,
+ *	taking account of the magic 38400 baud rate (with spd_*
+ *	flags), and mapping the %B0 rate to 9600 baud.
+ *
+ *	If the new baud rate is invalid, try the old termios setting.
+ *	If it's still invalid, we try 9600 baud.
+ *
+ *	Update the @termios structure to reflect the baud rate
+ *	we're actually going to be using. Don't do this for the case
+ *	where B0 is requested ("hang up").
+ */
+unsigned int
+uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old, unsigned int min, unsigned int max)
+{
+	unsigned int try;
+	unsigned int baud;
+	unsigned int altbaud;
+	int hung_up = 0;
+	upf_t flags = port->flags & UPF_SPD_MASK;
+
+	switch (flags) {
+	case UPF_SPD_HI:
+		altbaud = 57600;
+		break;
+	case UPF_SPD_VHI:
+		altbaud = 115200;
+		break;
+	case UPF_SPD_SHI:
+		altbaud = 230400;
+		break;
+	case UPF_SPD_WARP:
+		altbaud = 460800;
+		break;
+	default:
+		altbaud = 38400;
+		break;
+	}
+
+	for (try = 0; try < 2; try++) {
+		baud = tty_termios_baud_rate(termios);
+
+		/*
+		 * The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
+		 * Die! Die! Die!
+		 */
+		if (try == 0 && baud == 38400)
+			baud = altbaud;
+
+		/*
+		 * Special case: B0 rate.
+		 */
+		if (baud == 0) {
+			hung_up = 1;
+			baud = 9600;
+		}
+
+		if (baud >= min && baud <= max)
+			return baud;
+
+		/*
+		 * Oops, the quotient was zero.  Try again with
+		 * the old baud rate if possible.
+		 */
+		termios->c_cflag &= ~CBAUD;
+		if (old) {
+			baud = tty_termios_baud_rate(old);
+			if (!hung_up)
+				tty_termios_encode_baud_rate(termios,
+								baud, baud);
+			old = NULL;
+			continue;
+		}
+
+		/*
+		 * As a last resort, if the range cannot be met then clip to
+		 * the nearest chip supported rate.
+		 */
+		if (!hung_up) {
+			if (baud <= min)
+				tty_termios_encode_baud_rate(termios,
+							min + 1, min + 1);
+			else
+				tty_termios_encode_baud_rate(termios,
+							max - 1, max - 1);
+		}
+	}
+	/* Should never happen */
+	WARN_ON(1);
+	return 0;
+}
+EXPORT_SYMBOL(uart_get_baud_rate);
+
+/**
+ *	uart_get_divisor - return uart clock divisor
+ *	@port: uart_port structure describing the port.
+ *	@baud: desired baud rate
+ *
+ *	Calculate the uart clock divisor for the port.
+ */
+unsigned int
+uart_get_divisor(struct uart_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+	/*
+	 * Old custom speed handling.
+	 */
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+		quot = port->custom_divisor;
+	else
+		quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
+
+	return quot;
+}
+EXPORT_SYMBOL(uart_get_divisor);
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+/**
+ *	uart_console_write - write a console message to a serial port
+ *	@port: the port to write the message
+ *	@s: array of characters
+ *	@count: number of characters in string to write
+ *	@putchar: function to write character to port
+ */
+void uart_console_write(struct uart_port *port, const char *s,
+			unsigned int count,
+			void (*putchar)(struct uart_port *, int))
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++, s++) {
+		if (*s == '\n')
+			putchar(port, '\r');
+		putchar(port, *s);
+	}
+}
+EXPORT_SYMBOL_GPL(uart_console_write);
+
+/*
+ *	Check whether an invalid uart number has been specified, and
+ *	if so, search for the first available port that does have
+ *	console support.
+ */
+struct uart_port * __init
+uart_get_console(struct uart_port *ports, int nr, struct console *co)
+{
+	int idx = co->index;
+
+	if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
+				     ports[idx].membase == NULL))
+		for (idx = 0; idx < nr; idx++)
+			if (ports[idx].iobase != 0 ||
+			    ports[idx].membase != NULL)
+				break;
+
+	co->index = idx;
+
+	return ports + idx;
+}
+
+/**
+ *	uart_parse_earlycon - Parse earlycon options
+ *	@p:	  ptr to 2nd field (ie., just beyond '<name>,')
+ *	@iotype:  ptr for decoded iotype (out)
+ *	@addr:    ptr for decoded mapbase/iobase (out)
+ *	@options: ptr for <options> field; NULL if not present (out)
+ *
+ *	Decodes earlycon kernel command line parameters of the form
+ *	   earlycon=<name>,io|mmio|mmio16|mmio32|mmio32be|mmio32native,<addr>,<options>
+ *	   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
+ */
+int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr,
+			char **options)
+{
+	if (strncmp(p, "mmio,", 5) == 0) {
+		*iotype = UPIO_MEM;
+		p += 5;
+	} else if (strncmp(p, "mmio16,", 7) == 0) {
+		*iotype = UPIO_MEM16;
+		p += 7;
+	} else if (strncmp(p, "mmio32,", 7) == 0) {
+		*iotype = UPIO_MEM32;
+		p += 7;
+	} else if (strncmp(p, "mmio32be,", 9) == 0) {
+		*iotype = UPIO_MEM32BE;
+		p += 9;
+	} else if (strncmp(p, "mmio32native,", 13) == 0) {
+		*iotype = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) ?
+			UPIO_MEM32BE : UPIO_MEM32;
+		p += 13;
+	} else if (strncmp(p, "io,", 3) == 0) {
+		*iotype = UPIO_PORT;
+		p += 3;
+	} else if (strncmp(p, "0x", 2) == 0) {
+		*iotype = UPIO_MEM;
+	} else {
+		return -EINVAL;
+	}
+
+	/*
+	 * Before you replace it with kstrtoull(), think about options separator
+	 * (',') it will not tolerate
+	 */
+	*addr = simple_strtoull(p, NULL, 0);
+	p = strchr(p, ',');
+	if (p)
+		p++;
+
+	*options = p;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_parse_earlycon);
+
+/**
+ *	uart_parse_options - Parse serial port baud/parity/bits/flow control.
+ *	@options: pointer to option string
+ *	@baud: pointer to an 'int' variable for the baud rate.
+ *	@parity: pointer to an 'int' variable for the parity.
+ *	@bits: pointer to an 'int' variable for the number of data bits.
+ *	@flow: pointer to an 'int' variable for the flow control character.
+ *
+ *	uart_parse_options decodes a string containing the serial console
+ *	options.  The format of the string is <baud><parity><bits><flow>,
+ *	eg: 115200n8r
+ */
+void
+uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
+{
+	char *s = options;
+
+	*baud = simple_strtoul(s, NULL, 10);
+	while (*s >= '0' && *s <= '9')
+		s++;
+	if (*s)
+		*parity = *s++;
+	if (*s)
+		*bits = *s++ - '0';
+	if (*s)
+		*flow = *s;
+}
+EXPORT_SYMBOL_GPL(uart_parse_options);
+
+/**
+ *	uart_set_options - setup the serial console parameters
+ *	@port: pointer to the serial ports uart_port structure
+ *	@co: console pointer
+ *	@baud: baud rate
+ *	@parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
+ *	@bits: number of data bits
+ *	@flow: flow control character - 'r' (rts)
+ */
+int
+uart_set_options(struct uart_port *port, struct console *co,
+		 int baud, int parity, int bits, int flow)
+{
+	struct ktermios termios;
+	static struct ktermios dummy;
+
+	/*
+	 * Ensure that the serial console lock is initialised
+	 * early.
+	 */
+	uart_port_lock_init(port);
+
+	memset(&termios, 0, sizeof(struct ktermios));
+
+	termios.c_cflag |= CREAD | HUPCL | CLOCAL;
+	tty_termios_encode_baud_rate(&termios, baud, baud);
+
+	if (bits == 7)
+		termios.c_cflag |= CS7;
+	else
+		termios.c_cflag |= CS8;
+
+	switch (parity) {
+	case 'o': case 'O':
+		termios.c_cflag |= PARODD;
+		/*fall through*/
+	case 'e': case 'E':
+		termios.c_cflag |= PARENB;
+		break;
+	}
+
+	if (flow == 'r')
+		termios.c_cflag |= CRTSCTS;
+
+	/*
+	 * some uarts on other side don't support no flow control.
+	 * So we set * DTR in host uart to make them happy
+	 */
+	port->mctrl |= TIOCM_DTR;
+
+	port->ops->set_termios(port, &termios, &dummy);
+	/*
+	 * Allow the setting of the UART parameters with a NULL console
+	 * too:
+	 */
+	if (co)
+		co->cflag = termios.c_cflag;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_set_options);
+#endif /* CONFIG_SERIAL_CORE_CONSOLE */
+
+/*
+ *	Are the two ports equivalent?
+ */
+int uart_match_port(struct uart_port *port1, struct uart_port *port2)
+{
+	if (port1->iotype != port2->iotype)
+		return 0;
+
+	switch (port1->iotype) {
+	case UPIO_PORT:
+		return (port1->iobase == port2->iobase);
+	case UPIO_HUB6:
+		return (port1->iobase == port2->iobase) &&
+		       (port1->hub6   == port2->hub6);
+	case UPIO_MEM:
+	case UPIO_MEM16:
+	case UPIO_MEM32:
+	case UPIO_MEM32BE:
+	case UPIO_AU:
+	case UPIO_TSI:
+		return (port1->mapbase == port2->mapbase);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(uart_match_port);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 58484fb35c..505b51db59 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -402,6 +402,7 @@ void uart_unregister_driver(struct uart_driver *uart);
 int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
 int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
 int uart_match_port(struct uart_port *port1, struct uart_port *port2);
+void uart_port_lock_init(struct uart_port *port);
 
 /*
  * Power Management
-- 
2.9.3




More information about the linux-arm-kernel mailing list