whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit 5bd4af34a09a381a0f8b1552684650698937e6b0
parent 738b04fba18d35cd352b7b15afefb8a7b798648e
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Mon, 29 Oct 2018 10:42:20 -0700

Merge tag 'tty-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty/serial updates from Greg KH:
 "Here is the big tty and serial pull request for 4.20-rc1

  Lots of little things here, including a merge from the SPI tree in
  order to keep things simpler for everyone to sync around for one
  platform.

  Major stuff is:

   - tty buffer clearing after use

   - atmel_serial fixes and additions

   - xilinx uart driver updates

  and of course, lots of tiny fixes and additions to individual serial
  drivers.

  All of these have been in linux-next with no reported issues for a
  while"

* tag 'tty-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (66 commits)
  of: base: Change logic in of_alias_get_alias_list()
  of: base: Fix english spelling in of_alias_get_alias_list()
  serial: sh-sci: do not warn if DMA transfers are not supported
  serial: uartps: Do not allow use aliases >= MAX_UART_INSTANCES
  tty: check name length in tty_find_polling_driver()
  serial: sh-sci: Add r8a77990 support
  tty: wipe buffer if not echoing data
  tty: wipe buffer.
  serial: fsl_lpuart: Remove the alias node dependence
  TTY: sn_console: Replace spin_is_locked() with spin_trylock()
  Revert "serial:serial_core: Allow use of CTS for PPS line discipline"
  serial: 8250_uniphier: add auto-flow-control support
  serial: 8250_uniphier: flatten probe function
  serial: 8250_uniphier: remove unused "fifo-size" property
  dt-bindings: serial: sh-sci: Document r8a7744 bindings
  serial: uartps: Fix missing unlock on error in cdns_get_id()
  tty/serial: atmel: add ISO7816 support
  tty/serial_core: add ISO7816 infrastructure
  serial:serial_core: Allow use of CTS for PPS line discipline
  serial: docs: Fix filename for serial reference implementation
  ...

