提交 9abf8ace 编写于 作者: L Linus Torvalds

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

Pull tty/serial driver updates from Greg KH:
 "Here is the big set of tty and serial driver patches for 4.17-rc1

  Not all that big really, most are just small fixes and additions to
  existing drivers. There's a bunch of work on the imx serial driver
  recently for some reason, and a new embedded serial driver added as
  well.

  Full details are in the shortlog.

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

* tag 'tty-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (66 commits)
  serial: expose buf_overrun count through proc interface
  serial: mvebu-uart: fix tx lost characters
  tty: serial: msm_geni_serial: Fix return value check in qcom_geni_serial_probe()
  tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP
  8250-men-mcb: add support for 16z025 and 16z057
  powerpc: Mark the variable earlycon_acpi_spcr_enable maybe_unused
  serial: stm32: fix initialization of RS485 mode
  ARM: dts: STi: Remove "console=ttyASN" from bootargs for STi boards
  vt: change SGR 21 to follow the standards
  serdev: Fix typo in serdev_device_alloc
  ARM: dts: STi: Fix aliases property name for STi boards
  tty: st-asc: Update tty alias
  serial: stm32: add support for RS485 hardware control mode
  dt-bindings: serial: stm32: add RS485 optional properties
  selftests: add devpts selftests
  devpts: comment devpts_mntget()
  devpts: resolve devpts bind-mounts
  devpts: hoist out check for DEVPTS_SUPER_MAGIC
  serial: 8250: Add Nuvoton NPCM UART
  serial: mxs-auart: disable clks of Alphascale ASM9260
  ...
