Commit 5bd4af34 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 738b04fb 59eaeba6
......@@ -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.
......
......@@ -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>;
};
......@@ -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.
......
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
......@@ -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
......
......@@ -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 */
......
......@@ -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
......
......@@ -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
......
......@@ -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 */
......
......@@ -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
......
......@@ -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)
......
......@@ -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>
......@@ -1985,6 +1986,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
......
......@@ -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;
}
......
......@@ -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 &&
......
......@@ -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);
......
......@@ -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:
......
......@@ -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);
......
......@@ -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;
......
......@@ -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.
......
......@@ -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);