From patchwork Sat Apr 1 22:21:18 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicolas Pitre X-Patchwork-Id: 96582 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp1303685qgd; Sat, 1 Apr 2017 15:22:09 -0700 (PDT) X-Received: by 10.84.129.131 with SMTP id b3mr11790467plb.160.1491085329582; Sat, 01 Apr 2017 15:22:09 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id o184si9261375pfg.97.2017.04.01.15.22.09; Sat, 01 Apr 2017 15:22:09 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752090AbdDAWWI (ORCPT + 24 others); Sat, 1 Apr 2017 18:22:08 -0400 Received: from alt42.smtp-out.videotron.ca ([23.233.128.29]:13151 "EHLO alt42.smtp-out.videotron.ca" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751987AbdDAWVa (ORCPT ); Sat, 1 Apr 2017 18:21:30 -0400 Received: from yoda.home ([96.23.157.65]) by Videotron with SMTP id uROhcmFUPcMN9uROicgFyO; Sat, 01 Apr 2017 18:21:29 -0400 X-Authority-Analysis: v=2.1 cv=YqOvP9sX c=1 sm=1 tr=0 a=keA3yYpnlypCNW5BNWqu+w==:117 a=keA3yYpnlypCNW5BNWqu+w==:17 a=L9H7d07YOLsA:10 a=9cW_t1CCXrUA:10 a=s5jvgZ67dGcA:10 a=AzvcPWV-tVgA:10 a=KKAkSRfTAAAA:8 a=wLCnSH7QxGmrT19RutcA:9 a=ez5DB9EMAEzTdY6M:21 a=gyIAFTJFnijismHS:21 a=PynleWgEvfYA:10 a=cvBusfyB2V15izCimMoJ:22 Received: from xanadu.home (xanadu.home [192.168.2.2]) by yoda.home (Postfix) with ESMTP id 53B442DA069E; Sat, 1 Apr 2017 18:21:27 -0400 (EDT) From: Nicolas Pitre To: Greg Kroah-Hartman , Jiri Slaby , linux-serial@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH v2 4/5] serial: split generic UART driver helper functions into a separate file Date: Sat, 1 Apr 2017 18:21:18 -0400 Message-Id: <20170401222119.25106-5-nicolas.pitre@linaro.org> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170401222119.25106-1-nicolas.pitre@linaro.org> References: <20170401222119.25106-1-nicolas.pitre@linaro.org> X-CMAE-Envelope: MS4wfCv0X8GMyhlpj1OwP/pNfjdnUWdOj5n0KbsvMUyrS3C8H28sp/qfzRwy9Eecy4gc1gifZj+gNxCiPSHGP8OAUe6fbfq4g98H3C+4B0KCcDy32/GtIiZN wLmZZcQrb0H1B2XbywAi8NAdWCP/eVtFpyEcz4BiQTrEDQ41Prddjnw5okft2VpVwY/yVNbxhLBYv9ub00xFNRKH1Xttf+OiRtsEXfkT0ew3Wz6bPXR8cIoz kxvttzpicYFL3U1B0yTWpYeFcHjVfe4zSeCRGVbsqzL1d7QGBxMeFmuord9Wir6CS3YyNdq9QoSL1w8tDhyqEqOulH615xVJ1Qvso4jEGew= Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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 --- 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 -- 2.9.3 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 ',') - * @iotype: ptr for decoded iotype (out) - * @addr: ptr for decoded mapbase/iobase (out) - * @options: ptr for field; NULL if not present (out) - * - * Decodes earlycon kernel command line parameters of the form - * earlycon=,io|mmio|mmio16|mmio32|mmio32be|mmio32native,, - * console=,io|mmio|mmio16|mmio32|mmio32be|mmio32native,, - * - * The optional form - * earlycon=,0x, - * console=,0x, - * 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 , - * 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 +#include +#include +#include +#include + +/* + * 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 ',') + * @iotype: ptr for decoded iotype (out) + * @addr: ptr for decoded mapbase/iobase (out) + * @options: ptr for field; NULL if not present (out) + * + * Decodes earlycon kernel command line parameters of the form + * earlycon=,io|mmio|mmio16|mmio32|mmio32be|mmio32native,, + * console=,io|mmio|mmio16|mmio32|mmio32be|mmio32native,, + * + * The optional form + * earlycon=,0x, + * console=,0x, + * 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 , + * 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