......@@ -24,6 +24,7 @@ Required properties:
- "ti,da830-uart"
- "aspeed,ast2400-vuart"
- "aspeed,ast2500-vuart"
- "nuvoton,npcm750-uart"
- "serial" if the port type is unknown.
- reg : offset and length of the register set for the device.
- interrupts : should contain uart interrupt.
......
......@@ -43,6 +43,8 @@ Required properties:
- "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART.
- "renesas,scif-r8a77970" for R8A77970 (R-Car V3M) SCIF compatible UART.
- "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-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.
......
......@@ -15,6 +15,8 @@ Required properties:
Optional properties:
- pinctrl: The reference on the pins configuration
- st,hw-flow-ctrl: bool flag to enable hardware flow control.
- rs485-rts-delay, rs485-rx-during-tx, rs485-rts-active-low,
linux,rs485-enabled-at-boot-time: see rs485.txt.
- dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt
- dma-names: "rx" and/or "tx"
......
......@@ -14,7 +14,7 @@
compatible = "st,stih407-b2120", "st,stih407";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
......@@ -24,7 +24,7 @@
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};
......
......@@ -14,7 +14,7 @@
compatible = "st,stih410-b2120", "st,stih410";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
......@@ -24,7 +24,7 @@
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};
......
......@@ -15,7 +15,7 @@
compatible = "st,stih410-b2260", "st,stih410";
chosen {
bootargs = "console=ttyAS1,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &uart1;
};
......@@ -25,7 +25,7 @@
};
aliases {
ttyAS1 = &uart1;
serial1 = &uart1;
ethernet0 = &ethernet0;
};
......
......@@ -14,7 +14,7 @@
compatible = "st,stih418-b2199", "st,stih418";
chosen {
bootargs = "console=ttyAS0,115200 clk_ignore_unused";
bootargs = "clk_ignore_unused";
linux,stdout-path = &sbc_serial0;
};
......@@ -24,7 +24,7 @@
};
aliases {
ttyAS0 = &sbc_serial0;
serial0 = &sbc_serial0;
ethernet0 = &ethernet0;
};
......
......@@ -156,9 +156,6 @@ static struct parport_pc_pci cards[] = {
/* sunix_2s1p */ { 1, { { 3, -1 }, } },
};
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
static struct pci_device_id parport_serial_pci_tbl[] = {
/* PCI cards */
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L,
......
......@@ -88,6 +88,16 @@ config HVC_DCC
driver. This console is used through a JTAG only on ARM. If you don't have
a JTAG then you probably don't want this option.
config HVC_RISCV_SBI
bool "RISC-V SBI console support"
depends on RISCV
select HVC_DRIVER
help
This enables support for console output via RISC-V SBI calls, which
is normally used only during boot to output printk.
If you don't know what do to here, say Y.
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
depends on PPC_PSERIES && HVC_CONSOLE
......
......@@ -9,4 +9,5 @@ obj-$(CONFIG_HVC_IRQ) += hvc_irq.o
obj-$(CONFIG_HVC_XEN) += hvc_xen.o
obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o
obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o
obj-$(CONFIG_HVCS) += hvcs.o
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2008 David Gibson, IBM Corporation
* Copyright (C) 2012 Regents of the University of California
* Copyright (C) 2017 SiFive
*/
#include <linux/console.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <asm/sbi.h>
#include "hvc_console.h"
static int hvc_sbi_tty_put(uint32_t vtermno, const char *buf, int count)
{
int i;
for (i = 0; i < count; i++)
sbi_console_putchar(buf[i]);
return i;
}
static int hvc_sbi_tty_get(uint32_t vtermno, char *buf, int count)
{
int i, c;
for (i = 0; i < count; i++) {
c = sbi_console_getchar();
if (c < 0)
break;
buf[i] = c;
}
return i;
}
static const struct hv_ops hvc_sbi_ops = {
.get_chars = hvc_sbi_tty_get,
.put_chars = hvc_sbi_tty_put,
};
static int __init hvc_sbi_init(void)
{
return PTR_ERR_OR_ZERO(hvc_alloc(0, 0, &hvc_sbi_ops, 16));
}
device_initcall(hvc_sbi_init);
static int __init hvc_sbi_console_init(void)
{
hvc_instantiate(0, 0, &hvc_sbi_ops);
add_preferred_console("hvc", 0, NULL);
return 0;
}
console_initcall(hvc_sbi_console_init);
......@@ -350,7 +350,7 @@ static struct bus_type serdev_bus_type = {
};
/**
* serdev_controller_alloc() - Allocate a new serdev device
* serdev_device_alloc() - Allocate a new serdev device
* @ctrl: associated controller
*
* Caller is responsible for either calling serdev_device_add() to add the
......
......@@ -9,6 +9,7 @@
* LCR is written whilst busy. If it is, then a busy detect interrupt is
* raised, the LCR needs to be rewritten and the uart status register read.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
......@@ -119,10 +120,27 @@ static void dw8250_check_lcr(struct uart_port *p, int value)
*/
}
/* Returns once the transmitter is empty or we run out of retries */
static void dw8250_tx_wait_empty(struct uart_port *p, int tries)
{
unsigned int lsr;
while (tries--) {
lsr = readb (p->membase + (UART_LSR << p->regshift));
if (lsr & UART_LSR_TEMT)
break;
udelay (10);
}
}
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
/* Allow the TX to drain before we reconfigure */
if (offset == UART_LCR)
dw8250_tx_wait_empty(p, 1000);
writeb(value, p->membase + (offset << p->regshift));
if (offset == UART_LCR && !d->uart_16550_compatible)
......@@ -339,18 +357,12 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_in = dw8250_serial_in32be;
p->serial_out = dw8250_serial_out32be;
}
} else if (has_acpi_companion(p->dev)) {
const struct acpi_device_id *id;
id = acpi_match_device(p->dev->driver->acpi_match_table,
p->dev);
if (id && !strcmp(id->id, "APMC0D08")) {
} else if (acpi_dev_present("APMC0D08", NULL, -1)) {
p->iotype = UPIO_MEM32;
p->regshift = 2;
p->serial_in = dw8250_serial_in32;
data->uart_16550_compatible = true;
}
}
/* Platforms with iDMA */
if (platform_get_resource_byname(to_platform_device(p->dev),
......
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/mcb.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <uapi/linux/serial_core.h>
#define MEN_UART_ID_Z025 0x19
#define MEN_UART_ID_Z057 0x39
#define MEN_UART_ID_Z125 0x7d
#define MEN_UART_MEM_SIZE 0x10
struct serial_8250_men_mcb_data {
struct uart_8250_port uart;
int line;
......@@ -18,7 +25,7 @@ struct serial_8250_men_mcb_data {
* parameter in order to really set the correct baudrate, and
* do so if possible without user interaction
*/
static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
static u32 men_lookup_uartclk(struct mcb_device *mdev)
{
/* use default value if board is not available below */
u32 clkval = 1041666;
......@@ -32,6 +39,8 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
clkval = 1843200;
else if (strncmp(mdev->bus->name, "G215", 4) == 0)
clkval = 1843200;
else if (strncmp(mdev->bus->name, "F210", 4) == 0)
clkval = 115200;
else
dev_info(&mdev->dev,
"board not detected, using default uartclk\n");
......@@ -41,62 +50,108 @@ static u32 men_z125_lookup_uartclk(struct mcb_device *mdev)
return clkval;
}
static unsigned int get_num_ports(struct mcb_device *mdev,
void __iomem *membase)
{
switch (mdev->id) {
case MEN_UART_ID_Z125:
return 1U;
case MEN_UART_ID_Z025:
return readb(membase) >> 4;
case MEN_UART_ID_Z057:
return 4U;
default:
dev_err(&mdev->dev, "no supported device!\n");
return -ENODEV;
}
}
static int serial_8250_men_mcb_probe(struct mcb_device *mdev,
const struct mcb_device_id *id)
{
struct serial_8250_men_mcb_data *data;
struct resource *mem;
unsigned int num_ports;
unsigned int i;
void __iomem *membase;
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
if (mem == NULL)
return -ENXIO;
membase = devm_ioremap_resource(&mdev->dev, mem);
if (IS_ERR(membase))
return PTR_ERR_OR_ZERO(membase);
num_ports = get_num_ports(mdev, membase);
dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n",
mdev->id, num_ports);
if (num_ports == 0 || num_ports > 4) {
dev_err(&mdev->dev, "unexpected number of ports: %u\n",
num_ports);
return -ENODEV;
}
data = devm_kzalloc(&mdev->dev,
data = devm_kcalloc(&mdev->dev, num_ports,
sizeof(struct serial_8250_men_mcb_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
mcb_set_drvdata(mdev, data);
data->uart.port.dev = mdev->dma_dev;
spin_lock_init(&data->uart.port.lock);
data->uart.port.type = PORT_16550;
data->uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
data->uart.port.iotype = UPIO_MEM;
data->uart.port.uartclk = men_z125_lookup_uartclk(mdev);
data->uart.port.regshift = 0;
data->uart.port.fifosize = 60;
mem = mcb_get_resource(mdev, IORESOURCE_MEM);
if (mem == NULL)
return -ENXIO;
data->uart.port.irq = mcb_get_irq(mdev);
data->uart.port.membase = devm_ioremap_resource(&mdev->dev, mem);
if (IS_ERR(data->uart.port.membase))
return PTR_ERR_OR_ZERO(data->uart.port.membase);
data->uart.port.mapbase = (unsigned long) mem->start;
data->uart.port.iobase = data->uart.port.mapbase;
for (i = 0; i < num_ports; i++) {
data[i].uart.port.dev = mdev->dma_dev;
spin_lock_init(&data[i].uart.port.lock);
data[i].uart.port.type = PORT_16550;
data[i].uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ
| UPF_FIXED_TYPE;
data[i].uart.port.iotype = UPIO_MEM;
data[i].uart.port.uartclk = men_lookup_uartclk(mdev);
data[i].uart.port.regshift = 0;
data[i].uart.port.irq = mcb_get_irq(mdev);
data[i].uart.port.membase = membase;
data[i].uart.port.fifosize = 60;
data[i].uart.port.mapbase = (unsigned long) mem->start
+ i * MEN_UART_MEM_SIZE;
data[i].uart.port.iobase = data[i].uart.port.mapbase;
/* ok, register the port */
data->line = serial8250_register_8250_port(&data->uart);
if (data->line < 0)
return data->line;
dev_info(&mdev->dev, "found 16Z125 UART: ttyS%d\n", data->line);
data[i].line = serial8250_register_8250_port(&data[i].uart);
if (data[i].line < 0) {
dev_err(&mdev->dev, "unable to register UART port\n");
return data[i].line;
}
dev_info(&mdev->dev, "found MCB UART: ttyS%d\n", data[i].line);
}
return 0;
}
static void serial_8250_men_mcb_remove(struct mcb_device *mdev)
{
unsigned int num_ports, i;
struct serial_8250_men_mcb_data *data = mcb_get_drvdata(mdev);
if (data)
serial8250_unregister_port(data->line);
if (!data)
return;
num_ports = get_num_ports(mdev, data[0].uart.port.membase);
if (num_ports < 0 || num_ports > 4) {
dev_err(&mdev->dev, "error retrieving number of ports!\n");
return;
}
for (i = 0; i < num_ports; i++)
serial8250_unregister_port(data[i].line);
}
static const struct mcb_device_id serial_8250_men_mcb_ids[] = {
{ .device = 0x7d },
{ .device = MEN_UART_ID_Z025 },
{ .device = MEN_UART_ID_Z057 },
{ .device = MEN_UART_ID_Z125 },
{ }
};
MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids);
......@@ -113,6 +168,8 @@ static struct mcb_driver mcb_driver = {
module_mcb_driver(mcb_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MEN 16z125 8250 UART driver");
MODULE_DESCRIPTION("MEN 8250 UART driver");
MODULE_AUTHOR("Michael Moese <michael.moese@men.de");
MODULE_ALIAS("mcb:16z125");
MODULE_ALIAS("mcb:16z025");
MODULE_ALIAS("mcb:16z057");
......@@ -316,6 +316,7 @@ static const struct of_device_id of_platform_serial_table[] = {
{ .compatible = "mrvl,mmp-uart",
.data = (void *)PORT_XSCALE, },
{ .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, },
{ .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, of_platform_serial_table);
......
......@@ -114,6 +114,7 @@ struct omap8250_priv {
struct uart_8250_dma omap8250_dma;
spinlock_t rx_dma_lock;
bool rx_dma_broken;
bool throttled;
};
#ifdef CONFIG_SERIAL_8250_DMA
......@@ -692,6 +693,7 @@ static void omap_8250_shutdown(struct uart_port *port)
static void omap_8250_throttle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
......@@ -700,6 +702,7 @@ static void omap_8250_throttle(struct uart_port *port)
spin_lock_irqsave(&port->lock, flags);
up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
serial_out(up, UART_IER, up->ier);
priv->throttled = true;
spin_unlock_irqrestore(&port->lock, flags);
pm_runtime_mark_last_busy(port->dev);
......@@ -738,12 +741,16 @@ static int omap_8250_rs485_config(struct uart_port *port,
static void omap_8250_unthrottle(struct uart_port *port)
{
struct omap8250_priv *priv = port->private_data;
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
pm_runtime_get_sync(port->dev);
spin_lock_irqsave(&port->lock, flags);
priv->throttled = false;
if (up->dma)
up->dma->rx_dma(up);
up->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_out(up, UART_IER, up->ier);
spin_unlock_irqrestore(&port->lock, flags);
......@@ -788,6 +795,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
static void __dma_rx_complete(void *param)
{
struct uart_8250_port *p = param;
struct omap8250_priv *priv = p->port.private_data;
struct uart_8250_dma *dma = p->dma;
struct dma_tx_state state;
unsigned long flags;
......@@ -805,6 +813,7 @@ static void __dma_rx_complete(void *param)
return;
}
__dma_rx_do_complete(p);
if (!priv->throttled)
omap_8250_rx_dma(p);
spin_unlock_irqrestore(&p->port.lock, flags);
......
......@@ -1685,9 +1685,6 @@ pci_wch_ch38x_setup(struct serial_private *priv,
#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
#define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 0x818e
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
#define PCIE_VENDOR_ID_WCH 0x1c00
#define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250
#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470
......
......@@ -47,6 +47,10 @@
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
#define UART_EXAR_DVID 0x8d /* Device identification */
/* Nuvoton NPCM timeout register */
#define UART_NPCM_TOR 7
#define UART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */
/*
* Debugging.
*/
......@@ -293,6 +297,15 @@ static const struct serial8250_config uart_config[] = {
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.flags = UART_CAP_FIFO,
},
[PORT_NPCM] = {
.name = "Nuvoton 16550",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
.rxtrig_bytes = {1, 4, 8, 14},
.flags = UART_CAP_FIFO,
},
};
/* Uart divisor latch read */
......@@ -1854,7 +1867,8 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
status = serial_port_in(port, UART_LSR);
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (status & (UART_LSR_DR | UART_LSR_BI) &&
iir & UART_IIR_RDI) {
if (!up->dma || handle_rx_dma(up, iir))
status = serial8250_rx_chars(up, status);
}
......@@ -2140,6 +2154,15 @@ int serial8250_do_startup(struct uart_port *port)
UART_DA830_PWREMU_MGMT_FREE);
}
if (port->type == PORT_NPCM) {
/*
* Nuvoton calls the scratch register 'UART_TOR' (timeout
* register). Enable it, and set TIOC (timeout interrupt
* comparator) to be 0x20 for correct operation.
*/
serial_port_out(port, UART_NPCM_TOR, UART_NPCM_TOIE | 0x20);
}
#ifdef CONFIG_SERIAL_8250_RSA
/*
* If this is an RSA port, see if we can kick it up to the
......@@ -2462,6 +2485,15 @@ static unsigned int xr17v35x_get_divisor(struct uart_8250_port *up,
return quot_16 >> 4;
}
/* Nuvoton NPCM UARTs have a custom divisor calculation */
static unsigned int npcm_get_divisor(struct uart_8250_port *up,
unsigned int baud)
{
struct uart_port *port = &up->port;
return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2;
}
static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
unsigned int baud,
unsigned int *frac)
......@@ -2482,6 +2514,8 @@ static unsigned int serial8250_get_divisor(struct uart_8250_port *up,
quot = 0x8002;
else if (up->port.type == PORT_XR17V35X)
quot = xr17v35x_get_divisor(up, baud, frac);
else if (up->port.type == PORT_NPCM)
quot = npcm_get_divisor(up, baud);
else
quot = uart_get_divisor(port, baud);
......
......@@ -157,11 +157,12 @@ config SERIAL_8250_CS
If unsure, say N.
config SERIAL_8250_MEN_MCB
tristate "MEN Z125 UART device support"
tristate "MEN MCB UART device support"
depends on MCB && SERIAL_8250
help
This enables support for FPGA based UARTs found on many MEN
boards. This driver enables support for the Z125 UARTs.
boards. This driver enables support for the 16z025, 16z057
and 16z125 UARTs.
To compile this driver as a module, chose M here: the
module will be called 8250_men_mcb.
......
......@@ -989,6 +989,21 @@ config SERIAL_MSM_CONSOLE
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
config SERIAL_QCOM_GENI
tristate "QCOM on-chip GENI based serial port support"
depends on ARCH_QCOM || COMPILE_TEST
depends on QCOM_GENI_SE
select SERIAL_CORE
config SERIAL_QCOM_GENI_CONSOLE
bool "QCOM GENI Serial Console support"
depends on SERIAL_QCOM_GENI=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Serial console driver for Qualcomm Technologies Inc's GENI based
QUP hardware.
config SERIAL_VT8500
bool "VIA VT8500 on-chip serial port support"
depends on ARCH_VT8500
......
......@@ -58,6 +58,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
......
......@@ -109,6 +109,20 @@ static unsigned int altera_uart_get_mctrl(struct uart_port *port)
return sigs;
}
static void altera_uart_update_ctrl_reg(struct altera_uart *pp)
{
unsigned short imr = pp->imr;
/*
* If the device doesn't have an irq, ensure that the irq bits are
* masked out to keep the irq line inactive.
*/
if (!pp->port.irq)
imr &= ALTERA_UART_CONTROL_TRBK_MSK | ALTERA_UART_CONTROL_RTS_MSK;
altera_uart_writel(&pp->port, imr, ALTERA_UART_CONTROL_REG);
}
static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
......@@ -118,7 +132,7 @@ static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_start_tx(struct uart_port *port)
......@@ -126,7 +140,7 @@ static void altera_uart_start_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_tx(struct uart_port *port)
......@@ -134,7 +148,7 @@ static void altera_uart_stop_tx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_stop_rx(struct uart_port *port)
......@@ -142,7 +156,7 @@ static void altera_uart_stop_rx(struct uart_port *port)
struct altera_uart *pp = container_of(port, struct altera_uart, port);
pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
static void altera_uart_break_ctl(struct uart_port *port, int break_state)
......@@ -155,7 +169,7 @@ static void altera_uart_break_ctl(struct uart_port *port, int break_state)
pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
else
pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
}
......@@ -262,7 +276,7 @@ static void altera_uart_tx_chars(struct altera_uart *pp)
if (xmit->head == xmit->tail) {
pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
}
}
......@@ -307,13 +321,12 @@ static int altera_uart_startup(struct uart_port *port)
{
struct altera_uart *pp = container_of(port, struct altera_uart, port);
unsigned long flags;
int ret;
if (!port->irq) {
timer_setup(&pp->tmr, altera_uart_timer, 0);
mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
return 0;
}
} else {
int ret;
ret = request_irq(port->irq, altera_uart_interrupt, 0,
DRV_NAME, port);
......@@ -322,12 +335,13 @@ static int altera_uart_startup(struct uart_port *port)
"interrupt vector=%d\n", port->line, port->irq);
return ret;
}
}
spin_lock_irqsave(&port->lock, flags);
/* Enable RX interrupts now */
pp->imr = ALTERA_UART_CONTROL_RRDY_MSK;
writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
......@@ -343,7 +357,7 @@ static void altera_uart_shutdown(struct uart_port *port)
/* Disable all interrupts now */
pp->imr = 0;
writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_update_ctrl_reg(pp);
spin_unlock_irqrestore(&port->lock, flags);
......@@ -432,7 +446,7 @@ static void altera_uart_console_putc(struct uart_port *port, int c)
ALTERA_UART_STATUS_TRDY_MSK))
cpu_relax();
writel(c, port->membase + ALTERA_UART_TXDATA_REG);
altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
}
static void altera_uart_console_write(struct console *co, const char *s,
......@@ -502,13 +516,13 @@ static int __init altera_uart_earlycon_setup(struct earlycon_device *dev,
return -ENODEV;
/* Enable RX interrupts now */
writel(ALTERA_UART_CONTROL_RRDY_MSK,
port->membase + ALTERA_UART_CONTROL_REG);
altera_uart_writel(port, ALTERA_UART_CONTROL_RRDY_MSK,
ALTERA_UART_CONTROL_REG);
if (dev->baud) {
unsigned int baudclk = port->uartclk / dev->baud;
writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG);
altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
}
dev->con->write = altera_uart_earlycon_write;
......
......@@ -593,6 +593,11 @@ static int arc_serial_probe(struct platform_device *pdev)
if (dev_id < 0)
dev_id = 0;
if (dev_id >= ARRAY_SIZE(arc_uart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", dev_id);
return -EINVAL;
}
uart = &arc_uart_ports[dev_id];
port = &uart->port;
......
......@@ -2145,6 +2145,10 @@ static int lpuart_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
return ret;
}
if (ret >= ARRAY_SIZE(lpuart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", ret);
return -EINVAL;
}
sport->port.line = ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sport->port.membase = devm_ioremap_resource(&pdev->dev, res);
......
......@@ -71,12 +71,12 @@
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_RXDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
#define UCR1_TXDMAEN (1<<3) /* Transmitter ready DMA enable */
#define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
#define UCR1_ATDMAEN (1<<2) /* Aging DMA Timer Enable */
#define UCR1_DOZE (1<<1) /* Doze */
......@@ -204,8 +204,14 @@ struct imx_port {
struct mctrl_gpios *gpios;
/* shadow registers */
unsigned int ucr1;
unsigned int ucr2;
unsigned int ucr3;
unsigned int ucr4;
unsigned int ufcr;
/* DMA fields */
unsigned int dma_is_inited:1;
unsigned int dma_is_enabled:1;
unsigned int dma_is_rxing:1;
unsigned int dma_is_txing:1;
......@@ -274,27 +280,81 @@ static const struct of_device_id imx_uart_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
static inline unsigned uts_reg(struct imx_port *sport)
static void imx_uart_writel(struct imx_port *sport, u32 val, u32 offset)
{
switch (offset) {
case UCR1:
sport->ucr1 = val;
break;
case UCR2:
sport->ucr2 = val;
break;
case UCR3:
sport->ucr3 = val;
break;
case UCR4:
sport->ucr4 = val;
break;
case UFCR:
sport->ufcr = val;
break;
default:
break;
}
writel(val, sport->port.membase + offset);
}
static u32 imx_uart_readl(struct imx_port *sport, u32 offset)
{
switch (offset) {
case UCR1:
return sport->ucr1;
break;
case UCR2:
/*
* UCR2_SRST is the only bit in the cached registers that might
* differ from the value that was last written. As it only
* clears after being set, reread conditionally.
*/
if (sport->ucr2 & UCR2_SRST)
sport->ucr2 = readl(sport->port.membase + offset);
return sport->ucr2;
break;
case UCR3:
return sport->ucr3;
break;
case UCR4:
return sport->ucr4;
break;
case UFCR:
return sport->ufcr;
break;
default:
return readl(sport->port.membase + offset);
}
}
static inline unsigned imx_uart_uts_reg(struct imx_port *sport)
{
return sport->devdata->uts_reg;
}
static inline int is_imx1_uart(struct imx_port *sport)
static inline int imx_uart_is_imx1(struct imx_port *sport)
{
return sport->devdata->devtype == IMX1_UART;
}
static inline int is_imx21_uart(struct imx_port *sport)
static inline int imx_uart_is_imx21(struct imx_port *sport)
{
return sport->devdata->devtype == IMX21_UART;
}
static inline int is_imx53_uart(struct imx_port *sport)
static inline int imx_uart_is_imx53(struct imx_port *sport)
{
return sport->devdata->devtype == IMX53_UART;
}
static inline int is_imx6q_uart(struct imx_port *sport)
static inline int imx_uart_is_imx6q(struct imx_port *sport)
{
return sport->devdata->devtype == IMX6Q_UART;
}
......@@ -302,26 +362,26 @@ static inline int is_imx6q_uart(struct imx_port *sport)
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
#if defined(CONFIG_SERIAL_IMX_CONSOLE)
static void imx_port_ucrs_save(struct uart_port *port,
static void imx_uart_ucrs_save(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* save control registers */
ucr->ucr1 = readl(port->membase + UCR1);
ucr->ucr2 = readl(port->membase + UCR2);
ucr->ucr3 = readl(port->membase + UCR3);
ucr->ucr1 = imx_uart_readl(sport, UCR1);
ucr->ucr2 = imx_uart_readl(sport, UCR2);
ucr->ucr3 = imx_uart_readl(sport, UCR3);
}
static void imx_port_ucrs_restore(struct uart_port *port,
static void imx_uart_ucrs_restore(struct imx_port *sport,
struct imx_port_ucrs *ucr)
{
/* restore control registers */
writel(ucr->ucr1, port->membase + UCR1);
writel(ucr->ucr2, port->membase + UCR2);
writel(ucr->ucr3, port->membase + UCR3);
imx_uart_writel(sport, ucr->ucr1, UCR1);
imx_uart_writel(sport, ucr->ucr2, UCR2);
imx_uart_writel(sport, ucr->ucr3, UCR3);
}
#endif
static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
static void imx_uart_rts_active(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
......@@ -329,7 +389,7 @@ static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
{
*ucr2 &= ~UCR2_CTSC;
*ucr2 |= UCR2_CTS;
......@@ -338,75 +398,91 @@ static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2)
static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
{
*ucr2 |= UCR2_CTSC;
}
/*
* interrupts disabled on entry
*/
static void imx_stop_tx(struct uart_port *port)
/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
unsigned int ucr1, ucr2;
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 |= UCR2_RXEN;
if (sport->dma_is_enabled) {
ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN;
} else {
ucr1 |= UCR1_RRDYEN;
ucr2 |= UCR2_ATEN;
}
/* Write UCR2 first as it includes RXEN */
imx_uart_writel(sport, ucr2, UCR2);
imx_uart_writel(sport, ucr1, UCR1);
}
/* called with port.lock taken and irqs off */
static void imx_uart_stop_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
u32 ucr1;
/*
* We are maybe in the SMP context, so if the DMA TX thread is running
* on other cpu, we have to wait for it to finish.
*/
if (sport->dma_is_enabled && sport->dma_is_txing)
if (sport->dma_is_txing)
return;
temp = readl(port->membase + UCR1);
writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1);
/* in rs485 mode disable transmitter if shifter is empty */
if (port->rs485.flags & SER_RS485_ENABLED &&
readl(port->membase + USR2) & USR2_TXDC) {
temp = readl(port->membase + UCR2);
imx_uart_readl(sport, USR2) & USR2_TXDC) {
u32 ucr2 = imx_uart_readl(sport, UCR2), ucr4;
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
imx_port_rts_active(sport, &temp);
imx_uart_rts_active(sport, &ucr2);
else
imx_port_rts_inactive(sport, &temp);
temp |= UCR2_RXEN;
writel(temp, port->membase + UCR2);
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
imx_uart_start_rx(port);
temp = readl(port->membase + UCR4);
temp &= ~UCR4_TCEN;
writel(temp, port->membase + UCR4);
ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
}
}
/*
* interrupts disabled on entry
*/
static void imx_stop_rx(struct uart_port *port)
/* called with port.lock taken and irqs off */
static void imx_uart_stop_rx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
u32 ucr1, ucr2;
if (sport->dma_is_enabled && sport->dma_is_rxing) {
if (sport->port.suspended) {
dmaengine_terminate_all(sport->dma_chan_rx);
sport->dma_is_rxing = 0;
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
if (sport->dma_is_enabled) {
ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN);
} else {
return;
ucr1 &= ~UCR1_RRDYEN;
ucr2 &= ~UCR2_ATEN;
}
}
temp = readl(sport->port.membase + UCR2);
writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
imx_uart_writel(sport, ucr1, UCR1);
/* disable the `Receiver Ready Interrrupt` */
temp = readl(sport->port.membase + UCR1);
writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1);
ucr2 &= ~UCR2_RXEN;
imx_uart_writel(sport, ucr2, UCR2);
}
/*
* Set the modem control timer to fire immediately.
*/
static void imx_enable_ms(struct uart_port *port)
/* called with port.lock taken and irqs off */
static void imx_uart_enable_ms(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
......@@ -415,49 +491,50 @@ static void imx_enable_ms(struct uart_port *port)
mctrl_gpio_enable_ms(sport->gpios);
}
static void imx_dma_tx(struct imx_port *sport);
static inline void imx_transmit_buffer(struct imx_port *sport)
static void imx_uart_dma_tx(struct imx_port *sport);
/* called with port.lock taken and irqs off */
static inline void imx_uart_transmit_buffer(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long temp;
if (sport->port.x_char) {
/* Send next char */
writel(sport->port.x_char, sport->port.membase + URTX0);
imx_uart_writel(sport, sport->port.x_char, URTX0);
sport->port.icount.tx++;
sport->port.x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
imx_stop_tx(&sport->port);
imx_uart_stop_tx(&sport->port);
return;
}
if (sport->dma_is_enabled) {
u32 ucr1;
/*
* We've just sent a X-char Ensure the TX DMA is enabled
* and the TX IRQ is disabled.
**/
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TXMPTYEN;
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXMPTYEN;
if (sport->dma_is_txing) {
temp |= UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
} else {
writel(temp, sport->port.membase + UCR1);
imx_dma_tx(sport);
}
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_dma_tx(sport);
}
if (sport->dma_is_txing)
return;
}
while (!uart_circ_empty(xmit) &&
!(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
!(imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)) {
/* send xmit->buf[xmit->tail]
* out the port here */
writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
imx_uart_writel(sport, xmit->buf[xmit->tail], URTX0);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
sport->port.icount.tx++;
}
......@@ -466,24 +543,24 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
uart_write_wakeup(&sport->port);
if (uart_circ_empty(xmit))
imx_stop_tx(&sport->port);
imx_uart_stop_tx(&sport->port);
}
static void dma_tx_callback(void *data)
static void imx_uart_dma_tx_callback(void *data)
{
struct imx_port *sport = data;
struct scatterlist *sgl = &sport->tx_sgl[0];
struct circ_buf *xmit = &sport->port.state->xmit;
unsigned long flags;
unsigned long temp;
u32 ucr1;
spin_lock_irqsave(&sport->port.lock, flags);
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
/* update the stat */
xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
......@@ -497,24 +574,34 @@ static void dma_tx_callback(void *data)
uart_write_wakeup(&sport->port);
if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
imx_dma_tx(sport);
imx_uart_dma_tx(sport);
else if (sport->port.rs485.flags & SER_RS485_ENABLED) {
u32 ucr4 = imx_uart_readl(sport, UCR4);
ucr4 |= UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
}
spin_unlock_irqrestore(&sport->port.lock, flags);
}
static void imx_dma_tx(struct imx_port *sport)
/* called with port.lock taken and irqs off */
static void imx_uart_dma_tx(struct imx_port *sport)
{
struct circ_buf *xmit = &sport->port.state->xmit;
struct scatterlist *sgl = sport->tx_sgl;
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = sport->dma_chan_tx;
struct device *dev = sport->port.dev;
unsigned long temp;
u32 ucr1, ucr4;
int ret;
if (sport->dma_is_txing)
return;
ucr4 = imx_uart_readl(sport, UCR4);
ucr4 &= ~UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
sport->tx_bytes = uart_circ_chars_pending(xmit);
if (xmit->tail < xmit->head) {
......@@ -541,15 +628,15 @@ static void imx_dma_tx(struct imx_port *sport)
dev_err(dev, "We cannot prepare for the TX slave dma!\n");
return;
}
desc->callback = dma_tx_callback;
desc->callback = imx_uart_dma_tx_callback;
desc->callback_param = sport;
dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
uart_circ_chars_pending(xmit));
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
/* fire it */
sport->dma_is_txing = 1;
......@@ -558,99 +645,110 @@ static void imx_dma_tx(struct imx_port *sport)
return;
}
/*
* interrupts disabled on entry
*/
static void imx_start_tx(struct uart_port *port)
/* called with port.lock taken and irqs off */
static void imx_uart_start_tx(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
u32 ucr1;
if (!sport->port.x_char && uart_circ_empty(&port->state->xmit))
return;
if (port->rs485.flags & SER_RS485_ENABLED) {
temp = readl(port->membase + UCR2);
u32 ucr2;
ucr2 = imx_uart_readl(sport, UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
imx_port_rts_active(sport, &temp);
imx_uart_rts_active(sport, &ucr2);
else
imx_port_rts_inactive(sport, &temp);
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
temp &= ~UCR2_RXEN;
writel(temp, port->membase + UCR2);
imx_uart_stop_rx(port);
/* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR4);
temp |= UCR4_TCEN;
writel(temp, port->membase + UCR4);
/*
* Enable transmitter and shifter empty irq only if DMA is off.
* In the DMA case this is done in the tx-callback.
*/
if (!sport->dma_is_enabled) {
u32 ucr4 = imx_uart_readl(sport, UCR4);
ucr4 |= UCR4_TCEN;
imx_uart_writel(sport, ucr4, UCR4);
}
}
if (!sport->dma_is_enabled) {
temp = readl(sport->port.membase + UCR1);
writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
}
if (sport->dma_is_enabled) {
if (sport->port.x_char) {
/* We have X-char to send, so enable TX IRQ and
* disable TX DMA to let TX interrupt to send X-char */
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TDMAEN;
temp |= UCR1_TXMPTYEN;
writel(temp, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
ucr1 |= UCR1_TXMPTYEN;
imx_uart_writel(sport, ucr1, UCR1);
return;
}
if (!uart_circ_empty(&port->state->xmit) &&
!uart_tx_stopped(port))
imx_dma_tx(sport);
imx_uart_dma_tx(sport);
return;
}
}
static irqreturn_t imx_rtsint(int irq, void *dev_id)
static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int val;
u32 usr1;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
writel(USR1_RTSD, sport->port.membase + USR1);
val = readl(sport->port.membase + USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!val);
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);
return IRQ_HANDLED;
}
static irqreturn_t imx_txint(int irq, void *dev_id)
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);
imx_transmit_buffer(sport);
imx_uart_transmit_buffer(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
return IRQ_HANDLED;
}
static irqreturn_t imx_rxint(int irq, void *dev_id)
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, temp;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
while (readl(sport->port.membase + USR2) & USR2_RDR) {
while (imx_uart_readl(sport, USR2) & USR2_RDR) {
u32 usr2;
flg = TTY_NORMAL;
sport->port.icount.rx++;
rx = readl(sport->port.membase + URXD0);
rx = imx_uart_readl(sport, URXD0);
temp = readl(sport->port.membase + USR2);
if (temp & USR2_BRCD) {
writel(USR2_BRCD, sport->port.membase + USR2);
usr2 = imx_uart_readl(sport, USR2);
if (usr2 & USR2_BRCD) {
imx_uart_writel(sport, USR2_BRCD, USR2);
if (uart_handle_break(&sport->port))
continue;
}
......@@ -703,16 +801,16 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
return IRQ_HANDLED;
}
static void clear_rx_errors(struct imx_port *sport);
static void imx_uart_clear_rx_errors(struct imx_port *sport);
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
static unsigned int imx_get_hwmctrl(struct imx_port *sport)
static unsigned int imx_uart_get_hwmctrl(struct imx_port *sport)
{
unsigned int tmp = TIOCM_DSR;
unsigned usr1 = readl(sport->port.membase + USR1);
unsigned usr2 = readl(sport->port.membase + USR2);
unsigned usr1 = imx_uart_readl(sport, USR1);
unsigned usr2 = imx_uart_readl(sport, USR2);
if (usr1 & USR1_RTSS)
tmp |= TIOCM_CTS;
......@@ -722,7 +820,7 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport)
tmp |= TIOCM_CAR;
if (sport->dte_mode)
if (!(readl(sport->port.membase + USR2) & USR2_RIIN))
if (!(imx_uart_readl(sport, USR2) & USR2_RIIN))
tmp |= TIOCM_RI;
return tmp;
......@@ -731,11 +829,11 @@ static unsigned int imx_get_hwmctrl(struct imx_port *sport)
/*
* Handle any change of modem status signal since we were last called.
*/
static void imx_mctrl_check(struct imx_port *sport)
static void imx_uart_mctrl_check(struct imx_port *sport)
{
unsigned int status, changed;
status = imx_get_hwmctrl(sport);
status = imx_uart_get_hwmctrl(sport);
changed = status ^ sport->old_status;
if (changed == 0)
......@@ -755,55 +853,79 @@ static void imx_mctrl_check(struct imx_port *sport)
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}
static irqreturn_t imx_int(int irq, void *dev_id)
static irqreturn_t imx_uart_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int sts;
unsigned int sts2;
unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
irqreturn_t ret = IRQ_NONE;
sts = readl(sport->port.membase + USR1);
sts2 = readl(sport->port.membase + USR2);
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr3 = imx_uart_readl(sport, UCR3);
ucr4 = imx_uart_readl(sport, UCR4);
if (!sport->dma_is_enabled && (sts & (USR1_RRDY | USR1_AGTIM))) {
imx_rxint(irq, dev_id);
/*
* Even if a condition is true that can trigger an irq only handle it if
* the respective irq source is enabled. This prevents some undesired
* actions, for example if a character that sits in the RX FIFO and that
* should be fetched via DMA is tried to be fetched using PIO. Or the
* receiver is currently off and so reading from URXD0 results in an
* exception. So just mask the (raw) status bits for disabled irqs.
*/
if ((ucr1 & UCR1_RRDYEN) == 0)
usr1 &= ~USR1_RRDY;
if ((ucr2 & UCR2_ATEN) == 0)
usr1 &= ~USR1_AGTIM;
if ((ucr1 & UCR1_TXMPTYEN) == 0)
usr1 &= ~USR1_TRDY;
if ((ucr4 & UCR4_TCEN) == 0)
usr2 &= ~USR2_TXDC;
if ((ucr3 & UCR3_DTRDEN) == 0)
usr1 &= ~USR1_DTRD;
if ((ucr1 & UCR1_RTSDEN) == 0)
usr1 &= ~USR1_RTSD;
if ((ucr3 & UCR3_AWAKEN) == 0)
usr1 &= ~USR1_AWAKE;
if ((ucr4 & UCR4_OREN) == 0)
usr2 &= ~USR2_ORE;
if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
imx_uart_rxint(irq, dev_id);
ret = IRQ_HANDLED;
}
if ((sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
(sts2 & USR2_TXDC &&
readl(sport->port.membase + UCR4) & UCR4_TCEN)) {
imx_txint(irq, dev_id);
if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
imx_uart_txint(irq, dev_id);
ret = IRQ_HANDLED;
}
if (sts & USR1_DTRD) {
if (usr1 & USR1_DTRD) {
unsigned long flags;
if (sts & USR1_DTRD)
writel(USR1_DTRD, sport->port.membase + USR1);
imx_uart_writel(sport, USR1_DTRD, USR1);
spin_lock_irqsave(&sport->port.lock, flags);
imx_mctrl_check(sport);
imx_uart_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
ret = IRQ_HANDLED;
}
if (sts & USR1_RTSD) {
imx_rtsint(irq, dev_id);
if (usr1 & USR1_RTSD) {
imx_uart_rtsint(irq, dev_id);
ret = IRQ_HANDLED;
}
if (sts & USR1_AWAKE) {
writel(USR1_AWAKE, sport->port.membase + USR1);
if (usr1 & USR1_AWAKE) {
imx_uart_writel(sport, USR1_AWAKE, USR1);
ret = IRQ_HANDLED;
}
if (sts2 & USR2_ORE) {
if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
writel(USR2_ORE, sport->port.membase + USR2);
imx_uart_writel(sport, USR2_ORE, USR2);
ret = IRQ_HANDLED;
}
......@@ -813,52 +935,56 @@ static irqreturn_t imx_int(int irq, void *dev_id)
/*
* Return TIOCSER_TEMT when transmitter is not busy.
*/
static unsigned int imx_tx_empty(struct uart_port *port)
static unsigned int imx_uart_tx_empty(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int ret;
ret = (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
ret = (imx_uart_readl(sport, USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0;
/* If the TX DMA is working, return 0. */
if (sport->dma_is_enabled && sport->dma_is_txing)
if (sport->dma_is_txing)
ret = 0;
return ret;
}
static unsigned int imx_get_mctrl(struct uart_port *port)
/* called with port.lock taken and irqs off */
static unsigned int imx_uart_get_mctrl(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int ret = imx_get_hwmctrl(sport);
unsigned int ret = imx_uart_get_hwmctrl(sport);
mctrl_gpio_get(sport->gpios, &ret);
return ret;
}
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
/* called with port.lock taken and irqs off */
static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
u32 ucr3, uts;
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_CTS | UCR2_CTSC);
u32 ucr2;
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
if (mctrl & TIOCM_RTS)
temp |= UCR2_CTS | UCR2_CTSC;
writel(temp, sport->port.membase + UCR2);
ucr2 |= UCR2_CTS | UCR2_CTSC;
imx_uart_writel(sport, ucr2, UCR2);
}
temp = readl(sport->port.membase + UCR3) & ~UCR3_DSR;
ucr3 = imx_uart_readl(sport, UCR3) & ~UCR3_DSR;
if (!(mctrl & TIOCM_DTR))
temp |= UCR3_DSR;
writel(temp, sport->port.membase + UCR3);
ucr3 |= UCR3_DSR;
imx_uart_writel(sport, ucr3, UCR3);
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
uts = imx_uart_readl(sport, imx_uart_uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
temp |= UTS_LOOP;
writel(temp, sport->port.membase + uts_reg(sport));
uts |= UTS_LOOP;
imx_uart_writel(sport, uts, imx_uart_uts_reg(sport));
mctrl_gpio_set(sport->gpios, mctrl);
}
......@@ -866,19 +992,20 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
/*
* Interrupts always disabled.
*/
static void imx_break_ctl(struct uart_port *port, int break_state)
static void imx_uart_break_ctl(struct uart_port *port, int break_state)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags, temp;
unsigned long flags;
u32 ucr1;
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_SNDBRK;
if (break_state != 0)
temp |= UCR1_SNDBRK;
ucr1 |= UCR1_SNDBRK;
writel(temp, sport->port.membase + UCR1);
imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
......@@ -887,14 +1014,14 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
* This is our per-port timeout handler, for checking the
* modem status signals.
*/
static void imx_timeout(struct timer_list *t)
static void imx_uart_timeout(struct timer_list *t)
{
struct imx_port *sport = from_timer(sport, t, timer);
unsigned long flags;
if (sport->port.state) {
spin_lock_irqsave(&sport->port.lock, flags);
imx_mctrl_check(sport);
imx_uart_mctrl_check(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
......@@ -911,7 +1038,7 @@ static void imx_timeout(struct timer_list *t)
* Condition [2] is triggered when a character has been sitting in the FIFO
* for at least 8 byte durations.
*/
static void dma_rx_callback(void *data)
static void imx_uart_dma_rx_callback(void *data)
{
struct imx_port *sport = data;
struct dma_chan *chan = sport->dma_chan_rx;
......@@ -927,8 +1054,7 @@ static void dma_rx_callback(void *data)
status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state);
if (status == DMA_ERROR) {
dev_err(sport->port.dev, "DMA transaction error.\n");
clear_rx_errors(sport);
imx_uart_clear_rx_errors(sport);
return;
}
......@@ -988,7 +1114,7 @@ static void dma_rx_callback(void *data)
/* RX DMA buffer periods */
#define RX_DMA_PERIODS 4
static int start_rx_dma(struct imx_port *sport)
static int imx_uart_start_rx_dma(struct imx_port *sport)
{
struct scatterlist *sgl = &sport->rx_sgl;
struct dma_chan *chan = sport->dma_chan_rx;
......@@ -1016,7 +1142,7 @@ static int start_rx_dma(struct imx_port *sport)
dev_err(dev, "We cannot prepare for the RX slave dma!\n");
return -EINVAL;
}
desc->callback = dma_rx_callback;
desc->callback = imx_uart_dma_rx_callback;
desc->callback_param = sport;
dev_dbg(dev, "RX: prepare for the DMA.\n");
......@@ -1026,27 +1152,35 @@ static int start_rx_dma(struct imx_port *sport)
return 0;
}
static void clear_rx_errors(struct imx_port *sport)
static void imx_uart_clear_rx_errors(struct imx_port *sport)
{
unsigned int status_usr1, status_usr2;
struct tty_port *port = &sport->port.state->port;
u32 usr1, usr2;
status_usr1 = readl(sport->port.membase + USR1);
status_usr2 = readl(sport->port.membase + USR2);
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);
if (status_usr2 & USR2_BRCD) {
if (usr2 & USR2_BRCD) {
sport->port.icount.brk++;
writel(USR2_BRCD, sport->port.membase + USR2);
} else if (status_usr1 & USR1_FRAMERR) {
imx_uart_writel(sport, USR2_BRCD, USR2);
uart_handle_break(&sport->port);
if (tty_insert_flip_char(port, 0, TTY_BREAK) == 0)
sport->port.icount.buf_overrun++;
tty_flip_buffer_push(port);
} else {
dev_err(sport->port.dev, "DMA transaction error.\n");
if (usr1 & USR1_FRAMERR) {
sport->port.icount.frame++;
writel(USR1_FRAMERR, sport->port.membase + USR1);
} else if (status_usr1 & USR1_PARITYERR) {
imx_uart_writel(sport, USR1_FRAMERR, USR1);
} else if (usr1 & USR1_PARITYERR) {
sport->port.icount.parity++;
writel(USR1_PARITYERR, sport->port.membase + USR1);
imx_uart_writel(sport, USR1_PARITYERR, USR1);
}
}
if (status_usr2 & USR2_ORE) {
if (usr2 & USR2_ORE) {
sport->port.icount.overrun++;
writel(USR2_ORE, sport->port.membase + USR2);
imx_uart_writel(sport, USR2_ORE, USR2);
}
}
......@@ -1056,15 +1190,15 @@ static void clear_rx_errors(struct imx_port *sport)
#define TXTL_DMA 8 /* DMA burst setting */
#define RXTL_DMA 9 /* DMA burst setting */
static void imx_setup_ufcr(struct imx_port *sport,
static void imx_uart_setup_ufcr(struct imx_port *sport,
unsigned char txwl, unsigned char rxwl)
{
unsigned int val;
/* set receiver / transmitter trigger level */
val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
val = imx_uart_readl(sport, UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
val |= txwl << UFCR_TXTL_SHF | rxwl;
writel(val, sport->port.membase + UFCR);
imx_uart_writel(sport, val, UFCR);
}
static void imx_uart_dma_exit(struct imx_port *sport)
......@@ -1083,8 +1217,6 @@ static void imx_uart_dma_exit(struct imx_port *sport)
dma_release_channel(sport->dma_chan_tx);
sport->dma_chan_tx = NULL;
}
sport->dma_is_inited = 0;
}
static int imx_uart_dma_init(struct imx_port *sport)
......@@ -1137,43 +1269,41 @@ static int imx_uart_dma_init(struct imx_port *sport)
goto err;
}
sport->dma_is_inited = 1;
return 0;
err:
imx_uart_dma_exit(sport);
return ret;
}
static void imx_enable_dma(struct imx_port *sport)
static void imx_uart_enable_dma(struct imx_port *sport)
{
unsigned long temp;
u32 ucr1;
/* set UCR1 */
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN;
writel(temp, sport->port.membase + UCR1);
imx_uart_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA);
/* set UCR1 */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_enabled = 1;
}
static void imx_disable_dma(struct imx_port *sport)
static void imx_uart_disable_dma(struct imx_port *sport)
{
unsigned long temp;
u32 ucr1, ucr2;
/* clear UCR1 */
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
writel(temp, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_RXDMAEN | UCR1_TXDMAEN | UCR1_ATDMAEN);
imx_uart_writel(sport, ucr1, UCR1);
/* clear UCR2 */
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN);
writel(temp, sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN);
imx_uart_writel(sport, ucr2, UCR2);
imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
sport->dma_is_enabled = 0;
}
......@@ -1181,11 +1311,13 @@ static void imx_disable_dma(struct imx_port *sport)
/* half the RX buffer size */
#define CTSTL 16
static int imx_startup(struct uart_port *port)
static int imx_uart_startup(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
int retval, i;
unsigned long flags, temp;
unsigned long flags;
int dma_is_inited = 0;
u32 ucr1, ucr2, ucr4;
retval = clk_prepare_enable(sport->clk_per);
if (retval)
......@@ -1196,104 +1328,106 @@ static int imx_startup(struct uart_port *port)
return retval;
}
imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
/* disable the DREN bit (Data Ready interrupt enable) before
* requesting IRQs
*/
temp = readl(sport->port.membase + UCR4);
ucr4 = imx_uart_readl(sport, UCR4);
/* set the trigger level for CTS */
temp &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
temp |= CTSTL << UCR4_CTSTL_SHF;
ucr4 &= ~(UCR4_CTSTL_MASK << UCR4_CTSTL_SHF);
ucr4 |= CTSTL << UCR4_CTSTL_SHF;
writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
imx_uart_writel(sport, ucr4 & ~UCR4_DREN, UCR4);
/* Can we enable the DMA support? */
if (!uart_console(port) && !sport->dma_is_inited)
imx_uart_dma_init(sport);
if (!uart_console(port) && imx_uart_dma_init(sport) == 0)
dma_is_inited = 1;
spin_lock_irqsave(&sport->port.lock, flags);
/* Reset fifo's and state machines */
i = 100;
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_SRST;
writel(temp, sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~UCR2_SRST;
imx_uart_writel(sport, ucr2, UCR2);
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/*
* Finally, clear and enable interrupts
*/
writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1);
writel(USR2_ORE, sport->port.membase + USR2);
if (sport->dma_is_inited && !sport->dma_is_enabled)
imx_enable_dma(sport);
imx_uart_writel(sport, USR1_RTSD | USR1_DTRD, USR1);
imx_uart_writel(sport, USR2_ORE, USR2);
temp = readl(sport->port.membase + UCR1) & ~UCR1_RRDYEN;
if (!sport->dma_is_enabled)
temp |= UCR1_RRDYEN;
temp |= UCR1_UARTEN;
ucr1 = imx_uart_readl(sport, UCR1) & ~UCR1_RRDYEN;
ucr1 |= UCR1_UARTEN;
if (sport->have_rtscts)
temp |= UCR1_RTSDEN;
ucr1 |= UCR1_RTSDEN;
writel(temp, sport->port.membase + UCR1);
imx_uart_writel(sport, ucr1, UCR1);
temp = readl(sport->port.membase + UCR4) & ~UCR4_OREN;
ucr4 = imx_uart_readl(sport, UCR4) & ~UCR4_OREN;
if (!sport->dma_is_enabled)
temp |= UCR4_OREN;
writel(temp, sport->port.membase + UCR4);
ucr4 |= UCR4_OREN;
imx_uart_writel(sport, ucr4, UCR4);
temp = readl(sport->port.membase + UCR2) & ~UCR2_ATEN;
temp |= (UCR2_RXEN | UCR2_TXEN);
ucr2 = imx_uart_readl(sport, UCR2) & ~UCR2_ATEN;
ucr2 |= (UCR2_RXEN | UCR2_TXEN);
if (!sport->have_rtscts)
temp |= UCR2_IRTS;
ucr2 |= UCR2_IRTS;
/*
* make sure the edge sensitive RTS-irq is disabled,
* we're using RTSD instead.
*/
if (!is_imx1_uart(sport))
temp &= ~UCR2_RTSEN;
writel(temp, sport->port.membase + UCR2);
if (!imx_uart_is_imx1(sport))
ucr2 &= ~UCR2_RTSEN;
imx_uart_writel(sport, ucr2, UCR2);
if (!imx_uart_is_imx1(sport)) {
u32 ucr3;
if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
ucr3 = imx_uart_readl(sport, UCR3);
temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
ucr3 |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
/* disable broken interrupts */
temp &= ~(UCR3_RI | UCR3_DCD);
ucr3 &= ~(UCR3_RI | UCR3_DCD);
writel(temp, sport->port.membase + UCR3);
imx_uart_writel(sport, ucr3, UCR3);
}
/*
* Enable modem status interrupts
*/
imx_enable_ms(&sport->port);
imx_uart_enable_ms(&sport->port);
/*
* Start RX DMA immediately instead of waiting for RX FIFO interrupts.
* In our iMX53 the average delay for the first reception dropped from
* approximately 35000 microseconds to 1000 microseconds.
*/
if (sport->dma_is_enabled)
start_rx_dma(sport);
if (dma_is_inited) {
imx_uart_enable_dma(sport);
imx_uart_start_rx_dma(sport);
} else {
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 |= UCR1_RRDYEN;
imx_uart_writel(sport, ucr1, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 |= UCR2_ATEN;
imx_uart_writel(sport, ucr2, UCR2);
}
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
static void imx_shutdown(struct uart_port *port)
static void imx_uart_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
unsigned long flags;
u32 ucr1, ucr2;
if (sport->dma_is_enabled) {
sport->dma_is_rxing = 0;
......@@ -1302,9 +1436,9 @@ static void imx_shutdown(struct uart_port *port)
dmaengine_terminate_sync(sport->dma_chan_rx);
spin_lock_irqsave(&sport->port.lock, flags);
imx_stop_tx(port);
imx_stop_rx(port);
imx_disable_dma(sport);
imx_uart_stop_tx(port);
imx_uart_stop_rx(port);
imx_uart_disable_dma(sport);
spin_unlock_irqrestore(&sport->port.lock, flags);
imx_uart_dma_exit(sport);
}
......@@ -1312,9 +1446,9 @@ static void imx_shutdown(struct uart_port *port)
mctrl_gpio_disable_ms(sport->gpios);
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_TXEN | UCR2_ATEN);
imx_uart_writel(sport, ucr2, UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
/*
......@@ -1327,21 +1461,22 @@ static void imx_shutdown(struct uart_port *port)
*/
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
writel(temp, sport->port.membase + UCR1);
imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
clk_disable_unprepare(sport->clk_per);
clk_disable_unprepare(sport->clk_ipg);
}
static void imx_flush_buffer(struct uart_port *port)
/* called with port.lock taken and irqs off */
static void imx_uart_flush_buffer(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
struct scatterlist *sgl = &sport->tx_sgl[0];
unsigned long temp;
u32 ucr2;
int i = 100, ubir, ubmr, uts;
if (!sport->dma_chan_tx)
......@@ -1350,11 +1485,13 @@ static void imx_flush_buffer(struct uart_port *port)
sport->tx_bytes = 0;
dmaengine_terminate_all(sport->dma_chan_tx);
if (sport->dma_is_txing) {
u32 ucr1;
dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
DMA_TO_DEVICE);
temp = readl(sport->port.membase + UCR1);
temp &= ~UCR1_TDMAEN;
writel(temp, sport->port.membase + UCR1);
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
sport->dma_is_txing = 0;
}
......@@ -1369,33 +1506,33 @@ static void imx_flush_buffer(struct uart_port *port)
* UTXD. UBRC is read only, so only save/restore the other three
* registers.
*/
ubir = readl(sport->port.membase + UBIR);
ubmr = readl(sport->port.membase + UBMR);
uts = readl(sport->port.membase + IMX21_UTS);
ubir = imx_uart_readl(sport, UBIR);
ubmr = imx_uart_readl(sport, UBMR);
uts = imx_uart_readl(sport, IMX21_UTS);
temp = readl(sport->port.membase + UCR2);
temp &= ~UCR2_SRST;
writel(temp, sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~UCR2_SRST;
imx_uart_writel(sport, ucr2, UCR2);
while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
while (!(imx_uart_readl(sport, UCR2) & UCR2_SRST) && (--i > 0))
udelay(1);
/* Restore the registers */
writel(ubir, sport->port.membase + UBIR);
writel(ubmr, sport->port.membase + UBMR);
writel(uts, sport->port.membase + IMX21_UTS);
imx_uart_writel(sport, ubir, UBIR);
imx_uart_writel(sport, ubmr, UBMR);
imx_uart_writel(sport, uts, IMX21_UTS);
}
static void
imx_set_termios(struct uart_port *port, struct ktermios *termios,
imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
unsigned long ucr2, old_ucr1, old_ucr2;
u32 ucr2, old_ucr1, old_ucr2, ufcr;
unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned long div, ufcr;
unsigned long div;
unsigned long num, denom;
uint64_t tdiv64;
......@@ -1426,11 +1563,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (port->rs485.flags &
SER_RS485_RTS_AFTER_SEND)
imx_port_rts_active(sport, &ucr2);
imx_uart_rts_active(sport, &ucr2);
else
imx_port_rts_inactive(sport, &ucr2);
imx_uart_rts_inactive(sport, &ucr2);
} else {
imx_port_rts_auto(sport, &ucr2);
imx_uart_rts_auto(sport, &ucr2);
}
} else {
termios->c_cflag &= ~CRTSCTS;
......@@ -1438,9 +1575,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
} else if (port->rs485.flags & SER_RS485_ENABLED) {
/* disable transmitter */
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
imx_port_rts_active(sport, &ucr2);
imx_uart_rts_active(sport, &ucr2);
else
imx_port_rts_inactive(sport, &ucr2);
imx_uart_rts_inactive(sport, &ucr2);
}
......@@ -1495,17 +1632,18 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
/*
* disable interrupts and drain transmitter
*/
old_ucr1 = readl(sport->port.membase + UCR1);
writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
sport->port.membase + UCR1);
while (!(readl(sport->port.membase + USR2) & USR2_TXDC))
old_ucr1 = imx_uart_readl(sport, UCR1);
imx_uart_writel(sport,
old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
UCR1);
old_ucr2 = imx_uart_readl(sport, UCR2);
imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);
while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))
barrier();
/* then, disable everything */
old_ucr2 = readl(sport->port.membase + UCR2);
writel(old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN),
sport->port.membase + UCR2);
imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);
old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN);
/* custom-baudrate handling */
......@@ -1531,29 +1669,29 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
num -= 1;
denom -= 1;
ufcr = readl(sport->port.membase + UFCR);
ufcr = imx_uart_readl(sport, UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
writel(ufcr, sport->port.membase + UFCR);
imx_uart_writel(sport, ufcr, UFCR);
writel(num, sport->port.membase + UBIR);
writel(denom, sport->port.membase + UBMR);
imx_uart_writel(sport, num, UBIR);
imx_uart_writel(sport, denom, UBMR);
if (!is_imx1_uart(sport))
writel(sport->port.uartclk / div / 1000,
sport->port.membase + IMX21_ONEMS);
if (!imx_uart_is_imx1(sport))
imx_uart_writel(sport, sport->port.uartclk / div / 1000,
IMX21_ONEMS);
writel(old_ucr1, sport->port.membase + UCR1);
imx_uart_writel(sport, old_ucr1, UCR1);
/* set the parity, stop bits and data size */
writel(ucr2 | old_ucr2, sport->port.membase + UCR2);
imx_uart_writel(sport, ucr2 | old_ucr2, UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
imx_enable_ms(&sport->port);
imx_uart_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock, flags);
}
static const char *imx_type(struct uart_port *port)
static const char *imx_uart_type(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
......@@ -1563,7 +1701,7 @@ static const char *imx_type(struct uart_port *port)
/*
* Configure/autoconfigure the port.
*/
static void imx_config_port(struct uart_port *port, int flags)
static void imx_uart_config_port(struct uart_port *port, int flags)
{
struct imx_port *sport = (struct imx_port *)port;
......@@ -1577,7 +1715,7 @@ static void imx_config_port(struct uart_port *port, int flags)
* even then only between PORT_IMX and PORT_UNKNOWN
*/
static int
imx_verify_port(struct uart_port *port, struct serial_struct *ser)
imx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
{
struct imx_port *sport = (struct imx_port *)port;
int ret = 0;
......@@ -1601,11 +1739,11 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
#if defined(CONFIG_CONSOLE_POLL)
static int imx_poll_init(struct uart_port *port)
static int imx_uart_poll_init(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
unsigned long temp;
u32 ucr1, ucr2;
int retval;
retval = clk_prepare_enable(sport->clk_ipg);
......@@ -1615,58 +1753,76 @@ static int imx_poll_init(struct uart_port *port)
if (retval)
clk_disable_unprepare(sport->clk_ipg);
imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
if (is_imx1_uart(sport))
temp |= IMX1_UCR1_UARTCLKEN;
temp |= UCR1_UARTEN | UCR1_RRDYEN;
temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN);
writel(temp, sport->port.membase + UCR1);
/*
* Be careful about the order of enabling bits here. First enable the
* receiver (UARTEN + RXEN) and only then the corresponding irqs.
* This prevents that a character that already sits in the RX fifo is
* triggering an irq but the try to fetch it from there results in an
* exception because UARTEN or RXEN is still off.
*/
ucr1 = imx_uart_readl(sport, UCR1);
ucr2 = imx_uart_readl(sport, UCR2);
temp = readl(sport->port.membase + UCR2);
temp |= UCR2_RXEN;
writel(temp, sport->port.membase + UCR2);
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN);
ucr2 |= UCR2_RXEN;
ucr2 &= ~UCR2_ATEN;
imx_uart_writel(sport, ucr1, UCR1);
imx_uart_writel(sport, ucr2, UCR2);
/* now enable irqs */
imx_uart_writel(sport, ucr1 | UCR1_RRDYEN, UCR1);
imx_uart_writel(sport, ucr2 | UCR2_ATEN, UCR2);
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
}
static int imx_poll_get_char(struct uart_port *port)
static int imx_uart_poll_get_char(struct uart_port *port)
{
if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
struct imx_port *sport = (struct imx_port *)port;
if (!(imx_uart_readl(sport, USR2) & USR2_RDR))
return NO_POLL_CHAR;
return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
return imx_uart_readl(sport, URXD0) & URXD_RX_DATA;
}
static void imx_poll_put_char(struct uart_port *port, unsigned char c)
static void imx_uart_poll_put_char(struct uart_port *port, unsigned char c)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned int status;
/* drain */
do {
status = readl_relaxed(port->membase + USR1);
status = imx_uart_readl(sport, USR1);
} while (~status & USR1_TRDY);
/* write */
writel_relaxed(c, port->membase + URTX0);
imx_uart_writel(sport, c, URTX0);
/* flush */
do {
status = readl_relaxed(port->membase + USR2);
status = imx_uart_readl(sport, USR2);
} while (~status & USR2_TXDC);
}
#endif
static int imx_rs485_config(struct uart_port *port,
/* called with port.lock taken and irqs off or from .probe without locking */
static int imx_uart_rs485_config(struct uart_port *port,
struct serial_rs485 *rs485conf)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
u32 ucr2;
/* unimplemented */
rs485conf->delay_rts_before_send = 0;
......@@ -1678,70 +1834,67 @@ static int imx_rs485_config(struct uart_port *port,
if (rs485conf->flags & SER_RS485_ENABLED) {
/* disable transmitter */
temp = readl(sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
imx_port_rts_active(sport, &temp);
imx_uart_rts_active(sport, &ucr2);
else
imx_port_rts_inactive(sport, &temp);
writel(temp, sport->port.membase + UCR2);
imx_uart_rts_inactive(sport, &ucr2);
imx_uart_writel(sport, ucr2, UCR2);
}
/* Make sure Rx is enabled in case Tx is active with Rx disabled */
if (!(rs485conf->flags & SER_RS485_ENABLED) ||
rs485conf->flags & SER_RS485_RX_DURING_TX) {
temp = readl(sport->port.membase + UCR2);
temp |= UCR2_RXEN;
writel(temp, sport->port.membase + UCR2);
}
rs485conf->flags & SER_RS485_RX_DURING_TX)
imx_uart_start_rx(port);
port->rs485 = *rs485conf;
return 0;
}
static const struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
.get_mctrl = imx_get_mctrl,
.stop_tx = imx_stop_tx,
.start_tx = imx_start_tx,
.stop_rx = imx_stop_rx,
.enable_ms = imx_enable_ms,
.break_ctl = imx_break_ctl,
.startup = imx_startup,
.shutdown = imx_shutdown,
.flush_buffer = imx_flush_buffer,
.set_termios = imx_set_termios,
.type = imx_type,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
static const struct uart_ops imx_uart_pops = {
.tx_empty = imx_uart_tx_empty,
.set_mctrl = imx_uart_set_mctrl,
.get_mctrl = imx_uart_get_mctrl,
.stop_tx = imx_uart_stop_tx,
.start_tx = imx_uart_start_tx,
.stop_rx = imx_uart_stop_rx,
.enable_ms = imx_uart_enable_ms,
.break_ctl = imx_uart_break_ctl,
.startup = imx_uart_startup,
.shutdown = imx_uart_shutdown,
.flush_buffer = imx_uart_flush_buffer,
.set_termios = imx_uart_set_termios,
.type = imx_uart_type,
.config_port = imx_uart_config_port,
.verify_port = imx_uart_verify_port,
#if defined(CONFIG_CONSOLE_POLL)
.poll_init = imx_poll_init,
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
.poll_init = imx_uart_poll_init,
.poll_get_char = imx_uart_poll_get_char,
.poll_put_char = imx_uart_poll_put_char,
#endif
};
static struct imx_port *imx_ports[UART_NR];
static struct imx_port *imx_uart_ports[UART_NR];
#ifdef CONFIG_SERIAL_IMX_CONSOLE
static void imx_console_putchar(struct uart_port *port, int ch)
static void imx_uart_console_putchar(struct uart_port *port, int ch)
{
struct imx_port *sport = (struct imx_port *)port;
while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)
while (imx_uart_readl(sport, imx_uart_uts_reg(sport)) & UTS_TXFULL)
barrier();
writel(ch, sport->port.membase + URTX0);
imx_uart_writel(sport, ch, URTX0);
}
/*
* Interrupts are disabled on entering
*/
static void
imx_console_write(struct console *co, const char *s, unsigned int count)
imx_uart_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
struct imx_port *sport = imx_uart_ports[co->index];
struct imx_port_ucrs old_ucr;
unsigned int ucr1;
unsigned long flags = 0;
......@@ -1767,27 +1920,27 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
/*
* First, save UCR1/2/3 and then disable interrupts
*/
imx_port_ucrs_save(&sport->port, &old_ucr);
imx_uart_ucrs_save(sport, &old_ucr);
ucr1 = old_ucr.ucr1;
if (is_imx1_uart(sport))
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
writel(ucr1, sport->port.membase + UCR1);
imx_uart_writel(sport, ucr1, UCR1);
writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
uart_console_write(&sport->port, s, count, imx_console_putchar);
uart_console_write(&sport->port, s, count, imx_uart_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore UCR1/2/3
*/
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
while (!(imx_uart_readl(sport, USR2) & USR2_TXDC));
imx_port_ucrs_restore(&sport->port, &old_ucr);
imx_uart_ucrs_restore(sport, &old_ucr);
if (locked)
spin_unlock_irqrestore(&sport->port.lock, flags);
......@@ -1801,17 +1954,17 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
* try to determine the current setup.
*/
static void __init
imx_console_get_options(struct imx_port *sport, int *baud,
imx_uart_console_get_options(struct imx_port *sport, int *baud,
int *parity, int *bits)
{
if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
if (imx_uart_readl(sport, UCR1) & UCR1_UARTEN) {
/* ok, the port was enabled */
unsigned int ucr2, ubir, ubmr, uartclk;
unsigned int baud_raw;
unsigned int ucfr_rfdiv;
ucr2 = readl(sport->port.membase + UCR2);
ucr2 = imx_uart_readl(sport, UCR2);
*parity = 'n';
if (ucr2 & UCR2_PREN) {
......@@ -1826,10 +1979,10 @@ imx_console_get_options(struct imx_port *sport, int *baud,
else
*bits = 7;
ubir = readl(sport->port.membase + UBIR) & 0xffff;
ubmr = readl(sport->port.membase + UBMR) & 0xffff;
ubir = imx_uart_readl(sport, UBIR) & 0xffff;
ubmr = imx_uart_readl(sport, UBMR) & 0xffff;
ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
ucfr_rfdiv = (imx_uart_readl(sport, UFCR) & UFCR_RFDIV) >> 7;
if (ucfr_rfdiv == 6)
ucfr_rfdiv = 7;
else
......@@ -1860,7 +2013,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
}
static int __init
imx_console_setup(struct console *co, char *options)
imx_uart_console_setup(struct console *co, char *options)
{
struct imx_port *sport;
int baud = 9600;
......@@ -1874,9 +2027,9 @@ imx_console_setup(struct console *co, char *options)
* if so, search for the first available port that does have
* console support.
*/
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
if (co->index == -1 || co->index >= ARRAY_SIZE(imx_uart_ports))
co->index = 0;
sport = imx_ports[co->index];
sport = imx_uart_ports[co->index];
if (sport == NULL)
return -ENODEV;
......@@ -1888,9 +2041,9 @@ imx_console_setup(struct console *co, char *options)
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
imx_console_get_options(sport, &baud, &parity, &bits);
imx_uart_console_get_options(sport, &baud, &parity, &bits);
imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
imx_uart_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT);
retval = uart_set_options(&sport->port, co, baud, parity, bits, flow);
......@@ -1908,34 +2061,36 @@ imx_console_setup(struct console *co, char *options)
return retval;
}
static struct uart_driver imx_reg;
static struct console imx_console = {
static struct uart_driver imx_uart_uart_driver;
static struct console imx_uart_console = {
.name = DEV_NAME,
.write = imx_console_write,
.write = imx_uart_console_write,
.device = uart_console_device,
.setup = imx_console_setup,
.setup = imx_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &imx_reg,
.data = &imx_uart_uart_driver,
};
#define IMX_CONSOLE &imx_console
#define IMX_CONSOLE &imx_uart_console
#ifdef CONFIG_OF
static void imx_console_early_putchar(struct uart_port *port, int ch)
static void imx_uart_console_early_putchar(struct uart_port *port, int ch)
{
while (readl_relaxed(port->membase + IMX21_UTS) & UTS_TXFULL)
struct imx_port *sport = (struct imx_port *)port;
while (imx_uart_readl(sport, IMX21_UTS) & UTS_TXFULL)
cpu_relax();
writel_relaxed(ch, port->membase + URTX0);
imx_uart_writel(sport, ch, URTX0);
}
static void imx_console_early_write(struct console *con, const char *s,
static void imx_uart_console_early_write(struct console *con, const char *s,
unsigned count)
{
struct earlycon_device *dev = con->data;
uart_console_write(&dev->port, s, count, imx_console_early_putchar);
uart_console_write(&dev->port, s, count, imx_uart_console_early_putchar);
}
static int __init
......@@ -1944,7 +2099,7 @@ imx_console_early_setup(struct earlycon_device *dev, const char *opt)
if (!dev->port.membase)
return -ENODEV;
dev->con->write = imx_console_early_write;
dev->con->write = imx_uart_console_early_write;
return 0;
}
......@@ -1956,13 +2111,13 @@ OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup);
#define IMX_CONSOLE NULL
#endif
static struct uart_driver imx_reg = {
static struct uart_driver imx_uart_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_ports),
.nr = ARRAY_SIZE(imx_uart_ports),
.cons = IMX_CONSOLE,
};
......@@ -1971,7 +2126,7 @@ static struct uart_driver imx_reg = {
* This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
* could successfully get all information from dt or a negative errno.
*/
static int serial_imx_probe_dt(struct imx_port *sport,
static int imx_uart_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
......@@ -2002,14 +2157,14 @@ static int serial_imx_probe_dt(struct imx_port *sport,
return 0;
}
#else
static inline int serial_imx_probe_dt(struct imx_port *sport,
static inline int imx_uart_probe_dt(struct imx_port *sport,
struct platform_device *pdev)
{
return 1;
}
#endif
static void serial_imx_probe_pdata(struct imx_port *sport,
static void imx_uart_probe_pdata(struct imx_port *sport,
struct platform_device *pdev)
{
struct imxuart_platform_data *pdata = dev_get_platdata(&pdev->dev);
......@@ -2024,11 +2179,12 @@ static void serial_imx_probe_pdata(struct imx_port *sport,
sport->have_rtscts = 1;
}
static int serial_imx_probe(struct platform_device *pdev)
static int imx_uart_probe(struct platform_device *pdev)
{
struct imx_port *sport;
void __iomem *base;
int ret = 0, reg;
int ret = 0;
u32 ucr1;
struct resource *res;
int txirq, rxirq, rtsirq;
......@@ -2036,12 +2192,18 @@ static int serial_imx_probe(struct platform_device *pdev)
if (!sport)
return -ENOMEM;
ret = serial_imx_probe_dt(sport, pdev);
ret = imx_uart_probe_dt(sport, pdev);
if (ret > 0)
serial_imx_probe_pdata(sport, pdev);
imx_uart_probe_pdata(sport, pdev);
else if (ret < 0)
return ret;
if (sport->port.line >= ARRAY_SIZE(imx_uart_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n",
sport->port.line);
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
......@@ -2058,10 +2220,10 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_pops;
sport->port.rs485_config = imx_rs485_config;
sport->port.ops = &imx_uart_pops;
sport->port.rs485_config = imx_uart_rs485_config;
sport->port.flags = UPF_BOOT_AUTOCONF;
timer_setup(&sport->timer, imx_timeout, 0);
timer_setup(&sport->timer, imx_uart_timeout, 0);
sport->gpios = mctrl_gpio_init(&sport->port, 0);
if (IS_ERR(sport->gpios))
......@@ -2090,49 +2252,56 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
/* initialize shadow register values */
sport->ucr1 = readl(sport->port.membase + UCR1);
sport->ucr2 = readl(sport->port.membase + UCR2);
sport->ucr3 = readl(sport->port.membase + UCR3);
sport->ucr4 = readl(sport->port.membase + UCR4);
sport->ufcr = readl(sport->port.membase + UFCR);
uart_get_rs485_mode(&pdev->dev, &sport->port.rs485);
if (sport->port.rs485.flags & SER_RS485_ENABLED &&
(!sport->have_rtscts && !sport->have_rtsgpio))
dev_err(&pdev->dev, "no RTS control, disabling rs485\n");
imx_rs485_config(&sport->port, &sport->port.rs485);
imx_uart_rs485_config(&sport->port, &sport->port.rs485);
/* Disable interrupts before requesting them */
reg = readl_relaxed(sport->port.membase + UCR1);
reg &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
UCR1_TXMPTYEN | UCR1_RTSDEN);
writel_relaxed(reg, sport->port.membase + UCR1);
imx_uart_writel(sport, ucr1, UCR1);
if (!is_imx1_uart(sport) && sport->dte_mode) {
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
/*
* The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
* and influences if UCR3_RI and UCR3_DCD changes the level of RI
* and DCD (when they are outputs) or enables the respective
* irqs. So set this bit early, i.e. before requesting irqs.
*/
reg = readl(sport->port.membase + UFCR);
if (!(reg & UFCR_DCEDTE))
writel(reg | UFCR_DCEDTE, sport->port.membase + UFCR);
u32 ufcr = imx_uart_readl(sport, UFCR);
if (!(ufcr & UFCR_DCEDTE))
imx_uart_writel(sport, ufcr | UFCR_DCEDTE, UFCR);
/*
* Disable UCR3_RI and UCR3_DCD irqs. They are also not
* enabled later because they cannot be cleared
* (confirmed on i.MX25) which makes them unusable.
*/
writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
sport->port.membase + UCR3);
imx_uart_writel(sport,
IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
UCR3);
} else {
unsigned long ucr3 = UCR3_DSR;
reg = readl(sport->port.membase + UFCR);
if (reg & UFCR_DCEDTE)
writel(reg & ~UFCR_DCEDTE, sport->port.membase + UFCR);
u32 ucr3 = UCR3_DSR;
u32 ufcr = imx_uart_readl(sport, UFCR);
if (ufcr & UFCR_DCEDTE)
imx_uart_writel(sport, ufcr & ~UFCR_DCEDTE, UFCR);
if (!is_imx1_uart(sport))
if (!imx_uart_is_imx1(sport))
ucr3 |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP;
writel(ucr3, sport->port.membase + UCR3);
imx_uart_writel(sport, ucr3, UCR3);
}
clk_disable_unprepare(sport->clk_ipg);
......@@ -2142,7 +2311,7 @@ static int serial_imx_probe(struct platform_device *pdev)
* chips only have one interrupt.
*/
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0,
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request rx irq: %d\n",
......@@ -2150,7 +2319,7 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0,
ret = devm_request_irq(&pdev->dev, txirq, imx_uart_txint, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request tx irq: %d\n",
......@@ -2158,7 +2327,7 @@ static int serial_imx_probe(struct platform_device *pdev)
return ret;
}
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0,
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
......@@ -2166,90 +2335,90 @@ static int serial_imx_probe(struct platform_device *pdev)
}
}
imx_ports[sport->port.line] = sport;
imx_uart_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
return uart_add_one_port(&imx_reg, &sport->port);
return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}
static int serial_imx_remove(struct platform_device *pdev)
static int imx_uart_remove(struct platform_device *pdev)
{
struct imx_port *sport = platform_get_drvdata(pdev);
return uart_remove_one_port(&imx_reg, &sport->port);
return uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
}
static void serial_imx_restore_context(struct imx_port *sport)
static void imx_uart_restore_context(struct imx_port *sport)
{
if (!sport->context_saved)
return;
writel(sport->saved_reg[4], sport->port.membase + UFCR);
writel(sport->saved_reg[5], sport->port.membase + UESC);
writel(sport->saved_reg[6], sport->port.membase + UTIM);
writel(sport->saved_reg[7], sport->port.membase + UBIR);
writel(sport->saved_reg[8], sport->port.membase + UBMR);
writel(sport->saved_reg[9], sport->port.membase + IMX21_UTS);
writel(sport->saved_reg[0], sport->port.membase + UCR1);
writel(sport->saved_reg[1] | UCR2_SRST, sport->port.membase + UCR2);
writel(sport->saved_reg[2], sport->port.membase + UCR3);
writel(sport->saved_reg[3], sport->port.membase + UCR4);
imx_uart_writel(sport, sport->saved_reg[4], UFCR);
imx_uart_writel(sport, sport->saved_reg[5], UESC);
imx_uart_writel(sport, sport->saved_reg[6], UTIM);
imx_uart_writel(sport, sport->saved_reg[7], UBIR);
imx_uart_writel(sport, sport->saved_reg[8], UBMR);
imx_uart_writel(sport, sport->saved_reg[9], IMX21_UTS);
imx_uart_writel(sport, sport->saved_reg[0], UCR1);
imx_uart_writel(sport, sport->saved_reg[1] | UCR2_SRST, UCR2);
imx_uart_writel(sport, sport->saved_reg[2], UCR3);
imx_uart_writel(sport, sport->saved_reg[3], UCR4);
sport->context_saved = false;
}
static void serial_imx_save_context(struct imx_port *sport)
static void imx_uart_save_context(struct imx_port *sport)
{
/* Save necessary regs */
sport->saved_reg[0] = readl(sport->port.membase + UCR1);
sport->saved_reg[1] = readl(sport->port.membase + UCR2);
sport->saved_reg[2] = readl(sport->port.membase + UCR3);
sport->saved_reg[3] = readl(sport->port.membase + UCR4);
sport->saved_reg[4] = readl(sport->port.membase + UFCR);
sport->saved_reg[5] = readl(sport->port.membase + UESC);
sport->saved_reg[6] = readl(sport->port.membase + UTIM);
sport->saved_reg[7] = readl(sport->port.membase + UBIR);
sport->saved_reg[8] = readl(sport->port.membase + UBMR);
sport->saved_reg[9] = readl(sport->port.membase + IMX21_UTS);
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);
sport->saved_reg[3] = imx_uart_readl(sport, UCR4);
sport->saved_reg[4] = imx_uart_readl(sport, UFCR);
sport->saved_reg[5] = imx_uart_readl(sport, UESC);
sport->saved_reg[6] = imx_uart_readl(sport, UTIM);
sport->saved_reg[7] = imx_uart_readl(sport, UBIR);
sport->saved_reg[8] = imx_uart_readl(sport, UBMR);
sport->saved_reg[9] = imx_uart_readl(sport, IMX21_UTS);
sport->context_saved = true;
}
static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
static void imx_uart_enable_wakeup(struct imx_port *sport, bool on)
{
unsigned int val;
u32 ucr3;
val = readl(sport->port.membase + UCR3);
ucr3 = imx_uart_readl(sport, UCR3);
if (on) {
writel(USR1_AWAKE, sport->port.membase + USR1);
val |= UCR3_AWAKEN;
imx_uart_writel(sport, USR1_AWAKE, USR1);
ucr3 |= UCR3_AWAKEN;
} else {
ucr3 &= ~UCR3_AWAKEN;
}
else
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
imx_uart_writel(sport, ucr3, UCR3);
if (sport->have_rtscts) {
val = readl(sport->port.membase + UCR1);
u32 ucr1 = imx_uart_readl(sport, UCR1);
if (on)
val |= UCR1_RTSDEN;
ucr1 |= UCR1_RTSDEN;
else
val &= ~UCR1_RTSDEN;
writel(val, sport->port.membase + UCR1);
ucr1 &= ~UCR1_RTSDEN;
imx_uart_writel(sport, ucr1, UCR1);
}
}
static int imx_serial_port_suspend_noirq(struct device *dev)
static int imx_uart_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
serial_imx_save_context(sport);
imx_uart_save_context(sport);
clk_disable(sport->clk_ipg);
return 0;
}
static int imx_serial_port_resume_noirq(struct device *dev)
static int imx_uart_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
......@@ -2259,18 +2428,18 @@ static int imx_serial_port_resume_noirq(struct device *dev)
if (ret)
return ret;
serial_imx_restore_context(sport);
imx_uart_restore_context(sport);
return 0;
}
static int imx_serial_port_suspend(struct device *dev)
static int imx_uart_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
int ret;
uart_suspend_port(&imx_reg, &sport->port);
uart_suspend_port(&imx_uart_uart_driver, &sport->port);
disable_irq(sport->port.irq);
ret = clk_prepare_enable(sport->clk_ipg);
......@@ -2278,20 +2447,20 @@ static int imx_serial_port_suspend(struct device *dev)
return ret;
/* enable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, true);
imx_uart_enable_wakeup(sport, true);
return 0;
}
static int imx_serial_port_resume(struct device *dev)
static int imx_uart_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
/* disable wakeup from i.MX UART */
serial_imx_enable_wakeup(sport, false);
imx_uart_enable_wakeup(sport, false);
uart_resume_port(&imx_reg, &sport->port);
uart_resume_port(&imx_uart_uart_driver, &sport->port);
enable_irq(sport->port.irq);
clk_disable_unprepare(sport->clk_ipg);
......@@ -2299,74 +2468,74 @@ static int imx_serial_port_resume(struct device *dev)
return 0;
}
static int imx_serial_port_freeze(struct device *dev)
static int imx_uart_freeze(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
uart_suspend_port(&imx_reg, &sport->port);
uart_suspend_port(&imx_uart_uart_driver, &sport->port);
return clk_prepare_enable(sport->clk_ipg);
}
static int imx_serial_port_thaw(struct device *dev)
static int imx_uart_thaw(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct imx_port *sport = platform_get_drvdata(pdev);
uart_resume_port(&imx_reg, &sport->port);
uart_resume_port(&imx_uart_uart_driver, &sport->port);
clk_disable_unprepare(sport->clk_ipg);
return 0;
}
static const struct dev_pm_ops imx_serial_port_pm_ops = {
.suspend_noirq = imx_serial_port_suspend_noirq,
.resume_noirq = imx_serial_port_resume_noirq,
.freeze_noirq = imx_serial_port_suspend_noirq,
.restore_noirq = imx_serial_port_resume_noirq,
.suspend = imx_serial_port_suspend,
.resume = imx_serial_port_resume,
.freeze = imx_serial_port_freeze,
.thaw = imx_serial_port_thaw,
.restore = imx_serial_port_thaw,
static const struct dev_pm_ops imx_uart_pm_ops = {
.suspend_noirq = imx_uart_suspend_noirq,
.resume_noirq = imx_uart_resume_noirq,
.freeze_noirq = imx_uart_suspend_noirq,
.restore_noirq = imx_uart_resume_noirq,
.suspend = imx_uart_suspend,
.resume = imx_uart_resume,
.freeze = imx_uart_freeze,
.thaw = imx_uart_thaw,
.restore = imx_uart_thaw,
};
static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe,
.remove = serial_imx_remove,
static struct platform_driver imx_uart_platform_driver = {
.probe = imx_uart_probe,
.remove = imx_uart_remove,
.id_table = imx_uart_devtype,
.driver = {
.name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
.pm = &imx_serial_port_pm_ops,
.pm = &imx_uart_pm_ops,
},
};
static int __init imx_serial_init(void)
static int __init imx_uart_init(void)
{
int ret = uart_register_driver(&imx_reg);
int ret = uart_register_driver(&imx_uart_uart_driver);
if (ret)
return ret;
ret = platform_driver_register(&serial_imx_driver);
ret = platform_driver_register(&imx_uart_platform_driver);
if (ret != 0)
uart_unregister_driver(&imx_reg);
uart_unregister_driver(&imx_uart_uart_driver);
return ret;
}
static void __exit imx_serial_exit(void)
static void __exit imx_uart_exit(void)
{
platform_driver_unregister(&serial_imx_driver);
uart_unregister_driver(&imx_reg);
platform_driver_unregister(&imx_uart_platform_driver);
uart_unregister_driver(&imx_uart_uart_driver);
}
module_init(imx_serial_init);
module_exit(imx_serial_exit);
module_init(imx_uart_init);
module_exit(imx_uart_exit);
MODULE_AUTHOR("Sascha Hauer");
MODULE_DESCRIPTION("IMX generic serial port driver");
......
......@@ -1318,7 +1318,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype,
/* Setup GPIO cotroller */
s->gpio.owner = THIS_MODULE;
s->gpio.parent = dev;
s->gpio.label = dev_name(dev);
s->gpio.label = devtype->name;
s->gpio.direction_input = max310x_gpio_direction_input;
s->gpio.get = max310x_gpio_get;
s->gpio.direction_output= max310x_gpio_direction_output;
......
......@@ -65,7 +65,7 @@
#define STAT_FRM_ERR BIT(2)
#define STAT_PAR_ERR BIT(1)
#define STAT_OVR_ERR BIT(0)
#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\
#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR \
| STAT_PAR_ERR | STAT_OVR_ERR)
#define UART_BRDV 0x10
......@@ -618,7 +618,7 @@ static void wait_for_xmitr(struct uart_port *port)
u32 val;
readl_poll_timeout_atomic(port->membase + UART_STAT, val,
(val & STAT_TX_EMP), 1, 10000);
(val & STAT_TX_RDY(port)), 1, 10000);
}
static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
......
......@@ -1663,6 +1663,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->port.line = pdev->id < 0 ? 0 : pdev->id;
else if (ret < 0)
return ret;
if (s->port.line >= ARRAY_SIZE(auart_port)) {
dev_err(&pdev->dev, "serial%d out of range\n", s->port.line);
return -EINVAL;
}
if (of_id) {
pdev->id_entry = of_id->data;
......@@ -1674,8 +1678,10 @@ static int mxs_auart_probe(struct platform_device *pdev)
return ret;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENXIO;
if (!r) {
ret = -ENXIO;
goto out_disable_clks;
}
s->port.mapbase = r->start;
s->port.membase = ioremap(r->start, resource_size(r));
......@@ -1690,21 +1696,23 @@ static int mxs_auart_probe(struct platform_device *pdev)
s->mctrl_prev = 0;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
if (irq < 0) {
ret = irq;
goto out_disable_clks;
}
s->port.irq = irq;
ret = devm_request_irq(&pdev->dev, irq, mxs_auart_irq_handle, 0,
dev_name(&pdev->dev), s);
if (ret)
return ret;
goto out_disable_clks;
platform_set_drvdata(pdev, s);
ret = mxs_auart_init_gpios(s, &pdev->dev);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize GPIOs.\n");
return ret;
goto out_disable_clks;
}
/*
......@@ -1712,7 +1720,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
*/
ret = mxs_auart_request_gpio_irq(s);
if (ret)
return ret;
goto out_disable_clks;
auart_port[s->port.line] = s;
......@@ -1720,7 +1728,7 @@ static int mxs_auart_probe(struct platform_device *pdev)
ret = uart_add_one_port(&auart_driver, &s->port);
if (ret)
goto out_disable_clks_free_qpio_irq;
goto out_free_qpio_irq;
/* ASM9260 don't have version reg */
if (is_asm9260_auart(s)) {
......@@ -1734,13 +1742,15 @@ static int mxs_auart_probe(struct platform_device *pdev)
return 0;
out_disable_clks_free_qpio_irq:
if (s->clk)
clk_disable_unprepare(s->clk_ahb);
if (s->clk_ahb)
clk_disable_unprepare(s->clk_ahb);
out_free_qpio_irq:
mxs_auart_free_gpio_irq(s);
auart_port[pdev->id] = NULL;
out_disable_clks:
if (is_asm9260_auart(s)) {
clk_disable_unprepare(s->clk);
clk_disable_unprepare(s->clk_ahb);
}
return ret;
}
......@@ -1751,6 +1761,10 @@ static int mxs_auart_remove(struct platform_device *pdev)
uart_remove_one_port(&auart_driver, &s->port);
auart_port[pdev->id] = NULL;
mxs_auart_free_gpio_irq(s);
if (is_asm9260_auart(s)) {
clk_disable_unprepare(s->clk);
clk_disable_unprepare(s->clk_ahb);
}
return 0;
}
......
......@@ -885,6 +885,10 @@ static int serial_pxa_probe(struct platform_device *dev)
sport->port.line = dev->id;
else if (ret < 0)
goto err_clk;
if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
return -EINVAL;
}
snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
sport->port.membase = ioremap(mmres->start, resource_size(mmres));
......
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/qcom-geni-se.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
/* UART specific GENI registers */
#define SE_UART_TX_TRANS_CFG 0x25c
#define SE_UART_TX_WORD_LEN 0x268
#define SE_UART_TX_STOP_BIT_LEN 0x26c
#define SE_UART_TX_TRANS_LEN 0x270
#define SE_UART_RX_TRANS_CFG 0x280
#define SE_UART_RX_WORD_LEN 0x28c
#define SE_UART_RX_STALE_CNT 0x294
#define SE_UART_TX_PARITY_CFG 0x2a4
#define SE_UART_RX_PARITY_CFG 0x2a8
/* SE_UART_TRANS_CFG */
#define UART_TX_PAR_EN BIT(0)
#define UART_CTS_MASK BIT(1)
/* SE_UART_TX_WORD_LEN */
#define TX_WORD_LEN_MSK GENMASK(9, 0)
/* SE_UART_TX_STOP_BIT_LEN */
#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0)
#define TX_STOP_BIT_LEN_1 0
#define TX_STOP_BIT_LEN_1_5 1
#define TX_STOP_BIT_LEN_2 2
/* SE_UART_TX_TRANS_LEN */
#define TX_TRANS_LEN_MSK GENMASK(23, 0)
/* SE_UART_RX_TRANS_CFG */
#define UART_RX_INS_STATUS_BIT BIT(2)
#define UART_RX_PAR_EN BIT(3)
/* SE_UART_RX_WORD_LEN */
#define RX_WORD_LEN_MASK GENMASK(9, 0)
/* SE_UART_RX_STALE_CNT */
#define RX_STALE_CNT GENMASK(23, 0)
/* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */
#define PAR_CALC_EN BIT(0)
#define PAR_MODE_MSK GENMASK(2, 1)
#define PAR_MODE_SHFT 1
#define PAR_EVEN 0x00
#define PAR_ODD 0x01
#define PAR_SPACE 0x10
#define PAR_MARK 0x11
/* UART M_CMD OP codes */
#define UART_START_TX 0x1
#define UART_START_BREAK 0x4
#define UART_STOP_BREAK 0x5
/* UART S_CMD OP codes */
#define UART_START_READ 0x1
#define UART_PARAM 0x1
#define UART_OVERSAMPLING 32
#define STALE_TIMEOUT 16
#define DEFAULT_BITS_PER_CHAR 10
#define GENI_UART_CONS_PORTS 1
#define DEF_FIFO_DEPTH_WORDS 16
#define DEF_TX_WM 2
#define DEF_FIFO_WIDTH_BITS 32
#define UART_CONSOLE_RX_WM 2
#ifdef CONFIG_CONSOLE_POLL
#define RX_BYTES_PW 1
#else
#define RX_BYTES_PW 4
#endif
struct qcom_geni_serial_port {
struct uart_port uport;
struct geni_se se;
char name[20];
u32 tx_fifo_depth;
u32 tx_fifo_width;
u32 rx_fifo_depth;
u32 tx_wm;
u32 rx_wm;
u32 rx_rfr;
enum geni_se_xfer_mode xfer_mode;
bool setup;
int (*handle_rx)(struct uart_port *uport, u32 bytes, bool drop);
unsigned int xmit_size;
unsigned int baud;
unsigned int tx_bytes_pw;
unsigned int rx_bytes_pw;
bool brk;
};
static const struct uart_ops qcom_geni_serial_pops;
static struct uart_driver qcom_geni_console_driver;
static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop);
static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port);
static void qcom_geni_serial_stop_rx(struct uart_port *uport);
static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200,
32000000, 48000000, 64000000, 80000000,
96000000, 100000000};
#define to_dev_port(ptr, member) \
container_of(ptr, struct qcom_geni_serial_port, member)
static struct qcom_geni_serial_port qcom_geni_console_port;
static int qcom_geni_serial_request_port(struct uart_port *uport)
{
struct platform_device *pdev = to_platform_device(uport->dev);
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
uport->membase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(uport->membase))
return PTR_ERR(uport->membase);
port->se.base = uport->membase;
return 0;
}
static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags)
{
if (cfg_flags & UART_CONFIG_TYPE) {
uport->type = PORT_MSM;
qcom_geni_serial_request_port(uport);
}
}
static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport)
{
return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
}
static void qcom_geni_cons_set_mctrl(struct uart_port *uport,
unsigned int mctrl)
{
}
static const char *qcom_geni_serial_get_type(struct uart_port *uport)
{
return "MSM";
}
static struct qcom_geni_serial_port *get_port_from_line(int line)
{
if (line < 0 || line >= GENI_UART_CONS_PORTS)
return ERR_PTR(-ENXIO);
return &qcom_geni_console_port;
}
static bool qcom_geni_serial_poll_bit(struct uart_port *uport,
int offset, int field, bool set)
{
u32 reg;
struct qcom_geni_serial_port *port;
unsigned int baud;
unsigned int fifo_bits;
unsigned long timeout_us = 20000;
/* Ensure polling is not re-ordered before the prior writes/reads */
mb();
if (uport->private_data) {
port = to_dev_port(uport, uport);
baud = port->baud;
if (!baud)
baud = 115200;
fifo_bits = port->tx_fifo_depth * port->tx_fifo_width;
/*
* Total polling iterations based on FIFO worth of bytes to be
* sent at current baud. Add a little fluff to the wait.
*/
timeout_us = ((fifo_bits * USEC_PER_SEC) / baud) + 500;
}
return !readl_poll_timeout_atomic(uport->membase + offset, reg,
(bool)(reg & field) == set, 10, timeout_us);
}
static void qcom_geni_serial_setup_tx(struct uart_port *uport, u32 xmit_size)
{
u32 m_cmd;
writel_relaxed(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
m_cmd = UART_START_TX << M_OPCODE_SHFT;
writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
}
static void qcom_geni_serial_poll_tx_done(struct uart_port *uport)
{
int done;
u32 irq_clear = M_CMD_DONE_EN;
done = qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_DONE_EN, true);
if (!done) {
writel_relaxed(M_GENI_CMD_ABORT, uport->membase +
SE_GENI_M_CMD_CTRL_REG);
irq_clear |= M_CMD_ABORT_EN;
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
}
writel_relaxed(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
static void qcom_geni_serial_abort_rx(struct uart_port *uport)
{
u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_ABORT, false);
writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
writel_relaxed(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
}
#ifdef CONFIG_CONSOLE_POLL
static int qcom_geni_serial_get_char(struct uart_port *uport)
{
u32 rx_fifo;
u32 status;
status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
writel_relaxed(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
writel_relaxed(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
/*
* Ensure the writes to clear interrupts is not re-ordered after
* reading the data.
*/
mb();
status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
if (!(status & RX_FIFO_WC_MSK))
return NO_POLL_CHAR;
rx_fifo = readl(uport->membase + SE_GENI_RX_FIFOn);
return rx_fifo & 0xff;
}
static void qcom_geni_serial_poll_put_char(struct uart_port *uport,
unsigned char c)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
writel_relaxed(port->tx_wm, uport->membase + SE_GENI_TX_WATERMARK_REG);
qcom_geni_serial_setup_tx(uport, 1);
WARN_ON(!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_TX_FIFO_WATERMARK_EN, true));
writel_relaxed(c, uport->membase + SE_GENI_TX_FIFOn);
writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
qcom_geni_serial_poll_tx_done(uport);
}
#endif
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
{
writel_relaxed(ch, uport->membase + SE_GENI_TX_FIFOn);
}
static void
__qcom_geni_serial_console_write(struct uart_port *uport, const char *s,
unsigned int count)
{
int i;
u32 bytes_to_send = count;
for (i = 0; i < count; i++) {
if (s[i] == '\n')
bytes_to_send++;
}
writel_relaxed(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
qcom_geni_serial_setup_tx(uport, bytes_to_send);
for (i = 0; i < count; ) {
size_t chars_to_write = 0;
size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_TX_WM;
/*
* If the WM bit never set, then the Tx state machine is not
* in a valid state, so break, cancel/abort any existing
* command. Unfortunately the current data being written is
* lost.
*/
if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_TX_FIFO_WATERMARK_EN, true))
break;
chars_to_write = min_t(size_t, (size_t)(count - i), avail / 2);
uart_console_write(uport, s + i, chars_to_write,
qcom_geni_serial_wr_char);
writel_relaxed(M_TX_FIFO_WATERMARK_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
i += chars_to_write;
}
qcom_geni_serial_poll_tx_done(uport);
}
static void qcom_geni_serial_console_write(struct console *co, const char *s,
unsigned int count)
{
struct uart_port *uport;
struct qcom_geni_serial_port *port;
bool locked = true;
unsigned long flags;
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
port = get_port_from_line(co->index);
if (IS_ERR(port))
return;
uport = &port->uport;
if (oops_in_progress)
locked = spin_trylock_irqsave(&uport->lock, flags);
else
spin_lock_irqsave(&uport->lock, flags);
/* Cancel the current write to log the fault */
if (!locked) {
geni_se_cancel_m_cmd(&port->se);
if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_CANCEL_EN, true)) {
geni_se_abort_m_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
writel_relaxed(M_CMD_ABORT_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
}
writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
}
__qcom_geni_serial_console_write(uport, s, count);
if (locked)
spin_unlock_irqrestore(&uport->lock, flags);
}
static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
{
u32 i;
unsigned char buf[sizeof(u32)];
struct tty_port *tport;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
tport = &uport->state->port;
for (i = 0; i < bytes; ) {
int c;
int chunk = min_t(int, bytes - i, port->rx_bytes_pw);
ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1);
i += chunk;
if (drop)
continue;
for (c = 0; c < chunk; c++) {
int sysrq;
uport->icount.rx++;
if (port->brk && buf[c] == 0) {
port->brk = false;
if (uart_handle_break(uport))
continue;
}
sysrq = uart_handle_sysrq_char(uport, buf[c]);
if (!sysrq)
tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
}
}
if (!drop)
tty_flip_buffer_push(tport);
return 0;
}
#else
static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
{
return -EPERM;
}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void qcom_geni_serial_start_tx(struct uart_port *uport)
{
u32 irq_en;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 status;
if (port->xfer_mode == GENI_SE_FIFO) {
status = readl_relaxed(uport->membase + SE_GENI_STATUS);
if (status & M_GENI_CMD_ACTIVE)
return;
if (!qcom_geni_serial_tx_empty(uport))
return;
/*
* Ensure writing to IRQ_EN & watermark registers are not
* re-ordered before checking the status of the Serial
* Engine and TX FIFO
*/
mb();
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en |= M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN;
writel_relaxed(port->tx_wm, uport->membase +
SE_GENI_TX_WATERMARK_REG);
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
}
static void qcom_geni_serial_stop_tx(struct uart_port *uport)
{
u32 irq_en;
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en &= ~M_CMD_DONE_EN;
if (port->xfer_mode == GENI_SE_FIFO) {
irq_en &= ~M_TX_FIFO_WATERMARK_EN;
writel_relaxed(0, uport->membase +
SE_GENI_TX_WATERMARK_REG);
}
port->xmit_size = 0;
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
status = readl_relaxed(uport->membase + SE_GENI_STATUS);
/* Possible stop tx is called multiple times. */
if (!(status & M_GENI_CMD_ACTIVE))
return;
/*
* Ensure cancel command write is not re-ordered before checking
* the status of the Primary Sequencer.
*/
mb();
geni_se_cancel_m_cmd(&port->se);
if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_CANCEL_EN, true)) {
geni_se_abort_m_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
M_CMD_ABORT_EN, true);
writel_relaxed(M_CMD_ABORT_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
}
writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR);
}
static void qcom_geni_serial_start_rx(struct uart_port *uport)
{
u32 irq_en;
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
status = readl_relaxed(uport->membase + SE_GENI_STATUS);
if (status & S_GENI_CMD_ACTIVE)
qcom_geni_serial_stop_rx(uport);
/*
* Ensure setup command write is not re-ordered before checking
* the status of the Secondary Sequencer.
*/
mb();
geni_se_setup_s_cmd(&port->se, UART_START_READ, 0);
if (port->xfer_mode == GENI_SE_FIFO) {
irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN;
writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN;
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
}
static void qcom_geni_serial_stop_rx(struct uart_port *uport)
{
u32 irq_en;
u32 status;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
u32 irq_clear = S_CMD_DONE_EN;
if (port->xfer_mode == GENI_SE_FIFO) {
irq_en = readl_relaxed(uport->membase + SE_GENI_S_IRQ_EN);
irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN);
writel_relaxed(irq_en, uport->membase + SE_GENI_S_IRQ_EN);
irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN);
writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN);
}
status = readl_relaxed(uport->membase + SE_GENI_STATUS);
/* Possible stop rx is called multiple times. */
if (!(status & S_GENI_CMD_ACTIVE))
return;
/*
* Ensure cancel command write is not re-ordered before checking
* the status of the Secondary Sequencer.
*/
mb();
geni_se_cancel_s_cmd(&port->se);
qcom_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
S_GENI_CMD_CANCEL, false);
status = readl_relaxed(uport->membase + SE_GENI_STATUS);
writel_relaxed(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
if (status & S_GENI_CMD_ACTIVE)
qcom_geni_serial_abort_rx(uport);
}
static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop)
{
u32 status;
u32 word_cnt;
u32 last_word_byte_cnt;
u32 last_word_partial;
u32 total_bytes;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
status = readl_relaxed(uport->membase + SE_GENI_RX_FIFO_STATUS);
word_cnt = status & RX_FIFO_WC_MSK;
last_word_partial = status & RX_LAST;
last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
RX_LAST_BYTE_VALID_SHFT;
if (!word_cnt)
return;
total_bytes = port->rx_bytes_pw * (word_cnt - 1);
if (last_word_partial && last_word_byte_cnt)
total_bytes += last_word_byte_cnt;
else
total_bytes += port->rx_bytes_pw;
port->handle_rx(uport, total_bytes, drop);
}
static void qcom_geni_serial_handle_tx(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
struct circ_buf *xmit = &uport->state->xmit;
size_t avail;
size_t remaining;
int i;
u32 status;
unsigned int chunk;
int tail;
chunk = uart_circ_chars_pending(xmit);
status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
/* Both FIFO and framework buffer are drained */
if (chunk == port->xmit_size && !status) {
port->xmit_size = 0;
uart_circ_clear(xmit);
qcom_geni_serial_stop_tx(uport);
goto out_write_wakeup;
}
chunk -= port->xmit_size;
avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw;
tail = (xmit->tail + port->xmit_size) & (UART_XMIT_SIZE - 1);
if (chunk > (UART_XMIT_SIZE - tail))
chunk = UART_XMIT_SIZE - tail;
if (chunk > avail)
chunk = avail;
if (!chunk)
goto out_write_wakeup;
qcom_geni_serial_setup_tx(uport, chunk);
remaining = chunk;
for (i = 0; i < chunk; ) {
unsigned int tx_bytes;
unsigned int buf = 0;
int c;
tx_bytes = min_t(size_t, remaining, (size_t)port->tx_bytes_pw);
for (c = 0; c < tx_bytes ; c++)
buf |= (xmit->buf[tail + c] << (c * BITS_PER_BYTE));
writel_relaxed(buf, uport->membase + SE_GENI_TX_FIFOn);
i += tx_bytes;
tail = (tail + tx_bytes) & (UART_XMIT_SIZE - 1);
uport->icount.tx += tx_bytes;
remaining -= tx_bytes;
}
qcom_geni_serial_poll_tx_done(uport);
port->xmit_size += chunk;
out_write_wakeup:
uart_write_wakeup(uport);
}
static irqreturn_t qcom_geni_serial_isr(int isr, void *dev)
{
unsigned int m_irq_status;
unsigned int s_irq_status;
struct uart_port *uport = dev;
unsigned long flags;
unsigned int m_irq_en;
bool drop_rx = false;
struct tty_port *tport = &uport->state->port;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
if (uport->suspended)
return IRQ_HANDLED;
spin_lock_irqsave(&uport->lock, flags);
m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS);
s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS);
m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN);
writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR);
writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
if (WARN_ON(m_irq_status & M_ILLEGAL_CMD_EN))
goto out_unlock;
if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
uport->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
}
if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) &&
m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
qcom_geni_serial_handle_tx(uport);
if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) {
if (s_irq_status & S_GP_IRQ_0_EN)
uport->icount.parity++;
drop_rx = true;
} else if (s_irq_status & S_GP_IRQ_2_EN ||
s_irq_status & S_GP_IRQ_3_EN) {
uport->icount.brk++;
port->brk = true;
}
if (s_irq_status & S_RX_FIFO_WATERMARK_EN ||
s_irq_status & S_RX_FIFO_LAST_EN)
qcom_geni_serial_handle_rx(uport, drop_rx);
out_unlock:
spin_unlock_irqrestore(&uport->lock, flags);
return IRQ_HANDLED;
}
static int get_tx_fifo_size(struct qcom_geni_serial_port *port)
{
struct uart_port *uport;
if (!port)
return -ENODEV;
uport = &port->uport;
port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se);
port->tx_fifo_width = geni_se_get_tx_fifo_width(&port->se);
port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se);
uport->fifosize =
(port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
return 0;
}
static void set_rfr_wm(struct qcom_geni_serial_port *port)
{
/*
* Set RFR (Flow off) to FIFO_DEPTH - 2.
* RX WM level at 10% RX_FIFO_DEPTH.
* TX WM level at 10% TX_FIFO_DEPTH.
*/
port->rx_rfr = port->rx_fifo_depth - 2;
port->rx_wm = UART_CONSOLE_RX_WM;
port->tx_wm = DEF_TX_WM;
}
static void qcom_geni_serial_shutdown(struct uart_port *uport)
{
unsigned long flags;
/* Stop the console before stopping the current tx */
console_stop(uport->cons);
disable_irq(uport->irq);
free_irq(uport->irq, uport);
spin_lock_irqsave(&uport->lock, flags);
qcom_geni_serial_stop_tx(uport);
qcom_geni_serial_stop_rx(uport);
spin_unlock_irqrestore(&uport->lock, flags);
}
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;
set_rfr_wm(port);
writel_relaxed(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
/*
* Make an unconditional cancel on the main sequencer to reset
* it else we could end up in data loss scenarios.
*/
port->xfer_mode = GENI_SE_FIFO;
qcom_geni_serial_poll_tx_done(uport);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw,
false, true, false);
geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw,
false, false, true);
geni_se_init(&port->se, port->rx_wm, port->rx_rfr);
geni_se_select_mode(&port->se, port->xfer_mode);
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_geni%d", uport->line);
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)
return ret;
}
ret = request_irq(uport->irq, qcom_geni_serial_isr, IRQF_TRIGGER_HIGH,
port->name, uport);
if (ret)
dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret);
return ret;
}
static unsigned long get_clk_cfg(unsigned long clk_freq)
{
int i;
for (i = 0; i < ARRAY_SIZE(root_freq); i++) {
if (!(root_freq[i] % clk_freq))
return root_freq[i];
}
return 0;
}
static unsigned long get_clk_div_rate(unsigned int baud, unsigned int *clk_div)
{
unsigned long ser_clk;
unsigned long desired_clk;
desired_clk = baud * UART_OVERSAMPLING;
ser_clk = get_clk_cfg(desired_clk);
if (!ser_clk) {
pr_err("%s: Can't find matching DFS entry for baud %d\n",
__func__, baud);
return ser_clk;
}
*clk_div = ser_clk / desired_clk;
return ser_clk;
}
static void qcom_geni_serial_set_termios(struct uart_port *uport,
struct ktermios *termios, struct ktermios *old)
{
unsigned int baud;
unsigned int bits_per_char;
unsigned int tx_trans_cfg;
unsigned int tx_parity_cfg;
unsigned int rx_trans_cfg;
unsigned int rx_parity_cfg;
unsigned int stop_bit_len;
unsigned int clk_div;
unsigned long ser_clk_cfg;
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
unsigned long clk_rate;
qcom_geni_serial_stop_rx(uport);
/* baud rate */
baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
port->baud = baud;
clk_rate = get_clk_div_rate(baud, &clk_div);
if (!clk_rate)
goto out_restart_rx;
uport->uartclk = clk_rate;
clk_set_rate(port->se.clk, clk_rate);
ser_clk_cfg = SER_CLK_EN;
ser_clk_cfg |= clk_div << CLK_DIV_SHFT;
/* parity */
tx_trans_cfg = readl_relaxed(uport->membase + SE_UART_TX_TRANS_CFG);
tx_parity_cfg = readl_relaxed(uport->membase + SE_UART_TX_PARITY_CFG);
rx_trans_cfg = readl_relaxed(uport->membase + SE_UART_RX_TRANS_CFG);
rx_parity_cfg = readl_relaxed(uport->membase + SE_UART_RX_PARITY_CFG);
if (termios->c_cflag & PARENB) {
tx_trans_cfg |= UART_TX_PAR_EN;
rx_trans_cfg |= UART_RX_PAR_EN;
tx_parity_cfg |= PAR_CALC_EN;
rx_parity_cfg |= PAR_CALC_EN;
if (termios->c_cflag & PARODD) {
tx_parity_cfg |= PAR_ODD;
rx_parity_cfg |= PAR_ODD;
} else if (termios->c_cflag & CMSPAR) {
tx_parity_cfg |= PAR_SPACE;
rx_parity_cfg |= PAR_SPACE;
} else {
tx_parity_cfg |= PAR_EVEN;
rx_parity_cfg |= PAR_EVEN;
}
} else {
tx_trans_cfg &= ~UART_TX_PAR_EN;
rx_trans_cfg &= ~UART_RX_PAR_EN;
tx_parity_cfg &= ~PAR_CALC_EN;
rx_parity_cfg &= ~PAR_CALC_EN;
}
/* bits per char */
switch (termios->c_cflag & CSIZE) {
case CS5:
bits_per_char = 5;
break;
case CS6:
bits_per_char = 6;
break;
case CS7:
bits_per_char = 7;
break;
case CS8:
default:
bits_per_char = 8;
break;
}
/* stop bits */
if (termios->c_cflag & CSTOPB)
stop_bit_len = TX_STOP_BIT_LEN_2;
else
stop_bit_len = TX_STOP_BIT_LEN_1;
/* flow control, clear the CTS_MASK bit if using flow control. */
if (termios->c_cflag & CRTSCTS)
tx_trans_cfg &= ~UART_CTS_MASK;
else
tx_trans_cfg |= UART_CTS_MASK;
if (baud)
uart_update_timeout(uport, termios->c_cflag, baud);
writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
writel_relaxed(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
writel_relaxed(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
writel_relaxed(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
writel_relaxed(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_M_CLK_CFG);
writel_relaxed(ser_clk_cfg, uport->membase + GENI_SER_S_CLK_CFG);
out_restart_rx:
qcom_geni_serial_start_rx(uport);
}
static unsigned int qcom_geni_serial_tx_empty(struct uart_port *uport)
{
return !readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS);
}
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
static int __init qcom_geni_console_setup(struct console *co, char *options)
{
struct uart_port *uport;
struct qcom_geni_serial_port *port;
int baud;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index >= GENI_UART_CONS_PORTS || co->index < 0)
return -ENXIO;
port = get_port_from_line(co->index);
if (IS_ERR(port)) {
pr_err("Invalid line %d(%d)\n", co->index, (int)PTR_ERR(port));
return PTR_ERR(port);
}
uport = &port->uport;
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);
}
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(uport, co, baud, parity, bits, flow);
}
static int __init console_register(struct uart_driver *drv)
{
return uart_register_driver(drv);
}
static void console_unregister(struct uart_driver *drv)
{
uart_unregister_driver(drv);
}
static struct console cons_ops = {
.name = "ttyMSM",
.write = qcom_geni_serial_console_write,
.device = uart_console_device,
.setup = qcom_geni_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &qcom_geni_console_driver,
};
static struct uart_driver qcom_geni_console_driver = {
.owner = THIS_MODULE,
.driver_name = "qcom_geni_console",
.dev_name = "ttyMSM",
.nr = GENI_UART_CONS_PORTS,
.cons = &cons_ops,
};
#else
static int console_register(struct uart_driver *drv)
{
return 0;
}
static void console_unregister(struct uart_driver *drv)
{
}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void qcom_geni_serial_cons_pm(struct uart_port *uport,
unsigned int new_state, unsigned int old_state)
{
struct qcom_geni_serial_port *port = to_dev_port(uport, uport);
if (unlikely(!uart_console(uport)))
return;
if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
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);
}
static const struct uart_ops qcom_geni_console_pops = {
.tx_empty = qcom_geni_serial_tx_empty,
.stop_tx = qcom_geni_serial_stop_tx,
.start_tx = qcom_geni_serial_start_tx,
.stop_rx = qcom_geni_serial_stop_rx,
.set_termios = qcom_geni_serial_set_termios,
.startup = qcom_geni_serial_startup,
.request_port = qcom_geni_serial_request_port,
.config_port = qcom_geni_serial_config_port,
.shutdown = qcom_geni_serial_shutdown,
.type = qcom_geni_serial_get_type,
.set_mctrl = qcom_geni_cons_set_mctrl,
.get_mctrl = qcom_geni_cons_get_mctrl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = qcom_geni_serial_get_char,
.poll_put_char = qcom_geni_serial_poll_put_char,
#endif
.pm = qcom_geni_serial_cons_pm,
};
static int qcom_geni_serial_probe(struct platform_device *pdev)
{
int ret = 0;
int line = -1;
struct qcom_geni_serial_port *port;
struct uart_port *uport;
struct resource *res;
if (pdev->dev.of_node)
line = of_alias_get_id(pdev->dev.of_node, "serial");
else
line = pdev->id;
if (line < 0 || line >= GENI_UART_CONS_PORTS)
return -ENXIO;
port = get_port_from_line(line);
if (IS_ERR(port)) {
ret = PTR_ERR(port);
dev_err(&pdev->dev, "Invalid line %d(%d)\n", line, ret);
return ret;
}
uport = &port->uport;
/* Don't allow 2 drivers to access the same port */
if (uport->private_data)
return -ENODEV;
uport->dev = &pdev->dev;
port->se.dev = &pdev->dev;
port->se.wrapper = dev_get_drvdata(pdev->dev.parent);
port->se.clk = devm_clk_get(&pdev->dev, "se");
if (IS_ERR(port->se.clk)) {
ret = PTR_ERR(port->se.clk);
dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret);
return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
uport->mapbase = res->start;
port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS;
port->tx_fifo_width = DEF_FIFO_WIDTH_BITS;
uport->irq = platform_get_irq(pdev, 0);
if (uport->irq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ %d\n", uport->irq);
return uport->irq;
}
uport->private_data = &qcom_geni_console_driver;
platform_set_drvdata(pdev, port);
port->handle_rx = handle_rx_console;
port->setup = false;
return uart_add_one_port(&qcom_geni_console_driver, uport);
}
static int qcom_geni_serial_remove(struct platform_device *pdev)
{
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
struct uart_driver *drv = port->uport.private_data;
uart_remove_one_port(drv, &port->uport);
return 0;
}
static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
struct uart_port *uport = &port->uport;
uart_suspend_port(uport->private_data, uport);
return 0;
}
static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct qcom_geni_serial_port *port = platform_get_drvdata(pdev);
struct uart_port *uport = &port->uport;
if (console_suspend_enabled && uport->suspended) {
uart_resume_port(uport->private_data, uport);
disable_irq(uport->irq);
}
return 0;
}
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq,
qcom_geni_serial_sys_resume_noirq)
};
static const struct of_device_id qcom_geni_serial_match_table[] = {
{ .compatible = "qcom,geni-debug-uart", },
{}
};
MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table);
static struct platform_driver qcom_geni_serial_platform_driver = {
.remove = qcom_geni_serial_remove,
.probe = qcom_geni_serial_probe,
.driver = {
.name = "qcom_geni_serial",
.of_match_table = qcom_geni_serial_match_table,
.pm = &qcom_geni_serial_pm_ops,
},
};
static int __init qcom_geni_serial_init(void)
{
int ret;
qcom_geni_console_port.uport.iotype = UPIO_MEM;
qcom_geni_console_port.uport.ops = &qcom_geni_console_pops;
qcom_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
qcom_geni_console_port.uport.line = 0;
ret = console_register(&qcom_geni_console_driver);
if (ret)
return ret;
ret = platform_driver_register(&qcom_geni_serial_platform_driver);
if (ret)
console_unregister(&qcom_geni_console_driver);
return ret;
}
module_init(qcom_geni_serial_init);
static void __exit qcom_geni_serial_exit(void)
{
platform_driver_unregister(&qcom_geni_serial_platform_driver);
console_unregister(&qcom_geni_console_driver);
}
module_exit(qcom_geni_serial_exit);
MODULE_DESCRIPTION("Serial driver for GENI based QUP cores");
MODULE_LICENSE("GPL v2");
......@@ -1818,6 +1818,10 @@ static int s3c24xx_serial_probe(struct platform_device *pdev)
dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
if (index >= ARRAY_SIZE(s3c24xx_serial_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", index);
return -EINVAL;
}
ourport = &s3c24xx_serial_ports[index];
ourport->drv_data = s3c24xx_get_driver_data(pdev);
......
......@@ -1786,6 +1786,8 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
seq_printf(m, " brk:%d", uport->icount.brk);
if (uport->icount.overrun)
seq_printf(m, " oe:%d", uport->icount.overrun);
if (uport->icount.buf_overrun)
seq_printf(m, " bo:%d", uport->icount.buf_overrun);
#define INFOBIT(bit, str) \
if (uport->mctrl & (bit)) \
......
......@@ -33,6 +33,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/ktime.h>
#include <linux/major.h>
#include <linux/module.h>
#include <linux/mm.h>
......@@ -143,8 +144,8 @@ struct sci_port {
void *rx_buf[2];
size_t buf_len_rx;
struct work_struct work_tx;
struct timer_list rx_timer;
unsigned int rx_timeout;
struct hrtimer rx_timer;
unsigned int rx_timeout; /* microseconds */
#endif
unsigned int rx_frame;
int rx_trigger;
......@@ -1231,6 +1232,15 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
}
}
static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec)
{
long sec = usec / 1000000;
long nsec = (usec % 1000000) * 1000;
ktime_t t = ktime_set(sec, nsec);
hrtimer_start(hrt, t, HRTIMER_MODE_REL);
}
static void sci_dma_rx_complete(void *arg)
{
struct sci_port *s = arg;
......@@ -1249,7 +1259,7 @@ static void sci_dma_rx_complete(void *arg)
if (active >= 0)
count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
start_hrtimer_us(&s->rx_timer, s->rx_timeout);
if (count)
tty_flip_buffer_push(&port->state->port);
......@@ -1393,9 +1403,9 @@ static void work_fn_tx(struct work_struct *work)
dma_async_issue_pending(chan);
}
static void rx_timer_fn(struct timer_list *t)
static enum hrtimer_restart rx_timer_fn(struct hrtimer *t)
{
struct sci_port *s = from_timer(s, t, rx_timer);
struct sci_port *s = container_of(t, struct sci_port, rx_timer);
struct dma_chan *chan = s->chan_rx;
struct uart_port *port = &s->port;
struct dma_tx_state state;
......@@ -1412,7 +1422,7 @@ static void rx_timer_fn(struct timer_list *t)
active = sci_dma_rx_find_active(s);
if (active < 0) {
spin_unlock_irqrestore(&port->lock, flags);
return;
return HRTIMER_NORESTART;
}
status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
......@@ -1422,7 +1432,7 @@ static void rx_timer_fn(struct timer_list *t)
s->active_rx, active);
/* Let packet complete handler take care of the packet */
return;
return HRTIMER_NORESTART;
}
dmaengine_pause(chan);
......@@ -1437,7 +1447,7 @@ static void rx_timer_fn(struct timer_list *t)
if (status == DMA_COMPLETE) {
spin_unlock_irqrestore(&port->lock, flags);
dev_dbg(port->dev, "Transaction complete after DMA engine was stopped");
return;
return HRTIMER_NORESTART;
}
/* Handle incomplete DMA receive */
......@@ -1462,6 +1472,8 @@ static void rx_timer_fn(struct timer_list *t)
serial_port_out(port, SCSCR, scr | SCSCR_RIE);
spin_unlock_irqrestore(&port->lock, flags);
return HRTIMER_NORESTART;
}
static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
......@@ -1573,7 +1585,8 @@ static void sci_request_dma(struct uart_port *port)
dma += s->buf_len_rx;
}
timer_setup(&s->rx_timer, rx_timer_fn, 0);
hrtimer_init(&s->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
s->rx_timer.function = rx_timer_fn;
if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
sci_submit_rx(s);
......@@ -1632,9 +1645,9 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
/* Clear current interrupt */
serial_port_out(port, SCxSR,
ssr & ~(SCIF_DR | SCxSR_RDxF(port)));
dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n",
jiffies, s->rx_timeout);
mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
start_hrtimer_us(&s->rx_timer, s->rx_timeout);
return IRQ_HANDLED;
}
......@@ -1645,7 +1658,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
scif_set_rtrg(port, s->rx_trigger);
mod_timer(&s->rx_fifo_timer, jiffies + DIV_ROUND_UP(
s->rx_frame * s->rx_fifo_timeout, 1000));
s->rx_frame * HZ * s->rx_fifo_timeout, 1000000));
}
/* I think sci_receive_chars has to be called irrespective
......@@ -2081,7 +2094,7 @@ static void sci_shutdown(struct uart_port *port)
if (s->chan_rx) {
dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__,
port->line);
del_timer_sync(&s->rx_timer);
hrtimer_cancel(&s->rx_timer);
}
#endif
......@@ -2482,11 +2495,11 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
if (termios->c_cflag & PARENB)
bits++;
s->rx_frame = (100 * bits * HZ) / (baud / 10);
s->rx_frame = (10000 * bits) / (baud / 100);
#ifdef CONFIG_SERIAL_SH_SCI_DMA
s->rx_timeout = DIV_ROUND_UP(s->buf_len_rx * 2 * s->rx_frame, 1000);
if (s->rx_timeout < msecs_to_jiffies(20))
s->rx_timeout = msecs_to_jiffies(20);
s->rx_timeout = s->buf_len_rx * 2 * s->rx_frame;
if (s->rx_timeout < 20)
s->rx_timeout = 20;
#endif
if ((termios->c_cflag & CREAD) != 0)
......@@ -3098,6 +3111,10 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
dev_err(&pdev->dev, "failed to get alias id (%d)\n", id);
return NULL;
}
if (id >= ARRAY_SIZE(sci_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n", id);
return NULL;
}
sp = &sci_ports[id];
*dev_id = id;
......
......@@ -1283,6 +1283,11 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
goto err;
}
sirfport->port.line = of_alias_get_id(np, "serial");
if (sirfport->port.line >= ARRAY_SIZE(sirf_ports)) {
dev_err(&pdev->dev, "serial%d out of range\n",
sirfport->port.line);
return -EINVAL;
}
sirf_ports[sirfport->port.line] = sirfport;
sirfport->port.iotype = UPIO_MEM;
sirfport->port.flags = UPF_BOOT_AUTOCONF;
......
......@@ -782,6 +782,8 @@ static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev)
if (!np)
return NULL;
id = of_alias_get_id(np, "serial");
if (id < 0)
id = of_alias_get_id(np, ASC_SERIAL_NAME);
if (id < 0)
......
......@@ -62,6 +62,113 @@ static void stm32_clr_bits(struct uart_port *port, u32 reg, u32 bits)
writel_relaxed(val, port->membase + reg);
}
static void stm32_config_reg_rs485(u32 *cr1, u32 *cr3, u32 delay_ADE,
u32 delay_DDE, u32 baud)
{
u32 rs485_deat_dedt;
u32 rs485_deat_dedt_max = (USART_CR1_DEAT_MASK >> USART_CR1_DEAT_SHIFT);
bool over8;
*cr3 |= USART_CR3_DEM;
over8 = *cr1 & USART_CR1_OVER8;
if (over8)
rs485_deat_dedt = delay_ADE * baud * 8;
else
rs485_deat_dedt = delay_ADE * baud * 16;
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
rs485_deat_dedt_max : rs485_deat_dedt;
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEAT_SHIFT) &
USART_CR1_DEAT_MASK;
*cr1 |= rs485_deat_dedt;
if (over8)
rs485_deat_dedt = delay_DDE * baud * 8;
else
rs485_deat_dedt = delay_DDE * baud * 16;
rs485_deat_dedt = DIV_ROUND_CLOSEST(rs485_deat_dedt, 1000);
rs485_deat_dedt = rs485_deat_dedt > rs485_deat_dedt_max ?
rs485_deat_dedt_max : rs485_deat_dedt;
rs485_deat_dedt = (rs485_deat_dedt << USART_CR1_DEDT_SHIFT) &
USART_CR1_DEDT_MASK;
*cr1 |= rs485_deat_dedt;
}
static int stm32_config_rs485(struct uart_port *port,
struct serial_rs485 *rs485conf)
{
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
u32 usartdiv, baud, cr1, cr3;
bool over8;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
port->rs485 = *rs485conf;
rs485conf->flags |= SER_RS485_RX_DURING_TX;
if (rs485conf->flags & SER_RS485_ENABLED) {
cr1 = readl_relaxed(port->membase + ofs->cr1);
cr3 = readl_relaxed(port->membase + ofs->cr3);
usartdiv = readl_relaxed(port->membase + ofs->brr);
usartdiv = usartdiv & GENMASK(15, 0);
over8 = cr1 & USART_CR1_OVER8;
if (over8)
usartdiv = usartdiv | (usartdiv & GENMASK(4, 0))
<< USART_BRR_04_R_SHIFT;
baud = DIV_ROUND_CLOSEST(port->uartclk, usartdiv);
stm32_config_reg_rs485(&cr1, &cr3,
rs485conf->delay_rts_before_send,
rs485conf->delay_rts_after_send, baud);
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
cr3 &= ~USART_CR3_DEP;
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
} else {
cr3 |= USART_CR3_DEP;
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
}
writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr1, port->membase + ofs->cr1);
} else {
stm32_clr_bits(port, ofs->cr3, USART_CR3_DEM | USART_CR3_DEP);
stm32_clr_bits(port, ofs->cr1,
USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
}
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
static int stm32_init_rs485(struct uart_port *port,
struct platform_device *pdev)
{
struct serial_rs485 *rs485conf = &port->rs485;
rs485conf->flags = 0;
rs485conf->delay_rts_before_send = 0;
rs485conf->delay_rts_after_send = 0;
if (!pdev->dev.of_node)
return -ENODEV;
uart_get_rs485_mode(&pdev->dev, rs485conf);
return 0;
}
static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res,
bool threaded)
{
......@@ -498,6 +605,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
struct stm32_port *stm32_port = to_stm32_port(port);
struct stm32_usart_offsets *ofs = &stm32_port->info->ofs;
struct stm32_usart_config *cfg = &stm32_port->info->cfg;
struct serial_rs485 *rs485conf = &port->rs485;
unsigned int baud;
u32 usartdiv, mantissa, fraction, oversampling;
tcflag_t cflag = termios->c_cflag;
......@@ -515,7 +623,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
writel_relaxed(0, port->membase + ofs->cr1);
cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
cr1 |= BIT(cfg->uart_enable_bit);
if (stm32_port->fifoen)
cr1 |= USART_CR1_FIFOEN;
cr2 = 0;
......@@ -553,9 +661,11 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
*/
if (usartdiv < 16) {
oversampling = 8;
cr1 |= USART_CR1_OVER8;
stm32_set_bits(port, ofs->cr1, USART_CR1_OVER8);
} else {
oversampling = 16;
cr1 &= ~USART_CR1_OVER8;
stm32_clr_bits(port, ofs->cr1, USART_CR1_OVER8);
}
......@@ -592,10 +702,28 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios,
if (stm32_port->rx_ch)
cr3 |= USART_CR3_DMAR;
if (rs485conf->flags & SER_RS485_ENABLED) {
stm32_config_reg_rs485(&cr1, &cr3,
rs485conf->delay_rts_before_send,
rs485conf->delay_rts_after_send, baud);
if (rs485conf->flags & SER_RS485_RTS_ON_SEND) {
cr3 &= ~USART_CR3_DEP;
rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
} else {
cr3 |= USART_CR3_DEP;
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
}
} else {
cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP);
cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK);
}
writel_relaxed(cr3, port->membase + ofs->cr3);
writel_relaxed(cr2, port->membase + ofs->cr2);
writel_relaxed(cr1, port->membase + ofs->cr1);
stm32_set_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit));
spin_unlock_irqrestore(&port->lock, flags);
}
......@@ -681,6 +809,10 @@ static int stm32_init_port(struct stm32_port *stm32port,
port->ops = &stm32_uart_ops;
port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0);
port->rs485_config = stm32_config_rs485;
stm32_init_rs485(port, pdev);
stm32port->wakeirq = platform_get_irq(pdev, 1);
stm32port->fifoen = stm32port->info->cfg.has_fifo;
......
......@@ -135,6 +135,7 @@ struct stm32_usart_info stm32h7_info = {
#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4
#define USART_BRR_04_R_SHIFT 1
/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
......@@ -162,6 +163,8 @@ struct stm32_usart_info stm32h7_info = {
#define USART_CR1_M1 BIT(28) /* F7 */
#define USART_CR1_IE_MASK (GENMASK(8, 4) | BIT(14) | BIT(26) | BIT(27))
#define USART_CR1_FIFOEN BIT(29) /* H7 */
#define USART_CR1_DEAT_SHIFT 21
#define USART_CR1_DEDT_SHIFT 16
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0) /* F4 */
......
......@@ -1110,7 +1110,7 @@ static struct uart_port *cdns_uart_get_port(int id)
struct uart_port *port;
/* Try the given port id if failed use default method */
if (cdns_uart_port[id].mapbase != 0) {
if (id < CDNS_UART_NR_PORTS && cdns_uart_port[id].mapbase != 0) {
/* Find the next unused port */
for (id = 0; id < CDNS_UART_NR_PORTS; id++)
if (cdns_uart_port[id].mapbase == 0)
......
......@@ -1354,6 +1354,11 @@ static void csi_m(struct vc_data *vc)
case 3:
vc->vc_italic = 1;
break;
case 21:
/*
* No console drivers support double underline, so
* convert it to a single underline.
*/
case 4:
vc->vc_underline = 1;
break;
......@@ -1389,7 +1394,6 @@ static void csi_m(struct vc_data *vc)
vc->vc_disp_ctrl = 1;
vc->vc_toggle_meta = 1;
break;
case 21:
case 22:
vc->vc_intensity = 1;
break;
......
......@@ -1217,7 +1217,7 @@ sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
/* Interface routine */
static int
sisusbcon_font_set(struct vc_data *c, struct console_font *font,
unsigned flags)
unsigned int flags)
{
struct sisusb_usb_data *sisusb;
unsigned charcount = font->charcount;
......@@ -1338,29 +1338,65 @@ static void sisusbdummycon_init(struct vc_data *vc, int init)
vc_resize(vc, 80, 25);
}
static int sisusbdummycon_dummy(void)
static void sisusbdummycon_deinit(struct vc_data *vc) { }
static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx,
int height, int width) { }
static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos,
int xpos) { }
static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { }
static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines)
{
return false;
}
static int sisusbdummycon_switch(struct vc_data *vc)
{
return 0;
}
static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch)
{
return 0;
}
static int sisusbdummycon_font_set(struct vc_data *vc,
struct console_font *font,
unsigned int flags)
{
return 0;
}
static int sisusbdummycon_font_default(struct vc_data *vc,
struct console_font *font, char *name)
{
return 0;
}
#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy
static int sisusbdummycon_font_copy(struct vc_data *vc, int con)
{
return 0;
}
static const struct consw sisusb_dummy_con = {
.owner = THIS_MODULE,
.con_startup = sisusbdummycon_startup,
.con_init = sisusbdummycon_init,
.con_deinit = SISUSBCONDUMMY,
.con_clear = SISUSBCONDUMMY,
.con_putc = SISUSBCONDUMMY,
.con_putcs = SISUSBCONDUMMY,
.con_cursor = SISUSBCONDUMMY,
.con_scroll = SISUSBCONDUMMY,
.con_switch = SISUSBCONDUMMY,
.con_blank = SISUSBCONDUMMY,
.con_font_set = SISUSBCONDUMMY,
.con_font_get = SISUSBCONDUMMY,
.con_font_default = SISUSBCONDUMMY,
.con_font_copy = SISUSBCONDUMMY,
.con_deinit = sisusbdummycon_deinit,
.con_clear = sisusbdummycon_clear,
.con_putc = sisusbdummycon_putc,
.con_putcs = sisusbdummycon_putcs,
.con_cursor = sisusbdummycon_cursor,
.con_scroll = sisusbdummycon_scroll,
.con_switch = sisusbdummycon_switch,
.con_blank = sisusbdummycon_blank,
.con_font_set = sisusbdummycon_font_set,
.con_font_default = sisusbdummycon_font_default,
.con_font_copy = sisusbdummycon_font_copy,
};
int
......
......@@ -41,12 +41,47 @@ static void dummycon_init(struct vc_data *vc, int init)
vc_resize(vc, DUMMY_COLUMNS, DUMMY_ROWS);
}
static int dummycon_dummy(void)
static void dummycon_deinit(struct vc_data *vc) { }
static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
int width) { }
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
static void dummycon_cursor(struct vc_data *vc, int mode) { }
static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines)
{
return false;
}
static int dummycon_switch(struct vc_data *vc)
{
return 0;
}
static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
{
return 0;
}
#define DUMMY (void *)dummycon_dummy
static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
unsigned int flags)
{
return 0;
}
static int dummycon_font_default(struct vc_data *vc,
struct console_font *font, char *name)
{
return 0;
}
static int dummycon_font_copy(struct vc_data *vc, int con)
{
return 0;
}
/*
* The console `switch' structure for the dummy console
......@@ -58,16 +93,16 @@ const struct consw dummy_con = {
.owner = THIS_MODULE,
.con_startup = dummycon_startup,
.con_init = dummycon_init,
.con_deinit = DUMMY,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_cursor = DUMMY,
.con_scroll = DUMMY,
.con_switch = DUMMY,
.con_blank = DUMMY,
.con_font_set = DUMMY,
.con_font_default = DUMMY,
.con_font_copy = DUMMY,
.con_deinit = dummycon_deinit,
.con_clear = dummycon_clear,
.con_putc = dummycon_putc,
.con_putcs = dummycon_putcs,
.con_cursor = dummycon_cursor,
.con_scroll = dummycon_scroll,
.con_switch = dummycon_switch,
.con_blank = dummycon_blank,
.con_font_set = dummycon_font_set,
.con_font_default = dummycon_font_default,
.con_font_copy = dummycon_font_copy,
};
EXPORT_SYMBOL_GPL(dummy_con);
......@@ -673,12 +673,12 @@ static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
return true;
}
static int newport_dummy(struct vc_data *c)
static int newport_set_origin(struct vc_data *vc)
{
return 0;
}
#define DUMMY (void *) newport_dummy
static void newport_save_screen(struct vc_data *vc) { }
const struct consw newport_con = {
.owner = THIS_MODULE,
......@@ -694,8 +694,8 @@ const struct consw newport_con = {
.con_blank = newport_blank,
.con_font_set = newport_font_set,
.con_font_default = newport_font_default,
.con_set_origin = DUMMY,
.con_save_screen = DUMMY
.con_set_origin = newport_set_origin,
.con_save_screen = newport_save_screen
};
static int newport_probe(struct gio_device *dev,
......
......@@ -1272,7 +1272,8 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
return 0;
}
static int vgacon_font_set(struct vc_data *c, struct console_font *font, unsigned flags)
static int vgacon_font_set(struct vc_data *c, struct console_font *font,
unsigned int flags)
{
unsigned charcount = font->charcount;
int rc;
......@@ -1407,21 +1408,20 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
* The console `switch' structure for the VGA based console
*/
static int vgacon_dummy(struct vc_data *c)
{
return 0;
}
#define DUMMY (void *) vgacon_dummy
static void vgacon_clear(struct vc_data *vc, int sy, int sx, int height,
int width) { }
static void vgacon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
static void vgacon_putcs(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos) { }
const struct consw vga_con = {
.owner = THIS_MODULE,
.con_startup = vgacon_startup,
.con_init = vgacon_init,
.con_deinit = vgacon_deinit,
.con_clear = DUMMY,
.con_putc = DUMMY,
.con_putcs = DUMMY,
.con_clear = vgacon_clear,
.con_putc = vgacon_putc,
.con_putcs = vgacon_putcs,
.con_cursor = vgacon_cursor,
.con_scroll = vgacon_scroll,
.con_switch = vgacon_switch,
......
......@@ -2595,7 +2595,8 @@ static int fbcon_copy_font(struct vc_data *vc, int con)
* is ever implemented.
*/
static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags)
static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
unsigned int flags)
{
struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
unsigned charcount = font->charcount;
......
......@@ -138,10 +138,6 @@ static int devpts_ptmx_path(struct path *path)
struct super_block *sb;
int err;
/* Has the devpts filesystem already been found? */
if (path->mnt->mnt_sb->s_magic == DEVPTS_SUPER_MAGIC)
return 0;
/* Is a devpts filesystem at "pts" in the same directory? */
err = path_pts(path);
if (err)
......@@ -156,25 +152,53 @@ static int devpts_ptmx_path(struct path *path)
return 0;
}
/*
* Try to find a suitable devpts filesystem. We support the following
* scenarios:
* - The ptmx device node is located in the same directory as the devpts
* mount where the pts device nodes are located.
* This is e.g. the case when calling open on the /dev/pts/ptmx device
* node when the devpts filesystem is mounted at /dev/pts.
* - The ptmx device node is located outside the devpts filesystem mount
* where the pts device nodes are located. For example, the ptmx device
* is a symlink, separate device node, or bind-mount.
* A supported scenario is bind-mounting /dev/pts/ptmx to /dev/ptmx and
* then calling open on /dev/ptmx. In this case a suitable pts
* subdirectory can be found in the common parent directory /dev of the
* devpts mount and the ptmx bind-mount, after resolving the /dev/ptmx
* bind-mount.
* If no suitable pts subdirectory can be found this function will fail.
* This is e.g. the case when bind-mounting /dev/pts/ptmx to /ptmx.
*/
struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
{
struct path path;
int err;
int err = 0;
path = filp->f_path;
path_get(&path);
/* Walk upward while the start point is a bind mount of
* a single file.
*/
while (path.mnt->mnt_root == path.dentry)
if (follow_up(&path) == 0)
break;
/* devpts_ptmx_path() finds a devpts fs or returns an error. */
if ((path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) ||
(DEVPTS_SB(path.mnt->mnt_sb) != fsi))
err = devpts_ptmx_path(&path);
dput(path.dentry);
if (err) {
mntput(path.mnt);
return ERR_PTR(err);
if (!err) {
if (DEVPTS_SB(path.mnt->mnt_sb) == fsi)
return path.mnt;
err = -ENODEV;
}
if (DEVPTS_SB(path.mnt->mnt_sb) != fsi) {
mntput(path.mnt);
return ERR_PTR(-ENODEV);
}
return path.mnt;
return ERR_PTR(err);
}
struct pts_fs_info *devpts_acquire(struct file *filp)
......@@ -182,16 +206,20 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
struct pts_fs_info *result;
struct path path;
struct super_block *sb;
int err;
path = filp->f_path;
path_get(&path);
/* Has the devpts filesystem already been found? */
if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
int err;
err = devpts_ptmx_path(&path);
if (err) {
result = ERR_PTR(err);
goto out;
}
}
/*
* pty code needs to hold extra references in case of last /dev/tty close
......
......@@ -46,46 +46,52 @@ enum con_scroll {
struct consw {
struct module *owner;
const char *(*con_startup)(void);
void (*con_init)(struct vc_data *, int);
void (*con_deinit)(struct vc_data *);
void (*con_clear)(struct vc_data *, int, int, int, int);
void (*con_putc)(struct vc_data *, int, int, int);
void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int);
void (*con_cursor)(struct vc_data *, int);
bool (*con_scroll)(struct vc_data *, unsigned int top,
void (*con_init)(struct vc_data *vc, int init);
void (*con_deinit)(struct vc_data *vc);
void (*con_clear)(struct vc_data *vc, int sy, int sx, int height,
int width);
void (*con_putc)(struct vc_data *vc, int c, int ypos, int xpos);
void (*con_putcs)(struct vc_data *vc, const unsigned short *s,
int count, int ypos, int xpos);
void (*con_cursor)(struct vc_data *vc, int mode);
bool (*con_scroll)(struct vc_data *vc, unsigned int top,
unsigned int bottom, enum con_scroll dir,
unsigned int lines);
int (*con_switch)(struct vc_data *);
int (*con_blank)(struct vc_data *, int, int);
int (*con_font_set)(struct vc_data *, struct console_font *, unsigned);
int (*con_font_get)(struct vc_data *, struct console_font *);
int (*con_font_default)(struct vc_data *, struct console_font *, char *);
int (*con_font_copy)(struct vc_data *, int);
int (*con_resize)(struct vc_data *, unsigned int, unsigned int,
unsigned int);
void (*con_set_palette)(struct vc_data *,
int (*con_switch)(struct vc_data *vc);
int (*con_blank)(struct vc_data *vc, int blank, int mode_switch);
int (*con_font_set)(struct vc_data *vc, struct console_font *font,
unsigned int flags);
int (*con_font_get)(struct vc_data *vc, struct console_font *font);
int (*con_font_default)(struct vc_data *vc,
struct console_font *font, char *name);
int (*con_font_copy)(struct vc_data *vc, int con);
int (*con_resize)(struct vc_data *vc, unsigned int width,
unsigned int height, unsigned int user);
void (*con_set_palette)(struct vc_data *vc,
const unsigned char *table);
void (*con_scrolldelta)(struct vc_data *, int lines);
int (*con_set_origin)(struct vc_data *);
void (*con_save_screen)(struct vc_data *);
u8 (*con_build_attr)(struct vc_data *, u8, u8, u8, u8, u8, u8);
void (*con_invert_region)(struct vc_data *, u16 *, int);
u16 *(*con_screen_pos)(struct vc_data *, int);
unsigned long (*con_getxy)(struct vc_data *, unsigned long, int *, int *);
void (*con_scrolldelta)(struct vc_data *vc, int lines);
int (*con_set_origin)(struct vc_data *vc);
void (*con_save_screen)(struct vc_data *vc);
u8 (*con_build_attr)(struct vc_data *vc, u8 color, u8 intensity,
u8 blink, u8 underline, u8 reverse, u8 italic);
void (*con_invert_region)(struct vc_data *vc, u16 *p, int count);
u16 *(*con_screen_pos)(struct vc_data *vc, int offset);
unsigned long (*con_getxy)(struct vc_data *vc, unsigned long position,
int *px, int *py);
/*
* Flush the video console driver's scrollback buffer
*/
void (*con_flush_scrollback)(struct vc_data *);
void (*con_flush_scrollback)(struct vc_data *vc);
/*
* Prepare the console for the debugger. This includes, but is not
* limited to, unblanking the console, loading an appropriate
* palette, and allowing debugger generated output.
*/
int (*con_debug_enter)(struct vc_data *);
int (*con_debug_enter)(struct vc_data *vc);
/*
* Restore the console to its pre-debug state as closely as possible.
*/
int (*con_debug_leave)(struct vc_data *);
int (*con_debug_leave)(struct vc_data *vc);
};
extern const struct consw *conswitchp;
......
......@@ -2557,6 +2557,9 @@
#define PCI_DEVICE_ID_TEHUTI_3010 0x3010
#define PCI_DEVICE_ID_TEHUTI_3014 0x3014
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
#define PCI_VENDOR_ID_HINT 0x3388
#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013
......
......@@ -379,7 +379,7 @@ extern int of_setup_earlycon(const struct earlycon_id *match,
extern bool earlycon_acpi_spcr_enable __initdata;
int setup_earlycon(char *buf);
#else
static const bool earlycon_acpi_spcr_enable;
static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED;
static inline int setup_earlycon(char *buf) { return 0; }
#endif
......
......@@ -76,6 +76,9 @@
#define PORT_SUNZILOG 38
#define PORT_SUNSAB 39
/* Nuvoton UART */
#define PORT_NPCM 40
/* Intel EG20 */
#define PORT_PCH_8LINE 44
#define PORT_PCH_2LINE 45
......
......@@ -7,6 +7,7 @@ TARGETS += cpufreq
TARGETS += cpu-hotplug
TARGETS += efivarfs
TARGETS += exec
TARGETS += filesystems
TARGETS += firmware
TARGETS += ftrace
TARGETS += futex
......
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := dnotify_test
TEST_PROGS := dnotify_test devpts_pts
all: $(TEST_PROGS)
include ../lib.mk
......
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/wait.h>
static bool terminal_dup2(int duplicate, int original)
{
int ret;
ret = dup2(duplicate, original);
if (ret < 0)
return false;
return true;
}
static int terminal_set_stdfds(int fd)
{
int i;
if (fd < 0)
return 0;
for (i = 0; i < 3; i++)
if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO,
STDERR_FILENO}[i]))
return -1;
return 0;
}
static int login_pty(int fd)
{
int ret;
setsid();
ret = ioctl(fd, TIOCSCTTY, NULL);
if (ret < 0)
return -1;
ret = terminal_set_stdfds(fd);
if (ret < 0)
return -1;
if (fd > STDERR_FILENO)
close(fd);
return 0;
}
static int wait_for_pid(pid_t pid)
{
int status, ret;
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
return -1;
}
if (ret != pid)
goto again;
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return -1;
return 0;
}
static int resolve_procfd_symlink(int fd, char *buf, size_t buflen)
{
int ret;
char procfd[4096];
ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd);
if (ret < 0 || ret >= 4096)
return -1;
ret = readlink(procfd, buf, buflen);
if (ret < 0 || (size_t)ret >= buflen)
return -1;
buf[ret] = '\0';
return 0;
}
static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents)
{
int ret;
int master = -1, slave = -1, fret = -1;
master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC);
if (master < 0) {
fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx,
strerror(errno));
return -1;
}
/*
* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also
* not really needed.
*/
ret = unlockpt(master);
if (ret < 0) {
fprintf(stderr, "Failed to unlock terminal\n");
goto do_cleanup;
}
#ifdef TIOCGPTPEER
slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
#endif
if (slave < 0) {
if (errno == EINVAL) {
fprintf(stderr, "TIOCGPTPEER is not supported. "
"Skipping test.\n");
fret = EXIT_SUCCESS;
}
fprintf(stderr, "Failed to perform TIOCGPTPEER ioctl\n");
goto do_cleanup;
}
pid_t pid = fork();
if (pid < 0)
goto do_cleanup;
if (pid == 0) {
char buf[4096];
ret = login_pty(slave);
if (ret < 0) {
fprintf(stderr, "Failed to setup terminal\n");
_exit(EXIT_FAILURE);
}
ret = resolve_procfd_symlink(STDIN_FILENO, buf, sizeof(buf));
if (ret < 0) {
fprintf(stderr, "Failed to retrieve pathname of pts "
"slave file descriptor\n");
_exit(EXIT_FAILURE);
}
if (strncmp(expected_procfd_contents, buf,
strlen(expected_procfd_contents)) != 0) {
fprintf(stderr, "Received invalid contents for "
"\"/proc/<pid>/fd/%d\" symlink: %s\n",
STDIN_FILENO, buf);
_exit(-1);
}
fprintf(stderr, "Contents of \"/proc/<pid>/fd/%d\" "
"symlink are valid: %s\n", STDIN_FILENO, buf);
_exit(EXIT_SUCCESS);
}
ret = wait_for_pid(pid);
if (ret < 0)
goto do_cleanup;
fret = EXIT_SUCCESS;
do_cleanup:
if (master >= 0)
close(master);
if (slave >= 0)
close(slave);
return fret;
}
static int verify_non_standard_devpts_mount(void)
{
char *mntpoint;
int ret = -1;
char devpts[] = P_tmpdir "/devpts_fs_XXXXXX";
char ptmx[] = P_tmpdir "/devpts_fs_XXXXXX/ptmx";
ret = umount("/dev/pts");
if (ret < 0) {
fprintf(stderr, "Failed to unmount \"/dev/pts\": %s\n",
strerror(errno));
return -1;
}
(void)umount("/dev/ptmx");
mntpoint = mkdtemp(devpts);
if (!mntpoint) {
fprintf(stderr, "Failed to create temporary mountpoint: %s\n",
strerror(errno));
return -1;
}
ret = mount("devpts", mntpoint, "devpts", MS_NOSUID | MS_NOEXEC,
"newinstance,ptmxmode=0666,mode=0620,gid=5");
if (ret < 0) {
fprintf(stderr, "Failed to mount devpts fs to \"%s\" in new "
"mount namespace: %s\n", mntpoint,
strerror(errno));
unlink(mntpoint);
return -1;
}
ret = snprintf(ptmx, sizeof(ptmx), "%s/ptmx", devpts);
if (ret < 0 || (size_t)ret >= sizeof(ptmx)) {
unlink(mntpoint);
return -1;
}
ret = do_tiocgptpeer(ptmx, mntpoint);
unlink(mntpoint);
if (ret < 0)
return -1;
return 0;
}
static int verify_ptmx_bind_mount(void)
{
int ret;
ret = mount("/dev/pts/ptmx", "/dev/ptmx", NULL, MS_BIND, NULL);
if (ret < 0) {
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
"\"/dev/ptmx\" mount namespace\n");
return -1;
}
ret = do_tiocgptpeer("/dev/ptmx", "/dev/pts/");
if (ret < 0)
return -1;
return 0;
}
static int verify_invalid_ptmx_bind_mount(void)
{
int ret;
char mntpoint_fd;
char ptmx[] = P_tmpdir "/devpts_ptmx_XXXXXX";
mntpoint_fd = mkstemp(ptmx);
if (mntpoint_fd < 0) {
fprintf(stderr, "Failed to create temporary directory: %s\n",
strerror(errno));
return -1;
}
ret = mount("/dev/pts/ptmx", ptmx, NULL, MS_BIND, NULL);
close(mntpoint_fd);
if (ret < 0) {
fprintf(stderr, "Failed to bind mount \"/dev/pts/ptmx\" to "
"\"%s\" mount namespace\n", ptmx);
return -1;
}
ret = do_tiocgptpeer(ptmx, "/dev/pts/");
if (ret == 0)
return -1;
return 0;
}
int main(int argc, char *argv[])
{
int ret;
if (!isatty(STDIN_FILENO)) {
fprintf(stderr, "Standard input file desciptor is not attached "
"to a terminal. Skipping test\n");
exit(EXIT_FAILURE);
}
ret = unshare(CLONE_NEWNS);
if (ret < 0) {
fprintf(stderr, "Failed to unshare mount namespace\n");
exit(EXIT_FAILURE);
}
ret = mount("", "/", NULL, MS_PRIVATE | MS_REC, 0);
if (ret < 0) {
fprintf(stderr, "Failed to make \"/\" MS_PRIVATE in new mount "
"namespace\n");
exit(EXIT_FAILURE);
}
ret = verify_ptmx_bind_mount();
if (ret < 0)
exit(EXIT_FAILURE);
ret = verify_invalid_ptmx_bind_mount();
if (ret < 0)
exit(EXIT_FAILURE);
ret = verify_non_standard_devpts_mount();
if (ret < 0)
exit(EXIT_FAILURE);
exit(EXIT_SUCCESS);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册