Diffstat:
MDocumentation/devicetree/bindings/serial/renesas,sci-serial.txt | 6++++++
MDocumentation/devicetree/bindings/serial/uniphier-uart.txt | 3+--
MDocumentation/serial/driver | 2+-
ADocumentation/serial/serial-iso7816.txt | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
March/alpha/include/uapi/asm/ioctls.h | 2++
March/mips/include/uapi/asm/ioctls.h | 2++
March/parisc/include/uapi/asm/ioctls.h | 2++
March/powerpc/include/uapi/asm/ioctls.h | 2++
March/sh/include/uapi/asm/ioctls.h | 2++
March/sparc/include/uapi/asm/ioctls.h | 2++
March/xtensa/include/uapi/asm/ioctls.h | 2++
Mdrivers/of/base.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/tty/ehv_bytechan.c | 12++++++------
Mdrivers/tty/n_tty.c | 20+++++++++++++++++---
Mdrivers/tty/serial/8250/8250_core.c | 6+-----
Mdrivers/tty/serial/8250/8250_of.c | 22+++++++++++++---------
Mdrivers/tty/serial/8250/8250_port.c | 29++++++++++++++++++++++++-----
Mdrivers/tty/serial/8250/8250_uniphier.c | 62+++++++++++++++++++++-----------------------------------------
Mdrivers/tty/serial/8250/Kconfig | 2+-
Mdrivers/tty/serial/atmel_serial.c | 190++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mdrivers/tty/serial/atmel_serial.h | 3++-
Mdrivers/tty/serial/cpm_uart/cpm_uart_core.c | 8++++----
Mdrivers/tty/serial/fsl_lpuart.c | 12++++++++++--
Mdrivers/tty/serial/imx.c | 38++++++++++++++++++++++++--------------
Mdrivers/tty/serial/kgdboc.c | 46++++++++++++++++++++++++++--------------------
Mdrivers/tty/serial/mxs-auart.c | 3++-
Mdrivers/tty/serial/pmac_zilog.c | 8+++-----
Mdrivers/tty/serial/qcom_geni_serial.c | 69++++++++++++++++++++++++++++++++-------------------------------------
Mdrivers/tty/serial/samsung.c | 8++++++++
Mdrivers/tty/serial/sc16is7xx.c | 47+++++++++++++++++++++++++++++++++++++++++------
Mdrivers/tty/serial/serial_core.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdrivers/tty/serial/sh-sci.c | 9++++++++-
Mdrivers/tty/serial/sn_console.c | 15++++++---------
Mdrivers/tty/serial/sprd_serial.c | 98+++++++++++++++++++++++++++++++++++++------------------------------------------
Mdrivers/tty/serial/uartlite.c | 71++++++++++++++++++++++++++++++++++++++++-------------------------------
Mdrivers/tty/serial/xilinx_uartps.c | 303++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mdrivers/tty/tty_buffer.c | 13+++++++++++--
Mdrivers/tty/tty_io.c | 2+-
Mdrivers/tty/tty_port.c | 7-------
Minclude/linux/of.h | 10++++++++++
Minclude/linux/serial_core.h | 3+++
Minclude/uapi/asm-generic/ioctls.h | 2++
Minclude/uapi/linux/serial.h | 17+++++++++++++++++
43 files changed, 977 insertions(+), 380 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -14,6 +14,10 @@ Required properties: - "renesas,scifa-r8a7743" for R8A7743 (RZ/G1M) SCIFA compatible UART. - "renesas,scifb-r8a7743" for R8A7743 (RZ/G1M) SCIFB compatible UART. - "renesas,hscif-r8a7743" for R8A7743 (RZ/G1M) HSCIF compatible UART. + - "renesas,scif-r8a7744" for R8A7744 (RZ/G1N) SCIF compatible UART. + - "renesas,scifa-r8a7744" for R8A7744 (RZ/G1N) SCIFA compatible UART. + - "renesas,scifb-r8a7744" for R8A7744 (RZ/G1N) SCIFB compatible UART. + - "renesas,hscif-r8a7744" for R8A7744 (RZ/G1N) HSCIF compatible UART. - "renesas,scif-r8a7745" for R8A7745 (RZ/G1E) SCIF compatible UART. - "renesas,scifa-r8a7745" for R8A7745 (RZ/G1E) SCIFA compatible UART. - "renesas,scifb-r8a7745" for R8A7745 (RZ/G1E) SCIFB compatible UART. @@ -50,6 +54,8 @@ Required properties: - "renesas,hscif-r8a77970" for R8A77970 (R-Car V3M) HSCIF compatible UART. - "renesas,scif-r8a77980" for R8A77980 (R-Car V3H) SCIF compatible UART. - "renesas,hscif-r8a77980" for R8A77980 (R-Car V3H) HSCIF compatible UART. + - "renesas,scif-r8a77990" for R8A77990 (R-Car E3) SCIF compatible UART. + - "renesas,hscif-r8a77990" for R8A77990 (R-Car E3) HSCIF compatible UART. - "renesas,scif-r8a77995" for R8A77995 (R-Car D3) SCIF compatible UART. - "renesas,hscif-r8a77995" for R8A77995 (R-Car D3) HSCIF compatible UART. - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. diff --git a/Documentation/devicetree/bindings/serial/uniphier-uart.txt b/Documentation/devicetree/bindings/serial/uniphier-uart.txt @@ -7,7 +7,7 @@ Required properties: - clocks: phandle to the input clock. Optional properties: -- fifo-size: the RX/TX FIFO size. Defaults to 64 if not specified. +-auto-flow-control: enable automatic flow control support. Example: aliases { @@ -19,5 +19,4 @@ Example: reg = <0x54006800 0x40>; interrupts = <0 33 4>; clocks = <&uart_clk>; - fifo-size = <64>; }; diff --git a/Documentation/serial/driver b/Documentation/serial/driver @@ -7,7 +7,7 @@ This document is meant as a brief overview of some aspects of the new serial driver. It is not complete, any questions you have should be directed to <rmk@arm.linux.org.uk> -The reference implementation is contained within amba_pl011.c. +The reference implementation is contained within amba-pl011.c. diff --git a/Documentation/serial/serial-iso7816.txt b/Documentation/serial/serial-iso7816.txt @@ -0,0 +1,83 @@ + ISO7816 SERIAL COMMUNICATIONS + +1. INTRODUCTION + + ISO/IEC7816 is a series of standards specifying integrated circuit cards (ICC) + also known as smart cards. + +2. HARDWARE-RELATED CONSIDERATIONS + + Some CPUs/UARTs (e.g., Microchip AT91) contain a built-in mode capable of + handling communication with a smart card. + + For these microcontrollers, the Linux driver should be made capable of + working in both modes, and proper ioctls (see later) should be made + available at user-level to allow switching from one mode to the other, and + vice versa. + +3. DATA STRUCTURES ALREADY AVAILABLE IN THE KERNEL + + The Linux kernel provides the serial_iso7816 structure (see [1]) to handle + ISO7816 communications. This data structure is used to set and configure + ISO7816 parameters in ioctls. + + Any driver for devices capable of working both as RS232 and ISO7816 should + implement the iso7816_config callback in the uart_port structure. The + serial_core calls iso7816_config to do the device specific part in response + to TIOCGISO7816 and TIOCSISO7816 ioctls (see below). The iso7816_config + callback receives a pointer to struct serial_iso7816. + +4. USAGE FROM USER-LEVEL + + From user-level, ISO7816 configuration can be get/set using the previous + ioctls. For instance, to set ISO7816 you can use the following code: + + #include <linux/serial.h> + + /* Include definition for ISO7816 ioctls: TIOCSISO7816 and TIOCGISO7816 */ + #include <sys/ioctl.h> + + /* Open your specific device (e.g., /dev/mydevice): */ + int fd = open ("/dev/mydevice", O_RDWR); + if (fd < 0) { + /* Error handling. See errno. */ + } + + struct serial_iso7816 iso7816conf; + + /* Reserved fields as to be zeroed */ + memset(&iso7816conf, 0, sizeof(iso7816conf)); + + /* Enable ISO7816 mode: */ + iso7816conf.flags |= SER_ISO7816_ENABLED; + + /* Select the protocol: */ + /* T=0 */ + iso7816conf.flags |= SER_ISO7816_T(0); + /* or T=1 */ + iso7816conf.flags |= SER_ISO7816_T(1); + + /* Set the guard time: */ + iso7816conf.tg = 2; + + /* Set the clock frequency*/ + iso7816conf.clk = 3571200; + + /* Set transmission factors: */ + iso7816conf.sc_fi = 372; + iso7816conf.sc_di = 1; + + if (ioctl(fd_usart, TIOCSISO7816, &iso7816conf) < 0) { + /* Error handling. See errno. */ + } + + /* Use read() and write() syscalls here... */ + + /* Close the device when finished: */ + if (close (fd) < 0) { + /* Error handling. See errno. */ + } + +5. REFERENCES + + [1] include/uapi/linux/serial.h diff --git a/arch/alpha/include/uapi/asm/ioctls.h b/arch/alpha/include/uapi/asm/ioctls.h @@ -102,6 +102,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h @@ -93,6 +93,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h @@ -62,6 +62,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h @@ -102,6 +102,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h @@ -95,6 +95,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h @@ -27,6 +27,8 @@ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGRS485 _IOR('T', 0x41, struct serial_rs485) #define TIOCSRS485 _IOWR('T', 0x42, struct serial_rs485) +#define TIOCGISO7816 _IOR('T', 0x43, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x44, struct serial_iso7816) /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h @@ -107,6 +107,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/drivers/of/base.c b/drivers/of/base.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "OF: " fmt +#include <linux/bitmap.h> #include <linux/console.h> #include <linux/ctype.h> #include <linux/cpu.h> @@ -1986,6 +1987,59 @@ int of_alias_get_id(struct device_node *np, const char *stem) EXPORT_SYMBOL_GPL(of_alias_get_id); /** + * of_alias_get_alias_list - Get alias list for the given device driver + * @matches: Array of OF device match structures to search in + * @stem: Alias stem of the given device_node + * @bitmap: Bitmap field pointer + * @nbits: Maximum number of alias IDs which can be recorded in bitmap + * + * The function travels the lookup table to record alias ids for the given + * device match structures and alias stem. + * + * Return: 0 or -ENOSYS when !CONFIG_OF or + * -EOVERFLOW if alias ID is greater then allocated nbits + */ +int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + struct alias_prop *app; + int ret = 0; + + /* Zero bitmap field to make sure that all the time it is clean */ + bitmap_zero(bitmap, nbits); + + mutex_lock(&of_mutex); + pr_debug("%s: Looking for stem: %s\n", __func__, stem); + list_for_each_entry(app, &aliases_lookup, link) { + pr_debug("%s: stem: %s, id: %d\n", + __func__, app->stem, app->id); + + if (strcmp(app->stem, stem) != 0) { + pr_debug("%s: stem comparison didn't pass %s\n", + __func__, app->stem); + continue; + } + + if (of_match_node(matches, app->np)) { + pr_debug("%s: Allocated ID %d\n", __func__, app->id); + + if (app->id >= nbits) { + pr_warn("%s: ID %d >= than bitmap field %d\n", + __func__, app->id, nbits); + ret = -EOVERFLOW; + } else { + set_bit(app->id, bitmap); + } + } + } + mutex_unlock(&of_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(of_alias_get_alias_list); + +/** * of_alias_get_highest_id - Get highest alias id for the given stem * @stem: Alias stem to be examined * diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c @@ -128,8 +128,8 @@ static int find_console_handle(void) */ 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 @@ static int ehv_bc_tty_probe(struct platform_device *pdev) 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 @@ static int ehv_bc_tty_probe(struct platform_device *pdev) bc->rx_irq = irq_of_parse_and_map(np, 0); bc->tx_irq = irq_of_parse_and_map(np, 1); if ((bc->rx_irq == NO_IRQ) || (bc->tx_irq == NO_IRQ)) { - 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/n_tty.c b/drivers/tty/n_tty.c @@ -152,17 +152,28 @@ static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; } +/* If we are not echoing the data, perhaps this is a secret so erase it */ +static void zero_buffer(struct tty_struct *tty, u8 *buffer, int size) +{ + bool icanon = !!L_ICANON(tty); + bool no_echo = !L_ECHO(tty); + + if (icanon && no_echo) + memset(buffer, 0x00, size); +} + static int tty_copy_to_user(struct tty_struct *tty, void __user *to, size_t tail, size_t n) { struct n_tty_data *ldata = tty->disc_data; size_t size = N_TTY_BUF_SIZE - tail; - const void *from = read_buf_addr(ldata, tail); + void *from = read_buf_addr(ldata, tail); int uncopied; if (n > size) { tty_audit_add_data(tty, from, size); uncopied = copy_to_user(to, from, size); + zero_buffer(tty, from, size - uncopied); if (uncopied) return uncopied; to += size; @@ -171,7 +182,9 @@ static int tty_copy_to_user(struct tty_struct *tty, void __user *to, } tty_audit_add_data(tty, from, n); - return copy_to_user(to, from, n); + uncopied = copy_to_user(to, from, n); + zero_buffer(tty, from, n - uncopied); + return uncopied; } /** @@ -1960,11 +1973,12 @@ static int copy_from_read_buf(struct tty_struct *tty, n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); n = min(*nr, n); if (n) { - const unsigned char *from = read_buf_addr(ldata, tail); + unsigned char *from = read_buf_addr(ldata, tail); retval = copy_to_user(*b, from, n); n -= retval; is_eof = n == 1 && *from == EOF_CHAR(tty); tty_audit_add_data(tty, from, n); + zero_buffer(tty, from, n); smp_store_release(&ldata->read_tail, ldata->read_tail + n); /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c @@ -130,12 +130,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) 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); diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c @@ -58,7 +58,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, struct resource resource; struct device_node *np = ofdev->dev.of_node; u32 clk, spd, prop; - int ret; + int ret, irq; memset(port, 0, sizeof *port); @@ -143,21 +143,27 @@ static int of_platform_serial_setup(struct platform_device *ofdev, if (ret >= 0) port->line = ret; - port->irq = irq_of_parse_and_map(np, 0); - if (!port->irq) { - ret = -EPROBE_DEFER; - goto err_unprepare; + irq = of_irq_get(np, 0); + if (irq < 0) { + if (irq == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_unprepare; + } + /* IRQ support not mandatory */ + irq = 0; } + port->irq = irq; + info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); if (IS_ERR(info->rst)) { ret = PTR_ERR(info->rst); - goto err_dispose; + goto err_unprepare; } ret = reset_control_deassert(info->rst); if (ret) - goto err_dispose; + goto err_unprepare; port->type = type; port->uartclk = clk; @@ -184,8 +190,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->handle_irq = fsl8250_handle_irq; return 0; -err_dispose: - irq_dispose_mapping(port->irq); err_unprepare: clk_disable_unprepare(info->clk); err_pmruntime: diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c @@ -552,11 +552,30 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) */ static void serial8250_clear_fifos(struct uart_8250_port *p) { + unsigned char fcr; + unsigned char clr_mask = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT; + if (p->capabilities & UART_CAP_FIFO) { - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(p, UART_FCR, 0); + /* + * Make sure to avoid changing FCR[7:3] and ENABLE_FIFO bits. + * In case ENABLE_FIFO is not set, there is nothing to flush + * so just return. Furthermore, on certain implementations of + * the 8250 core, the FCR[7:3] bits may only be changed under + * specific conditions and changing them if those conditions + * are not met can have nasty side effects. One such core is + * the 8250-omap present in TI AM335x. + */ + fcr = serial_in(p, UART_FCR); + + /* FIFO is not enabled, there's nothing to clear. */ + if (!(fcr & UART_FCR_ENABLE_FIFO)) + return; + + fcr |= clr_mask; + serial_out(p, UART_FCR, fcr); + + fcr &= ~clr_mask; + serial_out(p, UART_FCR, fcr); } } @@ -1448,7 +1467,7 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p) * Enable previously disabled RX interrupts. */ if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { - serial8250_clear_and_reinit_fifos(p); + serial8250_clear_fifos(p); p->ier |= UART_IER_RLSI | UART_IER_RDI; serial_port_out(&p->port, UART_IER, p->ier); diff --git 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 @@ static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) 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; @@ -226,10 +187,25 @@ static int uniphier_uart_probe(struct platform_device *pdev) 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); up.port.dev = dev; @@ -241,10 +217,14 @@ static int uniphier_uart_probe(struct platform_device *pdev) 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 @@ -375,7 +375,7 @@ config SERIAL_8250_RT288X 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. diff --git 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 atmel_uart_port { 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,10 @@ struct atmel_uart_port { unsigned int pending_status; spinlock_t lock_suspended; + /* ISO7816 */ + unsigned int fidi_min; + unsigned int fidi_max; + #ifdef CONFIG_PM struct { u32 cr; @@ -361,6 +368,127 @@ static int atmel_config_rs485(struct uart_port *port, 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. */ @@ -480,8 +608,9 @@ static void atmel_stop_tx(struct uart_port *port) /* 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 (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) atmel_start_rx(port); } @@ -499,8 +628,9 @@ static void atmel_start_tx(struct uart_port *port) 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 (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) atmel_stop_rx(port); if (atmel_use_pdc_tx(port)) @@ -798,8 +928,9 @@ static void atmel_complete_tx_dma(void *arg) */ 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)) { + else if (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1282,6 +1413,9 @@ atmel_handle_status(struct uart_port *port, unsigned int pending, 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); } /* @@ -1374,8 +1508,9 @@ static void atmel_tx_pdc(struct uart_port *port) 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 (((port->rs485.flags & SER_RS485_ENABLED) && + !(port->rs485.flags & SER_RS485_RX_DURING_TX)) || + port->iso7816.flags & SER_ISO7816_ENABLED) { /* DMA done, stop TX, start RX for RS485 */ atmel_start_rx(port); } @@ -1727,6 +1862,22 @@ static void atmel_get_ip_name(struct uart_port *port) 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 { @@ -2100,6 +2251,17 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, 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) && @@ -2176,7 +2338,8 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, } 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; @@ -2357,6 +2520,7 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, port->mapbase = mpdev->resource[0].start; port->irq = mpdev->resource[1].start; port->rs485_config = atmel_config_rs485; + port->iso7816_config = atmel_config_iso7816; port->membase = NULL; memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); @@ -2380,8 +2544,12 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port, /* 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; diff --git 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/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1155,8 +1155,8 @@ static int cpm_uart_init_port(struct device_node *np, 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 +1164,8 @@ static int cpm_uart_init_port(struct device_node *np, 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/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c @@ -232,6 +232,8 @@ /* IMX lpuart has four extra unused regs located at the beginning */ #define IMX_REG_OFF 0x10 +static DEFINE_IDA(fsl_lpuart_ida); + struct lpuart_port { struct uart_port port; struct clk *clk; @@ -2143,8 +2145,11 @@ static int lpuart_probe(struct platform_device *pdev) 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); @@ -2246,6 +2251,8 @@ static int lpuart_remove(struct platform_device *pdev) uart_remove_one_port(&lpuart_reg, &sport->port); + ida_simple_remove(&fsl_lpuart_ida, sport->port.line); + clk_disable_unprepare(sport->clk); if (sport->dma_tx_chan) @@ -2384,6 +2391,7 @@ static int __init lpuart_serial_init(void) 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/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> @@ -706,27 +707,25 @@ static irqreturn_t imx_uart_rtsint(int irq, void *dev_id) { 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 +734,8 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) struct imx_port *sport = dev_id; unsigned int rx, flg, ignored = 0; struct tty_port *port = &sport->port.state->port; - 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 +795,7 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id) } out: - spin_unlock_irqrestore(&sport->port.lock, flags); + spin_unlock(&sport->port.lock); tty_flip_buffer_push(port); return IRQ_HANDLED; } @@ -903,13 +901,11 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id) } 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; } @@ -2384,8 +2380,13 @@ static int imx_uart_remove(struct platform_device *pdev) 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 +2399,15 @@ static void imx_uart_restore_context(struct imx_port *sport) 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 +2419,7 @@ static void imx_uart_save_context(struct imx_port *sport) 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 +2453,8 @@ static int imx_uart_suspend_noirq(struct device *dev) clk_disable(sport->clk_ipg); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -2455,6 +2463,8 @@ static int imx_uart_resume_noirq(struct device *dev) 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/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> @@ -128,19 +131,6 @@ static void kgdboc_unregister_kbd(void) #define kgdboc_restore_input() #endif /* ! CONFIG_KDB_KEYBOARD */ -static int kgdboc_option_setup(char *opt) -{ - if (strlen(opt) >= MAX_CONFIG_LEN) { - printk(KERN_ERR "kgdboc: config string too long\n"); - return -ENOSPC; - } - strcpy(config, opt); - - return 0; -} - -__setup("kgdboc=", kgdboc_option_setup); - static void cleanup_kgdboc(void) { if (kgdb_unregister_nmi_console()) @@ -154,15 +144,13 @@ static int configure_kgdboc(void) { struct tty_driver *p; int tty_line = 0; - int err; + int err = -ENODEV; char *cptr = config; struct console *cons; - err = kgdboc_option_setup(config); - if (err || !strlen(config) || isspace(config[0])) + if (!strlen(config) || isspace(config[0])) goto noconfig; - err = -ENODEV; kgdboc_io_ops.is_console = 0; kgdb_tty_driver = NULL; @@ -248,7 +236,7 @@ static int param_set_kgdboc_var(const char *kmessage, int 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; } @@ -259,8 +247,7 @@ static int param_set_kgdboc_var(const char *kmessage, } 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; } @@ -311,6 +298,25 @@ static struct kgdb_io kgdboc_io_ops = { }; #ifdef CONFIG_KGDB_SERIAL_CONSOLE +static int kgdboc_option_setup(char *opt) +{ + if (!opt) { + pr_err("config string not provided\n"); + return -EINVAL; + } + + if (strlen(opt) >= MAX_CONFIG_LEN) { + pr_err("config string too long\n"); + return -ENOSPC; + } + strcpy(config, opt); + + return 0; +} + +__setup("kgdboc=", kgdboc_option_setup); + + /* This is only available if kgdboc is a built in for early debugging */ static int __init kgdboc_early_init(char *opt) { diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c @@ -1634,8 +1634,9 @@ static int mxs_auart_request_gpio_irq(struct mxs_auart_port *s) /* * 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); diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c @@ -219,7 +219,7 @@ static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable) 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 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) port = &uap->port.state->port; while (1) { - error = 0; drop = 0; r1 = read_zsreg(uap, R1); @@ -273,7 +272,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) 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 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) * 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; diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c @@ -851,6 +851,23 @@ 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 proto; + + if (uart_console(uport)) + port->tx_bytes_pw = 1; + else + 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; + } + + qcom_geni_serial_stop_rx(uport); + + get_tx_fifo_size(port); set_rfr_wm(port); writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT); @@ -874,30 +891,19 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) 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) @@ -1056,6 +1062,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) 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 +1078,10 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) 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) @@ -1203,11 +1199,12 @@ static void qcom_geni_serial_pm(struct uart_port *uport, { 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 +1260,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) 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); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c @@ -1941,7 +1941,11 @@ static int s3c24xx_serial_resume(struct device *dev) 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 +1968,11 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) 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 @@ -328,6 +328,7 @@ struct sc16is7xx_port { 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 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) 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 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) /* 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); @@ -657,7 +675,7 @@ static void sc16is7xx_handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) +static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) { struct uart_port *port = &s->p[portno].port; @@ -666,7 +684,7 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG); if (iir & SC16IS7XX_IIR_NO_INT_BIT) - break; + return false; iir &= SC16IS7XX_IIR_ID_MASK; @@ -688,16 +706,27 @@ static void sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno) port->line, iir); break; } - } while (1); + } while (0); + return true; } static void sc16is7xx_ist(struct kthread_work *ws) { struct sc16is7xx_port *s = to_sc16is7xx_port(ws, irq_work); - int i; - for (i = 0; i < s->devtype->nr_uart; ++i) - sc16is7xx_port_irq(s, i); + mutex_lock(&s->efr_lock); + + while (1) { + bool keep_polling = false; + int i; + + for (i = 0; i < s->devtype->nr_uart; ++i) + keep_polling |= sc16is7xx_port_irq(s, i); + if (!keep_polling) + break; + } + + mutex_unlock(&s->efr_lock); } static irqreturn_t sc16is7xx_irq(int irq, void *dev_id) @@ -892,6 +921,9 @@ static void sc16is7xx_set_termios(struct uart_port *port, 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); @@ -913,6 +945,8 @@ static void sc16is7xx_set_termios(struct uart_port *port, /* 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, @@ -1178,6 +1212,7 @@ static int sc16is7xx_probe(struct device *dev, 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); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c @@ -1302,6 +1302,58 @@ static int uart_set_rs485_config(struct uart_port *port, 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. */ @@ -1371,6 +1423,14 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) 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); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c @@ -1516,7 +1516,7 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port, 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; } @@ -3414,6 +3414,12 @@ static int __init scif_early_console_setup(struct earlycon_device *device, { 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 +3438,7 @@ static int __init hscif_early_console_setup(struct earlycon_device *device, 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/sn_console.c b/drivers/tty/serial/sn_console.c @@ -888,7 +888,7 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) /* 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)) { + if (!spin_trylock_irqsave(&port->sc_port.lock, flags)) { int lhead = port->sc_port.state->xmit.head; int ltail = port->sc_port.state->xmit.tail; int counter, got_lock = 0; @@ -905,13 +905,11 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) */ 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; - } + if (stole_lock) + break; + + if (spin_trylock_irqsave(&port->sc_port.lock, flags)) { + got_lock = 1; break; } else { /* still locked */ @@ -938,7 +936,6 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) 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); diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c @@ -45,6 +45,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 +68,62 @@ #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 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 /* 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) - -struct reg_backup { - u32 ien; - u32 ctrl0; - u32 ctrl1; - u32 ctrl2; - u32 clkd0; - u32 clkd1; - u32 dspwait; -}; +#define SPRD_IMSR_BREAK_DETECT BIT(7) +#define SPRD_IMSR_TIMEOUT BIT(13) struct sprd_uart_port { struct uart_port port; - struct reg_backup reg_bak; char name[16]; }; 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 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; @@ -224,14 +221,15 @@ static inline void sprd_rx(struct 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--) { + 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)) + SPRD_LSR_FE | SPRD_LSR_OE)) if (handle_lsr_errors(port, &lsr, &flag)) continue; if (uart_handle_sysrq_char(port, ch)) @@ -294,8 +292,8 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id) 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) @@ -314,16 +312,17 @@ static int sprd_startup(struct uart_port *port) 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 */ @@ -334,7 +333,7 @@ static int sprd_startup(struct uart_port *port) sp = container_of(port, struct sprd_uart_port, port); snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line); 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); @@ -362,8 +361,8 @@ static void sprd_shutdown(struct uart_port *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; @@ -444,10 +443,11 @@ static void sprd_set_termios(struct uart_port *port, } /* 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 +480,7 @@ static void sprd_config_port(struct uart_port *port, int flags) 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; @@ -521,7 +520,7 @@ static void wait_for_xmitr(struct uart_port *port) 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 +530,7 @@ static void sprd_console_putchar(struct uart_port *port, int ch) } 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; @@ -594,23 +593,21 @@ static void sprd_putc(struct uart_port *port, int c) 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; @@ -692,8 +689,8 @@ static int sprd_probe(struct platform_device *pdev) 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; @@ -712,15 +709,12 @@ static int sprd_probe(struct platform_device *pdev) up->uartclk = clk_get_rate(clk); 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); diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c @@ -55,6 +55,11 @@ #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +/* 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; @@ -472,7 +477,7 @@ static void ulite_console_putchar(struct uart_port *port, int ch) 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 +511,8 @@ static int ulite_console_setup(struct console *co, char *options) 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 +544,6 @@ static struct console ulite_console = { .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 +655,17 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq, 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 +675,12 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq, 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; } @@ -776,13 +788,26 @@ static int ulite_probe(struct platform_device *pdev) pdata->clk = NULL; } - ret = clk_prepare(pdata->clk); + 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); + 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; + } + } + + ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); + + clk_disable(pdata->clk); + + return ret; } static int ulite_remove(struct platform_device *pdev) @@ -813,25 +838,9 @@ static struct platform_driver ulite_platform_driver = { 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) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c @@ -30,8 +30,6 @@ #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 @@ -180,7 +178,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @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,7 +188,9 @@ struct cdns_uart { 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; }; @@ -1003,13 +1005,12 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) 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 @@ -1217,7 +1218,7 @@ static void cdns_uart_console_write(struct console *co, const char *s, * * 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 +1238,8 @@ static int __init cdns_uart_console_setup(struct console *co, char *options) 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 +1250,12 @@ static struct uart_driver cdns_uart_uart_driver = { 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; - - /* 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); - } + struct cdns_uart *cdns_uart = port->private_data; + int may_wake; - /* - * 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)) { + may_wake = device_may_wakeup(device); + + if (console_suspend_enabled && may_wake) { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); @@ -1305,7 +1270,11 @@ static int cdns_uart_suspend(struct device *device) 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 +1286,14 @@ static int cdns_uart_suspend(struct device *device) 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; - - /* 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); - } + int may_wake; - if (console_suspend_enabled && !may_wake) { - struct cdns_uart *cdns_uart = port->private_data; + may_wake = device_may_wakeup(device); + if (console_suspend_enabled && !may_wake) { clk_enable(cdns_uart->pclk); clk_enable(cdns_uart->uartclk); @@ -1367,7 +1327,7 @@ static int cdns_uart_resume(struct device *device) 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 +1369,90 @@ static const struct of_device_id cdns_uart_of_match[] = { }; 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 +1461,16 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match); */ 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 +1480,63 @@ static int cdns_uart_probe(struct platform_device *pdev) 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 = CDNS_UART_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) + return -ENOMEM; + + 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; @@ -1446,7 +1552,8 @@ static int cdns_uart_probe(struct platform_device *pdev) } if (IS_ERR(cdns_uart_data->pclk)) { dev_err(&pdev->dev, "pclk clock not found.\n"); - return PTR_ERR(cdns_uart_data->pclk); + rc = PTR_ERR(cdns_uart_data->pclk); + goto err_out_unregister_driver; } cdns_uart_data->uartclk = devm_clk_get(&pdev->dev, "uart_clk"); @@ -1457,13 +1564,14 @@ static int cdns_uart_probe(struct platform_device *pdev) } if (IS_ERR(cdns_uart_data->uartclk)) { dev_err(&pdev->dev, "uart_clk clock not found.\n"); - return PTR_ERR(cdns_uart_data->uartclk); + rc = PTR_ERR(cdns_uart_data->uartclk); + goto err_out_unregister_driver; } 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 +1598,14 @@ static int cdns_uart_probe(struct platform_device *pdev) &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. @@ -1538,11 +1632,11 @@ static int cdns_uart_probe(struct platform_device *pdev) * 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,7 +1645,8 @@ static int cdns_uart_probe(struct platform_device *pdev) #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 @@ -1561,7 +1656,6 @@ 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 +1664,13 @@ err_out_clk_disable: 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 +1691,24 @@ static int cdns_uart_remove(struct platform_device *pdev) 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); + +#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE + if (console_port == port) + console_port = NULL; +#endif + + uart_unregister_driver(cdns_uart_data->cdns_uart_driver); return rc; } @@ -1613,28 +1724,14 @@ static struct platform_driver cdns_uart_platform_driver = { 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/tty_buffer.c b/drivers/tty/tty_buffer.c @@ -118,9 +118,12 @@ void tty_buffer_free_all(struct tty_port *port) 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 @@ void tty_buffer_free_all(struct tty_port *port) 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); } /** @@ -468,11 +473,15 @@ receive_buf(struct tty_port *port, struct tty_buffer *head, int count) { unsigned char *p = char_buf_ptr(head, head->read); char *f = NULL; + int n; if (~head->flags & TTYB_NORMAL) f = flag_buf_ptr(head, head->read); - return port->client_ops->receive_buf(port, p, f, count); + n = port->client_ops->receive_buf(port, p, f, count); + if (n > 0) + memset(p, 0, n); + return n; } /** diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c @@ -409,7 +409,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) mutex_lock(&tty_mutex); /* Search through the tty devices to look for a match */ list_for_each_entry(p, &tty_drivers, tty_drivers) { - if (strncmp(name, p->name, len) != 0) + if (!len || strncmp(name, p->name, len) != 0) continue; stp = str; if (*stp == ',') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c @@ -279,7 +279,6 @@ EXPORT_SYMBOL(tty_port_put); * 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 @@ EXPORT_SYMBOL(tty_port_tty_get); * 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 @@ out: * * Caller holds tty lock. */ - void tty_port_hangup(struct tty_port *port) { struct tty_struct *tty; @@ -399,7 +396,6 @@ EXPORT_SYMBOL_GPL(tty_port_tty_wakeup); * to hide some internal details. This will eventually become entirely * internal to the tty port. */ - int tty_port_carrier_raised(struct tty_port *port) { if (port->ops->carrier_raised == NULL) @@ -416,7 +412,6 @@ EXPORT_SYMBOL(tty_port_carrier_raised); * 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 @@ EXPORT_SYMBOL(tty_port_raise_dtr_rts); * 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 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts); * 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/include/linux/of.h b/include/linux/of.h @@ -388,6 +388,9 @@ extern int of_phandle_iterator_args(struct of_phandle_iterator *it, extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); extern int of_alias_get_highest_id(const char *stem); +extern int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits); extern int of_machine_is_compatible(const char *compat); @@ -898,6 +901,13 @@ static inline int of_alias_get_highest_id(const char *stem) return -ENOSYS; } +static inline int of_alias_get_alias_list(const struct of_device_id *matches, + const char *stem, unsigned long *bitmap, + unsigned int nbits) +{ + return -ENOSYS; +} + static inline int of_machine_is_compatible(const char *compat) { return 0; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h @@ -144,6 +144,8 @@ struct uart_port { void (*handle_break)(struct uart_port *); int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485); + int (*iso7816_config)(struct uart_port *, + struct serial_iso7816 *iso7816); unsigned int irq; /* irq number */ unsigned long irqflags; /* irq flags */ unsigned int uartclk; /* base uart clock */ @@ -260,6 +262,7 @@ struct uart_port { struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ struct serial_rs485 rs485; + struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ }; diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h @@ -79,6 +79,8 @@ #define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ #define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCGPTPEER _IO('T', 0x41) /* Safely open the slave */ +#define TIOCGISO7816 _IOR('T', 0x42, struct serial_iso7816) +#define TIOCSISO7816 _IOWR('T', 0x43, struct serial_iso7816) #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h @@ -132,4 +132,21 @@ struct serial_rs485 { are a royal PITA .. */ }; +/* + * Serial interface for controlling ISO7816 settings on chips with suitable + * support. Set with TIOCSISO7816 and get with TIOCGISO7816 if supported by + * your platform. + */ +struct serial_iso7816 { + __u32 flags; /* ISO7816 feature flags */ +#define SER_ISO7816_ENABLED (1 << 0) +#define SER_ISO7816_T_PARAM (0x0f << 4) +#define SER_ISO7816_T(t) (((t) & 0x0f) << 4) + __u32 tg; + __u32 sc_fi; + __u32 sc_di; + __u32 clk; + __u32 reserved[5]; +}; + #endif /* _UAPI_LINUX_SERIAL_H */